summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Evans <grknight@gentoo.org>2020-10-06 11:22:15 -0400
committerBrian Evans <grknight@gentoo.org>2020-10-06 11:22:15 -0400
commit4a2d3a0b7596731e11ef9257138653bec81d6fd3 (patch)
tree0e33fdfd38e791cc9ffe46f11f954a7dce86d618
parentOpenIDConnect: Fix newly protected function (diff)
downloadextensions-4a2d3a0b7596731e11ef9257138653bec81d6fd3.tar.gz
extensions-4a2d3a0b7596731e11ef9257138653bec81d6fd3.tar.bz2
extensions-4a2d3a0b7596731e11ef9257138653bec81d6fd3.zip
Add OAuth for API access
Signed-off-by: Brian Evans <grknight@gentoo.org>
-rw-r--r--OAuth/.eslintrc.json16
-rw-r--r--OAuth/.gitignore9
-rw-r--r--OAuth/.gitreview6
-rw-r--r--OAuth/.phan/config.php26
-rw-r--r--OAuth/.phpcs.xml18
-rw-r--r--OAuth/.stylelintrc.json6
-rw-r--r--OAuth/CODE_OF_CONDUCT.md1
-rw-r--r--OAuth/COPYING339
-rw-r--r--OAuth/Gruntfile.js33
-rw-r--r--OAuth/MWOAuth.alias.php176
-rw-r--r--OAuth/composer.json30
-rw-r--r--OAuth/examples/testClient.php228
-rw-r--r--OAuth/examples/testClientHeaders.php79
-rw-r--r--OAuth/extension.json197
-rw-r--r--OAuth/i18n/af.json39
-rw-r--r--OAuth/i18n/ar.json318
-rw-r--r--OAuth/i18n/as.json11
-rw-r--r--OAuth/i18n/ast.json313
-rw-r--r--OAuth/i18n/atj.json14
-rw-r--r--OAuth/i18n/awa.json8
-rw-r--r--OAuth/i18n/az.json24
-rw-r--r--OAuth/i18n/azb.json12
-rw-r--r--OAuth/i18n/bcc.json8
-rw-r--r--OAuth/i18n/bcl.json10
-rw-r--r--OAuth/i18n/be-tarask.json30
-rw-r--r--OAuth/i18n/be.json14
-rw-r--r--OAuth/i18n/bg.json158
-rw-r--r--OAuth/i18n/bn.json125
-rw-r--r--OAuth/i18n/br.json92
-rw-r--r--OAuth/i18n/bs.json38
-rw-r--r--OAuth/i18n/ca.json26
-rw-r--r--OAuth/i18n/ce.json120
-rw-r--r--OAuth/i18n/ckb.json24
-rw-r--r--OAuth/i18n/cs.json277
-rw-r--r--OAuth/i18n/da.json67
-rw-r--r--OAuth/i18n/de-formal.json8
-rw-r--r--OAuth/i18n/de.json302
-rw-r--r--OAuth/i18n/diq.json34
-rw-r--r--OAuth/i18n/dty.json8
-rw-r--r--OAuth/i18n/el.json25
-rw-r--r--OAuth/i18n/en-gb.json22
-rw-r--r--OAuth/i18n/en.json330
-rw-r--r--OAuth/i18n/eo.json16
-rw-r--r--OAuth/i18n/es.json255
-rw-r--r--OAuth/i18n/et.json168
-rw-r--r--OAuth/i18n/eu.json49
-rw-r--r--OAuth/i18n/fa.json255
-rw-r--r--OAuth/i18n/fi.json80
-rw-r--r--OAuth/i18n/fit.json11
-rw-r--r--OAuth/i18n/fr.json345
-rw-r--r--OAuth/i18n/frr.json14
-rw-r--r--OAuth/i18n/fy.json21
-rw-r--r--OAuth/i18n/gl.json283
-rw-r--r--OAuth/i18n/gsw.json17
-rw-r--r--OAuth/i18n/gu.json20
-rw-r--r--OAuth/i18n/he.json333
-rw-r--r--OAuth/i18n/hi.json10
-rw-r--r--OAuth/i18n/hr.json202
-rw-r--r--OAuth/i18n/hsb.json8
-rw-r--r--OAuth/i18n/ht.json8
-rw-r--r--OAuth/i18n/hu.json79
-rw-r--r--OAuth/i18n/hy.json8
-rw-r--r--OAuth/i18n/ia.json295
-rw-r--r--OAuth/i18n/id.json80
-rw-r--r--OAuth/i18n/ig.json10
-rw-r--r--OAuth/i18n/inh.json10
-rw-r--r--OAuth/i18n/io.json18
-rw-r--r--OAuth/i18n/is.json53
-rw-r--r--OAuth/i18n/it.json268
-rw-r--r--OAuth/i18n/ja.json230
-rw-r--r--OAuth/i18n/jv.json11
-rw-r--r--OAuth/i18n/ka.json89
-rw-r--r--OAuth/i18n/kiu.json8
-rw-r--r--OAuth/i18n/kjp.json16
-rw-r--r--OAuth/i18n/kk-cyrl.json9
-rw-r--r--OAuth/i18n/km.json12
-rw-r--r--OAuth/i18n/ko.json267
-rw-r--r--OAuth/i18n/krc.json24
-rw-r--r--OAuth/i18n/ksh.json85
-rw-r--r--OAuth/i18n/ku-latn.json12
-rw-r--r--OAuth/i18n/lb.json132
-rw-r--r--OAuth/i18n/lfn.json14
-rw-r--r--OAuth/i18n/lij.json11
-rw-r--r--OAuth/i18n/lki.json13
-rw-r--r--OAuth/i18n/lld.json11
-rw-r--r--OAuth/i18n/lt.json18
-rw-r--r--OAuth/i18n/lv.json47
-rw-r--r--OAuth/i18n/lzh.json8
-rw-r--r--OAuth/i18n/min.json30
-rw-r--r--OAuth/i18n/mk.json329
-rw-r--r--OAuth/i18n/ml.json105
-rw-r--r--OAuth/i18n/mn.json8
-rw-r--r--OAuth/i18n/mr.json155
-rw-r--r--OAuth/i18n/ms.json15
-rw-r--r--OAuth/i18n/mwl.json15
-rw-r--r--OAuth/i18n/my.json79
-rw-r--r--OAuth/i18n/nah.json8
-rw-r--r--OAuth/i18n/nap.json8
-rw-r--r--OAuth/i18n/nb.json325
-rw-r--r--OAuth/i18n/nds-nl.json21
-rw-r--r--OAuth/i18n/ne.json28
-rw-r--r--OAuth/i18n/nl.json280
-rw-r--r--OAuth/i18n/nn.json43
-rw-r--r--OAuth/i18n/oc.json123
-rw-r--r--OAuth/i18n/olo.json9
-rw-r--r--OAuth/i18n/pam.json8
-rw-r--r--OAuth/i18n/pcd.json11
-rw-r--r--OAuth/i18n/pl.json331
-rw-r--r--OAuth/i18n/ps.json35
-rw-r--r--OAuth/i18n/pt-br.json336
-rw-r--r--OAuth/i18n/pt.json303
-rw-r--r--OAuth/i18n/qqq.json343
-rw-r--r--OAuth/i18n/ro.json13
-rw-r--r--OAuth/i18n/roa-tara.json79
-rw-r--r--OAuth/i18n/ru.json264
-rw-r--r--OAuth/i18n/sa.json33
-rw-r--r--OAuth/i18n/scn.json9
-rw-r--r--OAuth/i18n/sco.json14
-rw-r--r--OAuth/i18n/sd.json61
-rw-r--r--OAuth/i18n/sh.json256
-rw-r--r--OAuth/i18n/si.json9
-rw-r--r--OAuth/i18n/sk.json84
-rw-r--r--OAuth/i18n/sl.json37
-rw-r--r--OAuth/i18n/so.json27
-rw-r--r--OAuth/i18n/sq.json14
-rw-r--r--OAuth/i18n/sr-ec.json117
-rw-r--r--OAuth/i18n/sr-el.json78
-rw-r--r--OAuth/i18n/su.json8
-rw-r--r--OAuth/i18n/sv.json299
-rw-r--r--OAuth/i18n/sw.json9
-rw-r--r--OAuth/i18n/szl.json9
-rw-r--r--OAuth/i18n/ta.json13
-rw-r--r--OAuth/i18n/tcy.json8
-rw-r--r--OAuth/i18n/te.json13
-rw-r--r--OAuth/i18n/tg-cyrl.json13
-rw-r--r--OAuth/i18n/th.json178
-rw-r--r--OAuth/i18n/tl.json12
-rw-r--r--OAuth/i18n/tly.json12
-rw-r--r--OAuth/i18n/tr.json338
-rw-r--r--OAuth/i18n/tt-cyrl.json15
-rw-r--r--OAuth/i18n/uk.json335
-rw-r--r--OAuth/i18n/ur.json30
-rw-r--r--OAuth/i18n/uz.json8
-rw-r--r--OAuth/i18n/vec.json16
-rw-r--r--OAuth/i18n/vi.json195
-rw-r--r--OAuth/i18n/vo.json8
-rw-r--r--OAuth/i18n/wuu.json11
-rw-r--r--OAuth/i18n/xmf.json8
-rw-r--r--OAuth/i18n/yi.json12
-rw-r--r--OAuth/i18n/yue.json9
-rw-r--r--OAuth/i18n/zh-hans.json298
-rw-r--r--OAuth/i18n/zh-hant.json337
-rw-r--r--OAuth/maintenance/createOAuthConsumer.php134
-rw-r--r--OAuth/maintenance/migrateCentralWiki.php113
-rw-r--r--OAuth/maintenance/migrateCentralWikiLogs.php147
-rw-r--r--OAuth/maintenance/testOAuthConsumer.php170
-rw-r--r--OAuth/package-lock.json3709
-rw-r--r--OAuth/package.json14
-rw-r--r--OAuth/resources/assets/echo-icon.pngbin0 -> 1372 bytes
-rw-r--r--OAuth/resources/modules/ext.MWOAuth.AuthorizeDialog.js40
-rw-r--r--OAuth/resources/modules/ext.MWOAuth.AuthorizeForm.css38
-rw-r--r--OAuth/resources/modules/ext.MWOAuth.BasicStyles.css61
-rw-r--r--OAuth/schema/OAuth.sql114
-rw-r--r--OAuth/schema/index_on_oaat_acceptance_id.sql2
-rw-r--r--OAuth/schema/mysql/callback_is_prefix.sql1
-rw-r--r--OAuth/schema/mysql/developer_agreement.sql1
-rw-r--r--OAuth/schema/mysql/oauth2_allowed_grants.sql2
-rw-r--r--OAuth/schema/mysql/oauth2_is_confidential.sql2
-rw-r--r--OAuth/schema/mysql/oauth_version_accepted.sql2
-rw-r--r--OAuth/schema/mysql/oauth_version_registered.sql2
-rw-r--r--OAuth/schema/mysql/owner_only.sql1
-rw-r--r--OAuth/schema/oauth2_access_tokens.sql18
-rw-r--r--OAuth/schema/sqlite/callback_is_prefix.sql1
-rw-r--r--OAuth/schema/sqlite/developer_agreement.sql1
-rw-r--r--OAuth/schema/sqlite/oauth2_allowed_grants.sql2
-rw-r--r--OAuth/schema/sqlite/oauth2_is_confidential.sql2
-rw-r--r--OAuth/schema/sqlite/oauth_version_accepted.sql2
-rw-r--r--OAuth/schema/sqlite/oauth_version_registered.sql2
-rw-r--r--OAuth/schema/sqlite/owner_only.sql1
-rw-r--r--OAuth/src/AuthorizationProvider/AccessToken.php35
-rw-r--r--OAuth/src/AuthorizationProvider/AuthorizationProvider.php178
-rw-r--r--OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAccessTokens.php27
-rw-r--r--OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAuthorization.php100
-rw-r--r--OAuth/src/AuthorizationProvider/Grant/ClientCredentials.php17
-rw-r--r--OAuth/src/AuthorizationProvider/Grant/RefreshToken.php21
-rw-r--r--OAuth/src/AuthorizationProvider/IAccessTokenProvider.php18
-rw-r--r--OAuth/src/AuthorizationProvider/IAuthorizationProvider.php25
-rw-r--r--OAuth/src/AuthorizationServerFactory.php57
-rw-r--r--OAuth/src/Backend/Consumer.php800
-rw-r--r--OAuth/src/Backend/ConsumerAcceptance.php274
-rw-r--r--OAuth/src/Backend/Hooks.php214
-rw-r--r--OAuth/src/Backend/MWOAuthDAO.php478
-rw-r--r--OAuth/src/Backend/MWOAuthDataStore.php257
-rw-r--r--OAuth/src/Backend/MWOAuthException.php23
-rw-r--r--OAuth/src/Backend/MWOAuthRequest.php78
-rw-r--r--OAuth/src/Backend/MWOAuthServer.php328
-rw-r--r--OAuth/src/Backend/MWOAuthSignatureMethod_RSA_SHA1.php61
-rw-r--r--OAuth/src/Backend/MWOAuthToken.php28
-rw-r--r--OAuth/src/Backend/OAuth1Consumer.php80
-rw-r--r--OAuth/src/Backend/UpdaterHooks.php98
-rw-r--r--OAuth/src/Backend/Utils.php471
-rw-r--r--OAuth/src/Control/ConsumerAcceptanceAccessControl.php105
-rw-r--r--OAuth/src/Control/ConsumerAcceptanceSubmitControl.php234
-rw-r--r--OAuth/src/Control/ConsumerAccessControl.php262
-rw-r--r--OAuth/src/Control/ConsumerSubmitControl.php550
-rw-r--r--OAuth/src/Control/DAOAccessControl.php126
-rw-r--r--OAuth/src/Control/SubmitControl.php228
-rw-r--r--OAuth/src/Entity/AccessTokenEntity.php150
-rw-r--r--OAuth/src/Entity/AuthCodeEntity.php26
-rw-r--r--OAuth/src/Entity/ClientEntity.php191
-rw-r--r--OAuth/src/Entity/RefreshTokenEntity.php21
-rw-r--r--OAuth/src/Entity/ScopeEntity.php26
-rw-r--r--OAuth/src/Entity/UserEntity.php55
-rw-r--r--OAuth/src/Exception/ClientApprovalDenyException.php19
-rw-r--r--OAuth/src/Frontend/EchoOAuthStageChangePresentationModel.php121
-rw-r--r--OAuth/src/Frontend/OAuthLogFormatter.php40
-rw-r--r--OAuth/src/Frontend/Pagers/ListConsumersPager.php128
-rw-r--r--OAuth/src/Frontend/Pagers/ListMyConsumersPager.php109
-rw-r--r--OAuth/src/Frontend/Pagers/ManageConsumersPager.php109
-rw-r--r--OAuth/src/Frontend/Pagers/ManageMyGrantsPager.php111
-rw-r--r--OAuth/src/Frontend/SpecialPages/SpecialMWOAuth.php743
-rw-r--r--OAuth/src/Frontend/SpecialPages/SpecialMWOAuthConsumerRegistration.php618
-rw-r--r--OAuth/src/Frontend/SpecialPages/SpecialMWOAuthListConsumers.php408
-rw-r--r--OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageConsumers.php521
-rw-r--r--OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageMyGrants.php348
-rw-r--r--OAuth/src/Frontend/UIHooks.php241
-rw-r--r--OAuth/src/Frontend/UIUtils.php39
-rw-r--r--OAuth/src/Lib/OAuthConsumer.php43
-rw-r--r--OAuth/src/Lib/OAuthDataStore.php53
-rw-r--r--OAuth/src/Lib/OAuthException.php34
-rw-r--r--OAuth/src/Lib/OAuthRequest.php297
-rw-r--r--OAuth/src/Lib/OAuthServer.php270
-rw-r--r--OAuth/src/Lib/OAuthSignatureMethod.php94
-rw-r--r--OAuth/src/Lib/OAuthSignatureMethod_HMAC_SHA1.php60
-rw-r--r--OAuth/src/Lib/OAuthSignatureMethod_PLAINTEXT.php63
-rw-r--r--OAuth/src/Lib/OAuthSignatureMethod_RSA_SHA1.php96
-rw-r--r--OAuth/src/Lib/OAuthToken.php58
-rw-r--r--OAuth/src/Lib/OAuthUtil.php209
-rw-r--r--OAuth/src/Repository/AccessTokenRepository.php147
-rw-r--r--OAuth/src/Repository/AuthCodeRepository.php75
-rw-r--r--OAuth/src/Repository/CacheRepository.php85
-rw-r--r--OAuth/src/Repository/ClientRepository.php64
-rw-r--r--OAuth/src/Repository/DatabaseRepository.php36
-rw-r--r--OAuth/src/Repository/RefreshTokenRepository.php75
-rw-r--r--OAuth/src/Repository/ScopeRepository.php116
-rw-r--r--OAuth/src/ResourceServer.php232
-rw-r--r--OAuth/src/Response.php139
-rw-r--r--OAuth/src/Rest/Handler/AccessToken.php127
-rw-r--r--OAuth/src/Rest/Handler/AuthenticationHandler.php197
-rw-r--r--OAuth/src/Rest/Handler/Authorize.php264
-rw-r--r--OAuth/src/Rest/Handler/Resource.php153
-rw-r--r--OAuth/src/SessionProvider.php440
-rw-r--r--OAuth/src/Setup.php41
-rw-r--r--OAuth/src/UserStatementProvider.php115
-rw-r--r--OAuth/tests/phpunit/AuthorizationProviderTest.php181
-rw-r--r--OAuth/tests/phpunit/Backend/MWOAuthHooksTest.php34
-rw-r--r--OAuth/tests/phpunit/Backend/MWOAuthServerTest.php88
-rw-r--r--OAuth/tests/phpunit/Backend/StubConsumer.php121
-rw-r--r--OAuth/tests/phpunit/Entity/AccessTokenEntityTest.php51
-rw-r--r--OAuth/tests/phpunit/Entity/ClientEntityTest.php49
-rw-r--r--OAuth/tests/phpunit/Entity/Mock_ClientEntity.php40
-rw-r--r--OAuth/tests/phpunit/Entity/UserEntityTest.php24
-rw-r--r--OAuth/tests/phpunit/Lib/Mock_OAuthBaseStringRequest.php14
-rw-r--r--OAuth/tests/phpunit/Lib/Mock_OAuthDataStore.php63
-rw-r--r--OAuth/tests/phpunit/Lib/Mock_OAuthSignatureMethod_RSA_SHA1.php51
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthConsumerTest.php39
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthRequestTest.php376
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthServerTest.php259
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthSignatureMethodHmacSha1Test.php90
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthSignatureMethodRsaSha1Test.php70
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthTestUtils.php62
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthTokenTest.php49
-rw-r--r--OAuth/tests/phpunit/Lib/OAuthUtilTest.php178
-rw-r--r--OAuth/tests/phpunit/Repository/AccessTokenRepositoryTest.php52
-rw-r--r--OAuth/tests/phpunit/Repository/AuthCodeRepositoryTest.php47
-rw-r--r--OAuth/tests/phpunit/Repository/ScopeRepositoryTest.php30
-rw-r--r--OAuth/tests/phpunit/Rest/AccessTokenEndpointTest.php72
-rw-r--r--OAuth/tests/phpunit/Rest/AuthorizationEndpointTest.php43
-rw-r--r--OAuth/tests/phpunit/Rest/EndpointTest.php80
-rw-r--r--OAuth/tests/phpunit/Rest/testRoutes.json15
-rw-r--r--OAuth/tests/phpunit/SessionProviderTest.php101
281 files changed, 33432 insertions, 0 deletions
diff --git a/OAuth/.eslintrc.json b/OAuth/.eslintrc.json
new file mode 100644
index 00000000..e2b5c47f
--- /dev/null
+++ b/OAuth/.eslintrc.json
@@ -0,0 +1,16 @@
+{
+ "root": true,
+ "extends": [
+ "wikimedia/client",
+ "wikimedia/jquery"
+ ],
+ "globals": {
+ "mw": false,
+ "OO": false
+ },
+ "rules": {
+ "no-jquery/no-global-selector": "off",
+ "no-jquery/no-sizzle": "off",
+ "no-jquery/variable-pattern": "off"
+ }
+}
diff --git a/OAuth/.gitignore b/OAuth/.gitignore
new file mode 100644
index 00000000..7917f9ba
--- /dev/null
+++ b/OAuth/.gitignore
@@ -0,0 +1,9 @@
+.svn
+*~
+*.kate-swp
+.*.swp
+/composer.lock
+/nbproject/private/node_modules/
+/vendor
+/node_modules
+/.eslintcache
diff --git a/OAuth/.gitreview b/OAuth/.gitreview
new file mode 100644
index 00000000..380babf5
--- /dev/null
+++ b/OAuth/.gitreview
@@ -0,0 +1,6 @@
+[gerrit]
+host=gerrit.wikimedia.org
+port=29418
+project=mediawiki/extensions/OAuth.git
+track=1
+defaultrebase=0
diff --git a/OAuth/.phan/config.php b/OAuth/.phan/config.php
new file mode 100644
index 00000000..ed6dff6f
--- /dev/null
+++ b/OAuth/.phan/config.php
@@ -0,0 +1,26 @@
+<?php
+
+$cfg = require __DIR__ . '/../vendor/mediawiki/mediawiki-phan-config/src/config.php';
+
+$cfg['scalar_implicit_cast'] = true;
+$cfg['null_casts_as_any_type'] = true;
+
+// Database->daoReadOnly and MWOAuthToken->oauth_callback_confirmed
+$cfg['suppress_issue_types'][] = 'PhanUndeclaredProperty';
+
+$cfg['directory_list'] = array_merge(
+ $cfg['directory_list'],
+ [
+ '../../extensions/Echo',
+ ]
+);
+
+$cfg['exclude_analysis_directory_list'] = array_merge(
+ $cfg['exclude_analysis_directory_list'],
+ [
+ 'src/Lib/',
+ '../../extensions/Echo',
+ ]
+);
+
+return $cfg;
diff --git a/OAuth/.phpcs.xml b/OAuth/.phpcs.xml
new file mode 100644
index 00000000..4359769c
--- /dev/null
+++ b/OAuth/.phpcs.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<ruleset>
+ <rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
+ <exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationPublic" />
+ <exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationProtected"/>
+ <exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationPrivate"/>
+ <exclude name="Squiz.Classes.ValidClassName.NotCamelCaps" />
+ <exclude name="MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName"/>
+ <exclude name="MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.NewLineComment"/>
+ <exclude name="MediaWiki.ControlStructures.AssignmentInControlStructures.AssignmentInControlStructures" />
+ <exclude name="PSR12.Properties.ConstantVisibility.NotFound" />
+ </rule>
+ <file>.</file>
+ <arg name="extensions" value="php,php5,inc"/>
+ <arg name="encoding" value="UTF-8"/>
+ <exclude-pattern type="relative">^src/lib/</exclude-pattern>
+ <exclude-pattern type="relative">^tests/phpunit/lib/</exclude-pattern>
+</ruleset>
diff --git a/OAuth/.stylelintrc.json b/OAuth/.stylelintrc.json
new file mode 100644
index 00000000..b681cd2e
--- /dev/null
+++ b/OAuth/.stylelintrc.json
@@ -0,0 +1,6 @@
+{
+ "extends": "stylelint-config-wikimedia",
+ "rules": {
+ "selector-max-id": null
+ }
+}
diff --git a/OAuth/CODE_OF_CONDUCT.md b/OAuth/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..498acf76
--- /dev/null
+++ b/OAuth/CODE_OF_CONDUCT.md
@@ -0,0 +1 @@
+The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct).
diff --git a/OAuth/COPYING b/OAuth/COPYING
new file mode 100644
index 00000000..d159169d
--- /dev/null
+++ b/OAuth/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/OAuth/Gruntfile.js b/OAuth/Gruntfile.js
new file mode 100644
index 00000000..25c293fb
--- /dev/null
+++ b/OAuth/Gruntfile.js
@@ -0,0 +1,33 @@
+/* eslint-env node */
+module.exports = function ( grunt ) {
+ var conf = grunt.file.readJSON( 'extension.json' );
+
+ grunt.loadNpmTasks( 'grunt-banana-checker' );
+ grunt.loadNpmTasks( 'grunt-eslint' );
+ grunt.loadNpmTasks( 'grunt-stylelint' );
+
+ grunt.initConfig( {
+ banana: conf.MessagesDirs,
+ eslint: {
+ options: {
+ extensions: [ '.js', '.json' ],
+ cache: true
+ },
+ all: [
+ '**/*.js{,on}',
+ '!node_modules/**',
+ '!vendor/**'
+ ]
+ },
+ stylelint: {
+ all: [
+ '**/*.css',
+ '!node_modules/**',
+ '!vendor/**'
+ ]
+ }
+ } );
+
+ grunt.registerTask( 'test', [ 'eslint', 'banana', 'stylelint' ] );
+ grunt.registerTask( 'default', 'test' );
+};
diff --git a/OAuth/MWOAuth.alias.php b/OAuth/MWOAuth.alias.php
new file mode 100644
index 00000000..8ba1dac4
--- /dev/null
+++ b/OAuth/MWOAuth.alias.php
@@ -0,0 +1,176 @@
+<?php
+/**
+ * Aliases for extension OAuth
+ *
+ * @file
+ * @ingroup Extensions
+ */
+
+$specialPageAliases = [];
+
+/** English (English) */
+$specialPageAliases['en'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuthConsumerRegistration', 'OAuthRegistration' ],
+ 'OAuthManageConsumers' => [ 'OAuthManageConsumers' ],
+ 'OAuthListConsumers' => [ 'OAuthListConsumers' ],
+ 'OAuthManageMyGrants' => [ 'OAuthManageMyGrants', 'OAuthGrants' ],
+ 'OAuth' => [ 'OAuth', 'MWOAuth' ],
+];
+
+/** Arabic (العربية) */
+$specialPageAliases['ar'] = [
+ 'OAuthConsumerRegistration' => [ 'تسجيل_مستهلك_أو_أوث', 'تسجيل_أو_أوث' ],
+ 'OAuthManageConsumers' => [ 'التحكم_بمستهلكي_أو_أوث' ],
+ 'OAuthListConsumers' => [ 'عرض_مستهلكي_أو_أوث' ],
+ 'OAuthManageMyGrants' => [ 'التحكم_بممنوحاتي_أو_أوث', 'ممنوحات_أو_أوث' ],
+ 'OAuth' => [ 'أو_أوث', 'مو_أو_أوث' ],
+];
+
+/** Egyptian Arabic (مصرى) */
+$specialPageAliases['arz'] = [
+ 'OAuthConsumerRegistration' => [ 'تسجيل_او_اوت', 'تسجيل_مستهلك_او_اوت' ],
+ 'OAuthManageConsumers' => [ 'التحكم_بمستهلكى_او_اوت' ],
+ 'OAuthListConsumers' => [ 'عرض_مستهلكين_او_اوت' ],
+ 'OAuthManageMyGrants' => [ 'التحكم-بممنوحاتى_او_اوت', 'ممنوحات_او_اوت' ],
+ 'OAuth' => [ 'او-اوت', 'مو_او_اوت' ],
+];
+
+/** Western Balochi (بلوچی رخشانی) */
+$specialPageAliases['bgn'] = [
+ 'OAuthConsumerRegistration' => [ 'سبت_کورتین' ],
+ 'OAuthManageConsumers' => [ 'OAuth_ئی_مسرپ_کنۆکانی_مدیریت' ],
+ 'OAuthListConsumers' => [ 'OAuth_ئی_مسرپ_کنۆکانی_لیست' ],
+ 'OAuthManageMyGrants' => [ 'نی_OAuth_ئی_بخش_یی_مدیریت' ],
+];
+
+/** Czech (čeština) */
+$specialPageAliases['cs'] = [
+ 'OAuthConsumerRegistration' => [ 'Registrace_aplikace_OAuth' ],
+ 'OAuthManageConsumers' => [ 'Správa_aplikací_OAuth' ],
+ 'OAuthListConsumers' => [ 'Seznam_aplikací_OAuth' ],
+ 'OAuthManageMyGrants' => [ 'Správa_připojených_aplikací' ],
+];
+
+/** German (Deutsch) */
+$specialPageAliases['de'] = [
+ 'OAuthConsumerRegistration' => [ 'Verbraucherregistrierung' ],
+ 'OAuthManageConsumers' => [ 'Verbraucher_verwalten' ],
+ 'OAuthListConsumers' => [ 'Verbraucher_auflisten' ],
+ 'OAuthManageMyGrants' => [ 'Meine_Berechtigungen_verwalten' ],
+];
+
+/** Estonian (eesti) */
+$specialPageAliases['et'] = [
+ 'OAuthListConsumers' => [ 'OAuthi-rakenduste_loend' ],
+ 'OAuthManageMyGrants' => [ 'OAuthi-volitused' ],
+];
+
+/** Persian (فارسی) */
+$specialPageAliases['fa'] = [
+ 'OAuthConsumerRegistration' => [ 'ثبت_OAuth' ],
+ 'OAuthManageConsumers' => [ 'مدیریت_مصرف‌کننده‌های_OAuth' ],
+ 'OAuthListConsumers' => [ 'فهرست_مصرف‌کننده‌های_OAuth' ],
+ 'OAuthManageMyGrants' => [ 'مدیریت_اعطاهای_OAuth_من' ],
+];
+
+/** Galician (galego) */
+$specialPageAliases['gl'] = [
+ 'OAuthManageMyGrants' => [ 'Administrar_as_concesión_de_conta_OAuth' ],
+];
+
+/** Hebrew (עברית) */
+$specialPageAliases['he'] = [
+ 'OAuthConsumerRegistration' => [ 'רישום_צרכן_OAuth' ],
+ 'OAuthManageConsumers' => [ 'ניהול_צרכני_OAuth' ],
+ 'OAuthListConsumers' => [ 'יישומי_OAuth', 'רשימות_יישומי_OAuth' ],
+ 'OAuthManageMyGrants' => [ 'ניהול_יישומים', 'ניהול_יישומים_מחוברים' ],
+];
+
+/** Korean (한국어) */
+$specialPageAliases['ko'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuth컨슈머등록', 'OAuth등록' ],
+ 'OAuthManageConsumers' => [ 'OAuth컨슈머관리' ],
+ 'OAuthListConsumers' => [ 'OAuth컨슈머목록' ],
+ 'OAuthManageMyGrants' => [ 'OAuth내부여관리' ],
+ 'OAuth' => [ 'MWO인증' ],
+];
+
+/** Luxembourgish (Lëtzebuergesch) */
+$specialPageAliases['lb'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuth-Registratioun_vu_Konsumenten' ],
+ 'OAuthManageConsumers' => [ 'OAuth_Gestioun_vu_Konsumenten' ],
+ 'OAuthListConsumers' => [ 'OAuth_Lëscht_vu_Konsumenten' ],
+ 'OAuthManageMyGrants' => [ 'Meng_OAuth_Autorisatioune_geréieren' ],
+];
+
+/** Northern Luri (لۊری شومالی) */
+$specialPageAliases['lrc'] = [
+ 'OAuth' => [ 'قأسأم_حأردئن' ],
+];
+
+/** Macedonian (македонски) */
+$specialPageAliases['mk'] = [
+ 'OAuthConsumerRegistration' => [ 'МВOAuthРегистрацијаПотрошувач' ],
+ 'OAuthManageConsumers' => [ 'МВOAuthРаководењеПотрошувач' ],
+ 'OAuthListConsumers' => [ 'OAuthНаведиПотрошувачи' ],
+ 'OAuthManageMyGrants' => [ 'OAuthРаководењеМоиДоделувања' ],
+];
+
+/** Dutch (Nederlands) */
+$specialPageAliases['nl'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuthGebruikerRegistratie' ],
+ 'OAuthManageConsumers' => [ 'OAuthBeheerGebruikers' ],
+ 'OAuthListConsumers' => [ 'OAuthLijstGebruikers' ],
+];
+
+/** Serbian Cyrillic (српски (ћирилица)) */
+$specialPageAliases['sr-ec'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuth_регистрација_апликације', 'OAuth_регистрација' ],
+ 'OAuthManageConsumers' => [ 'OAuth_управљање_апликацијама' ],
+ 'OAuthListConsumers' => [ 'Списак_OAuth_апликација' ],
+ 'OAuthManageMyGrants' => [ 'УправљањеПовезанимАпликацијама' ],
+];
+
+/** Serbian Latin (srpski (latinica)) */
+$specialPageAliases['sr-el'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuth_registracija_aplikacije', 'OAuth_registracija' ],
+ 'OAuthManageConsumers' => [ 'OAuth_upravljanje_aplikacijama' ],
+ 'OAuthListConsumers' => [ 'Spisak_OAuth_aplikacija' ],
+ 'OAuthManageMyGrants' => [ 'UpravljanjePovezanimAplikacijama' ],
+];
+
+/** Swedish (svenska) */
+$specialPageAliases['sv'] = [
+ 'OAuthListConsumers' => [ 'OAuth-applikationer' ],
+ 'OAuthManageMyGrants' => [ 'Anslutna_applikationer' ],
+];
+
+/** Urdu (اردو) */
+$specialPageAliases['ur'] = [
+ 'OAuthConsumerRegistration' => [ 'اندراج_اوآتھ_صارف', 'اوآتھ_اندراج' ],
+ 'OAuthManageConsumers' => [ 'انتظام_اوآتھ_صارف' ],
+ 'OAuthListConsumers' => [ 'فہرست_اوآتھ_صارفین' ],
+ 'OAuthManageMyGrants' => [ 'انتظام_اوآتھ_عطیات_من', 'اوآتھ_عطیات' ],
+ 'OAuth' => [ 'اوآتھ', 'میڈیا_ویکی_اوآتھ' ],
+];
+
+/** Yiddish (ייִדיש) */
+$specialPageAliases['yi'] = [
+ 'OAuthManageMyGrants' => [ 'פארוואלטן_פראיעקטן' ],
+];
+
+/** Simplified Chinese (中文(简体)‎) */
+$specialPageAliases['zh-hans'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuth应用程序注册', 'OAuth注册' ],
+ 'OAuthManageConsumers' => [ 'OAuth应用程序管理', 'OAuth管理' ],
+ 'OAuthListConsumers' => [ 'OAuth应用程序列表', 'OAuth列表' ],
+ 'OAuthManageMyGrants' => [ 'OAuth应用程序授权', 'OAuth授权' ],
+];
+
+/** Traditional Chinese (中文(繁體)‎) */
+$specialPageAliases['zh-hant'] = [
+ 'OAuthConsumerRegistration' => [ 'OAuth應用程式註冊', 'OAuth註冊' ],
+ 'OAuthManageConsumers' => [ 'OAuth應用程式管理', 'OAuth管理' ],
+ 'OAuthListConsumers' => [ 'OAuth應用程式清單', 'OAuth清單' ],
+ 'OAuthManageMyGrants' => [ 'OAuth應用程式授權', 'OAuth授權' ],
+];
diff --git a/OAuth/composer.json b/OAuth/composer.json
new file mode 100644
index 00000000..fd99695f
--- /dev/null
+++ b/OAuth/composer.json
@@ -0,0 +1,30 @@
+{
+ "name": "mediawiki/oauth",
+ "type": "mediawiki-extension",
+ "description": "Allows usage of OAuth 1.0a and OAuth 2.0 for API authorization",
+ "license": "GPL-2.0-or-later",
+ "prefer-stable": true,
+ "require": {
+ "firebase/php-jwt": "5.2.0",
+ "league/oauth2-server": "8.1.0"
+ },
+ "require-dev": {
+ "mediawiki/mediawiki-codesniffer": "31.0.0",
+ "mediawiki/mediawiki-phan-config": "0.10.2",
+ "mediawiki/minus-x": "1.1.0",
+ "php-parallel-lint/php-console-highlighter": "0.5.0",
+ "php-parallel-lint/php-parallel-lint": "1.2.0",
+ "wikimedia/testing-access-wrapper": "~1.0"
+ },
+ "scripts": {
+ "test": [
+ "parallel-lint . --exclude vendor --exclude node_modules",
+ "phpcs -p -s",
+ "minus-x check ."
+ ],
+ "fix": [
+ "minus-x fix .",
+ "phpcbf"
+ ]
+ }
+}
diff --git a/OAuth/examples/testClient.php b/OAuth/examples/testClient.php
new file mode 100644
index 00000000..0869257d
--- /dev/null
+++ b/OAuth/examples/testClient.php
@@ -0,0 +1,228 @@
+<?php
+
+if ( PHP_SAPI !== 'cli' ) {
+ die( "CLI-only test script\n" );
+}
+
+/**
+ * Testing integration with MediaWiki OAuth Extension
+ *
+ * The current extension follows OAuth 1.0a spec and while the
+ * extension works, you have to be aware of a few quirks.
+ *
+ * This sample is there to help you work your mind through the OAuth
+ * process. Its assuming your MediaWiki installation has this extension
+ * installed so you can become your own OAuth service provider. In other
+ * words, users in the wiki will be able to make various APIs calls
+ * to this wiki using OAuth tokens.
+ *
+ * PLEASE NOTE:
+ *
+ * Remember that OAuth 1.0 expects that the GET Request parameters
+ * are sorted in some order, then to have it hashed.
+ *
+ * In relation to MW; One known caveat is that the `$baseurl` has to
+ * be calling to your MediaWiki's `index.php` with
+ * `index.php?title=Special:OAuth` directly.
+ *
+ * Otherwise the extension will return an URL that way, and will break the hash
+ * signature and you will get an error.
+ */
+
+require __DIR__ . '/../lib/OAuth.php';
+
+/**
+ * Local to this example
+ *
+ * Whether you want to also see
+ * the objects being sent to the wire.
+ */
+$moreVerbose = false;
+
+/**
+ * Consumer key
+ *
+ * This is the application key you would
+ * get from the application you want to connect
+ * with your MediaWiki installation.
+ */
+$consumerKey = 'dpf43f3p2l4k3l03';
+
+/**
+ * Secret
+ *
+ * This is the generated secret key
+ * that you would get when you ask.
+ */
+$consumerSecret = 'kd94hf93k423kf44';
+
+/**
+ * Base URL
+ *
+ * Set to your MediaWiki address with "index.php?title=Special:OAuth".
+ *
+ * Remember that its a known limitation that you cannot use pretty URLs
+ * in this context.
+ *
+ * Ideally, you should have a SSL VirtualHost, but this test would not
+ * fail if you don't have one yet.
+ */
+$baseurl = 'https://localhost/w/index.php?title=Special:OAuth';
+
+/**
+ * Request token (a.k.a. the first step)
+ *
+ * The first step starts at "Special:OAuth/initiate" from the extension.
+ *
+ * Note that the `oauth_callback=oob` means "Out Of Band", and we currently
+ * cannot generate an URL based on headers, but from contents of the Response
+ * body (hence "out of band").
+ *
+ * This is due to the fact that the way the extension is made, it'll return
+ * something in the Response body that will need to create the link and
+ * make the user validate, and get the token.
+ */
+$request_token_url = $baseurl . '/initiate&format=json&oauth_callback=oob';
+
+/**
+ * Validate token (a.k.a. the 2nd step)
+ *
+ * This is the URL you use to send back to the application
+ * when that the connecting application gives you when the
+ * user accepted the request.
+ */
+$validate_token_url = $baseurl . '/token&format=json';
+
+/**
+ * You should not need to edit anything else beyond this point
+ */
+
+// This is to allow you to work without SSL locally
+$baseUrlIsSsl = (bool)preg_match( '/^https/i', $baseurl );
+
+print <<<HELPTEXT
+
+ Testing OAuth integration with MediaWiki.
+
+HELPTEXT;
+
+/**
+ * First step
+ */
+$c = new OAuthConsumer( $consumerKey, $consumerSecret );
+$parsed = parse_url( $request_token_url );
+$params = [];
+parse_str( $parsed['query'], $params );
+$req_req = OAuthRequest::from_consumer_and_token( $c, null, "GET", $request_token_url, $params );
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+$sig_method = $hmac_method;
+$req_req->sign_request( $sig_method, $c, null );
+
+print <<<HELPTEXT
+
+
+ First step, asking for an URL to send the user to.
+
+
+HELPTEXT;
+
+echo "Calling: $req_req" . PHP_EOL;
+
+$ch = curl_init();
+curl_setopt( $ch, CURLOPT_URL, (string)$req_req );
+if ( $baseUrlIsSsl === true ) {
+ curl_setopt( $ch, CURLOPT_PORT, 443 );
+ curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
+}
+curl_setopt( $ch, CURLOPT_HEADER, 0 );
+curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+if ( $moreVerbose === true ) {
+ curl_setopt( $ch, CURLOPT_VERBOSE, 1 );
+}
+$data = curl_exec( $ch );
+
+if ( !$data ) {
+ die( 'cURL error: ' . curl_error( $ch ) );
+}
+
+$token = json_decode( $data );
+
+// @codingStandardsIgnoreStart
+print <<<HELPTEXT
+
+ Response body should be a JSON object with three keys:
+ - key
+ - secret
+ - oauth_callback_confirmed
+
+ You got: {$data}
+
+
+ ************************
+
+ Step two!
+
+ So far, we made one request and we should have what we need to get acknowledgement from the end user.
+
+ In order to continue, we have to ask the user for a permission. With what we just did, it gave us a one-time URL to send our user to.
+
+ The process can continue only if the user accepted it. Once accepted, MediaWiki's OAuth Extension creates an "oauth_verifier" string that you need to give for the next step.
+
+ Now, WITH YOUR WEB BROWSER, follow this link and pass through the validation.
+
+ Link: {$baseurl}/authorize&oauth_token={$token->key}&oauth_consumer_key={$consumerKey}
+
+
+HELPTEXT;
+// @codingStandardsIgnoreEnd
+
+// ACCESS TOKEN
+print 'What was the "verification value" the MediaWiki installation gave?' . PHP_EOL;
+$fh = fopen( "php://stdin", "r" );
+$line = fgets( $fh );
+
+/**
+ * Second step
+ */
+$rc = new OAuthConsumer( $token->key, $token->secret );
+$parsed = parse_url( $validate_token_url );
+parse_str( $parsed['query'], $params );
+$params['oauth_verifier'] = trim( $line );
+
+$acc_req = OAuthRequest::from_consumer_and_token( $c, $rc, "GET", $validate_token_url, $params );
+$acc_req->sign_request( $sig_method, $c, $rc );
+
+print <<<HELPTEXT
+
+ Going to validate token with another Request to the backend...
+
+HELPTEXT;
+
+echo "Calling: $acc_req" . PHP_EOL;
+
+unset( $ch );
+$ch = curl_init();
+curl_setopt( $ch, CURLOPT_URL, (string)$acc_req );
+if ( $baseUrlIsSsl === true ) {
+ curl_setopt( $ch, CURLOPT_PORT, 443 );
+ curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
+}
+curl_setopt( $ch, CURLOPT_HEADER, 0 );
+curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+if ( $moreVerbose === true ) {
+ curl_setopt( $ch, CURLOPT_VERBOSE, 1 );
+}
+$data = curl_exec( $ch );
+if ( !$data ) {
+ echo 'Curl error: ' . curl_error( $ch );
+}
+
+print <<<HELPTEXT
+
+ If all worked well, you should have a JSON object with two keys: key, secret.
+
+ You got:
+
+HELPTEXT;
+
+var_dump( $data );
diff --git a/OAuth/examples/testClientHeaders.php b/OAuth/examples/testClientHeaders.php
new file mode 100644
index 00000000..431ba910
--- /dev/null
+++ b/OAuth/examples/testClientHeaders.php
@@ -0,0 +1,79 @@
+<?php
+
+if ( PHP_SAPI !== 'cli' ) {
+ die( "CLI-only test script\n" );
+}
+
+/**
+ * A basic client for overall testing
+ */
+
+require '../lib/OAuth.php';
+
+$consumerKey = 'dpf43f3p2l4k3l03';
+$consumerSecret = 'kd94hf93k423kf44';
+$baseurl = 'https://localhost/wiki/index.php?title=Special:OAuth';
+$endpoint = $baseurl . '/initiate&format=json&oauth_callback=oob';
+
+$endpoint_acc = $baseurl . '/token&format=json';
+
+$c = new OAuthConsumer( $consumerKey, $consumerSecret );
+$parsed = parse_url( $endpoint );
+$params = [];
+parse_str( $parsed['query'], $params );
+$req_req = OAuthRequest::from_consumer_and_token( $c, null, "GET", $endpoint, $params );
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+$sig_method = $hmac_method;
+$req_req->sign_request( $sig_method, $c, null );
+
+$headers = [ $req_req->to_header() ];
+
+echo "Calling: $endpoint\nHeader: {$headers[0]}\n\n";
+
+$ch = curl_init();
+curl_setopt( $ch, CURLOPT_URL, $endpoint );
+curl_setopt( $ch, CURLOPT_PORT, 443 );
+curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
+curl_setopt( $ch, CURLOPT_HEADER, 0 );
+curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
+$data = curl_exec( $ch );
+
+if ( !$data ) {
+ echo 'Curl error: ' . curl_error( $ch );
+}
+
+echo "Returned: $data\n\n";
+
+$token = json_decode( $data );
+
+print "Visit $baseurl/authorize&oauth_token={$token->key}&oauth_consumer_key=$consumerKey\n";
+
+// ACCESS TOKEN
+print "Enter the verification code:\n";
+$fh = fopen( "php://stdin", "r" );
+$line = fgets( $fh );
+
+$rc = new OAuthConsumer( $token->key, $token->secret );
+$parsed = parse_url( $endpoint_acc );
+parse_str( $parsed['query'], $params );
+$params['oauth_verifier'] = trim( $line );
+
+$acc_req = OAuthRequest::from_consumer_and_token( $c, $rc, "GET", $endpoint_acc, $params );
+$acc_req->sign_request( $sig_method, $c, $rc );
+
+echo "Calling: $acc_req\n";
+
+unset( $ch );
+$ch = curl_init();
+curl_setopt( $ch, CURLOPT_URL, (string)$acc_req );
+curl_setopt( $ch, CURLOPT_PORT, 443 );
+curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
+curl_setopt( $ch, CURLOPT_HEADER, 0 );
+curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+$data = curl_exec( $ch );
+if ( !$data ) {
+ echo 'Curl error: ' . curl_error( $ch );
+}
+
+echo "Returned: $data\n\n";
diff --git a/OAuth/extension.json b/OAuth/extension.json
new file mode 100644
index 00000000..c9d3dcee
--- /dev/null
+++ b/OAuth/extension.json
@@ -0,0 +1,197 @@
+{
+ "name": "OAuth",
+ "author": [
+ "Aaron Schulz",
+ "Chris Steipp",
+ "Brad Jorsch",
+ "Robert Vogel",
+ "Dejan Savuljesku"
+ ],
+ "url": "https://www.mediawiki.org/wiki/Extension:OAuth",
+ "descriptionmsg": "mwoauth-desc",
+ "license-name": "GPL-2.0-or-later",
+ "type": "other",
+ "requires": {
+ "MediaWiki": ">= 1.34.0"
+ },
+ "ExtensionFunctions": [
+ "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onExtensionFunctions"
+ ],
+ "DefaultUserOptions": {
+ "echo-subscriptions-web-oauth-owner": true,
+ "echo-subscriptions-email-oauth-owner": true,
+ "echo-subscriptions-web-oauth-admin": true,
+ "echo-subscriptions-email-oauth-admin": true
+ },
+ "GroupPermissions": {
+ "user": {
+ "mwoauthmanagemygrants": true
+ }
+ },
+ "AvailableRights": [
+ "mwoauthproposeconsumer",
+ "mwoauthupdateownconsumer",
+ "mwoauthmanageconsumer",
+ "mwoauthsuppress",
+ "mwoauthviewsuppressed",
+ "mwoauthviewprivate",
+ "mwoauthmanagemygrants"
+ ],
+ "SessionProviders": {
+ "MediaWiki\\Extensions\\OAuth\\SessionProvider": {
+ "class": "MediaWiki\\Extensions\\OAuth\\SessionProvider",
+ "args": []
+ }
+ },
+ "SpecialPages": {
+ "OAuth": "MediaWiki\\Extensions\\OAuth\\Frontend\\SpecialPages\\SpecialMWOAuth",
+ "OAuthManageMyGrants": "MediaWiki\\Extensions\\OAuth\\Frontend\\SpecialPages\\SpecialMWOAuthManageMyGrants",
+ "OAuthListConsumers": "MediaWiki\\Extensions\\OAuth\\Frontend\\SpecialPages\\SpecialMWOAuthListConsumers"
+ },
+ "MessagesDirs": {
+ "MWOAuth": [
+ "i18n"
+ ]
+ },
+ "ExtensionMessagesFiles": {
+ "MWOAuthAliases": "MWOAuth.alias.php"
+ },
+ "AutoloadNamespaces": {
+ "MediaWiki\\Extensions\\OAuth\\": "src/"
+ },
+ "TestAutoloadNamespaces": {
+ "MediaWiki\\Extensions\\OAuth\\Tests\\": "tests/phpunit/"
+ },
+ "ResourceModules": {
+ "ext.MWOAuth.styles": {
+ "styles": [
+ "ext.MWOAuth.BasicStyles.css",
+ "ext.MWOAuth.AuthorizeForm.css"
+ ]
+ },
+ "ext.MWOAuth.AuthorizeDialog": {
+ "scripts": [
+ "ext.MWOAuth.AuthorizeDialog.js"
+ ],
+ "dependencies": [
+ "jquery.ui"
+ ],
+ "messages": [
+ "mwoauth-desc"
+ ]
+ }
+ },
+ "ResourceFileModulePaths": {
+ "localBasePath": "resources/modules",
+ "remoteExtPath": "OAuth/resources/modules"
+ },
+ "callback": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onRegistration",
+ "Hooks": {
+ "ChangeTagCanCreate": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onChangeTagCanCreate",
+ "MergeAccountFromTo": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onMergeAccountFromTo",
+ "CentralAuthGlobalUserMerged": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onCentralAuthGlobalUserMerged",
+ "LoadExtensionSchemaUpdates": "MediaWiki\\Extensions\\OAuth\\Backend\\UpdaterHooks::addSchemaUpdates",
+ "GetPreferences": "MediaWiki\\Extensions\\OAuth\\Frontend\\UIHooks::onGetPreferences",
+ "MessagesPreLoad": "MediaWiki\\Extensions\\OAuth\\Frontend\\UIHooks::onMessagesPreLoad",
+ "SpecialPageAfterExecute": "MediaWiki\\Extensions\\OAuth\\Frontend\\UIHooks::onSpecialPageAfterExecute",
+ "SpecialPageBeforeFormDisplay": "MediaWiki\\Extensions\\OAuth\\Frontend\\UIHooks::onSpecialPageBeforeFormDisplay",
+ "BeforeCreateEchoEvent": "MediaWiki\\Extensions\\OAuth\\Frontend\\UIHooks::onBeforeCreateEchoEvent",
+ "CentralAuthAbortCentralAuthToken": "MediaWiki\\Extensions\\OAuth\\Setup::onCentralAuthAbortCentralAuthToken",
+ "TestCanonicalRedirect": "MediaWiki\\Extensions\\OAuth\\Setup::onTestCanonicalRedirect",
+ "SetupAfterCache": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onSetupAfterCache",
+ "ApiRsdServiceApis": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onApiRsdServiceApis",
+ "SpecialPage_initList": "MediaWiki\\Extensions\\OAuth\\Frontend\\UIHooks::onSpecialPage_initList",
+ "ListDefinedTags": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onListDefinedTags",
+ "ChangeTagsListActive": "MediaWiki\\Extensions\\OAuth\\Backend\\Hooks::onChangeTagsListActive",
+ "LoginFormValidErrorMessages": "MediaWiki\\Extensions\\OAuth\\Frontend\\UIHooks::onLoginFormValidErrorMessages"
+ },
+ "ConfigRegistry": {
+ "mwoauth": "GlobalVarConfig::newInstance"
+ },
+ "config": {
+ "MWOAuthCentralWiki": {
+ "value": false,
+ "description": "Wiki ID of OAuth management wiki. On wiki farms, it makes sense to set this to a wiki that acts as a portal site, is decidated to management, or just handles login/authentication. It can, however, be set to any wiki if the farm. For single-wiki sites or farms where each wiki manages consumers separately, it should be left as false."
+ },
+ "MWOAuthSharedUserIDs": {
+ "value": false,
+ "description": "Whether shared global user IDs are stored in the oauth tables. On wiki farms with a central authentication system (with integer user IDs) that share a single OAuth management wiki, this must be set to true. If wikis have a central authentication system but have their own OAuth management, then this can be either true or false. Otherwise it should always be set to false Setting this to true requires CentralIdLookup or an MWOAuth aware authentication extension. This value should not be changed after the fact to avoid ambigious IDs. Proper user ID migration should be done before any such changes."
+ },
+ "MWOAuthSharedUserSource": {
+ "value": null,
+ "description": "Source of shared user IDs, if enabled. If CentralIdLookup is available, this is the $providerId for CentralIdLookup::factory(). Generally null would be what you want, to use the default provider. If that class is not available or the named provider is not found, this is\n * passed to the 'OAuthGetUserNamesFromCentralIds', 'OAuthGetLocalUserFromCentralId', 'OAuthGetCentralIdFromLocalUser', and 'OAuthGetCentralIdFromUserName' hooks. This has no effect if $wgMWOAuthSharedUserIDs is set to false."
+ },
+ "MWOAuthRequestExpirationAge": {
+ "value": 2592000,
+ "description": "Seconds after which an idle consumer request is marked as \"expired\""
+ },
+ "MWOAuthSecureTokenTransfer": {
+ "value": true,
+ "description": "Require HTTPs for user transactions that might send out secret tokens"
+ },
+ "MWOauthDisabledApiModules": {
+ "value": [],
+ "description": "List of API module classes to disable when OAuth is used for the request"
+ },
+ "MWOAuthReadOnly": {
+ "value": false,
+ "description": "Prevent write activity to the database. When this is set, consumers cannot be added or updated, and new authorizations are prohibited. Authorization headers for existing authorizations will continue to work. Useful for migrating database tables"
+ },
+ "OAuthGroupsToNotify": {
+ "value": [],
+ "description": "User groups to notify about new consumers that need to be reviewed"
+ },
+ "MWOAuthSessionCacheType": {
+ "value": null
+ },
+ "OAuthSecretKey": {
+ "value": null,
+ "description": "Random 32-bit string used to encrypt OAuth requests. Generate using \"base64_encode(random_bytes(32))\""
+ },
+ "OAuth2EnabledGrantTypes": {
+ "value": [
+ "authorization_code",
+ "refresh_token",
+ "client_credentials"
+ ],
+ "description": "List of OAuth2 grants that client applications can use"
+ },
+ "OAuth2PrivateKey": {
+ "value": "",
+ "description": "Private key or a path to the private key used to sign OAuth2 JWT being transmitted"
+ },
+ "OAuth2PublicKey": {
+ "value": "",
+ "description": "Public key or a path to the public key used to verify OAuth2 resource requests"
+ },
+ "OAuth2RequireCodeChallengeForPublicClients": {
+ "value": true,
+ "description": "Controls whether clients are required to send code challenges with OAuth2 requests. This only applies to non-confidential clients"
+ },
+ "OAuth2GrantExpirationInterval": {
+ "description": "Controls validity period for access tokens. Does not apply to owner-only clients, whose access tokens are always non-expiring. Accepts ISO 8601 durations or can be set to \"infinity\" or false for non-expiring tokens",
+ "value": "PT1H"
+ },
+ "OAuth2RefreshTokenTTL": {
+ "description": "Controls validity period for refresh tokens. Accepts ISO 8601 durations or can be set to \"infinity\" or false for non-expiring tokens",
+ "value": "PT1M"
+ }
+ },
+ "RestRoutes": [
+ {
+ "path": "/oauth2/authorize",
+ "factory": "MediaWiki\\Extensions\\OAuth\\Rest\\Handler\\Authorize::factory"
+ },
+ {
+ "path": "/oauth2/access_token",
+ "factory": "MediaWiki\\Extensions\\OAuth\\Rest\\Handler\\AccessToken::factory",
+ "method": "POST"
+ },
+ {
+ "path": "/oauth2/resource/{type}",
+ "factory": "MediaWiki\\Extensions\\OAuth\\Rest\\Handler\\Resource::factory"
+ }
+ ],
+ "load_composer_autoloader": true,
+ "manifest_version": 2
+}
diff --git a/OAuth/i18n/af.json b/OAuth/i18n/af.json
new file mode 100644
index 00000000..76cc3d82
--- /dev/null
+++ b/OAuth/i18n/af.json
@@ -0,0 +1,39 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joris Darlington Quarshie",
+ "Naudefj"
+ ]
+ },
+ "mwoauth-desc": "Laat gebruik van OAuth 1.0a en OAuth 2.0 toe vir API matiging",
+ "mwoauth-prefs-managegrantslink": "Bestuur $1 gekoppelde {{PLURAL:$1|toepassing|toepassings}}",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "U OAuth 2.0-kliënt is geskep.\n\nJou tokens is:\n; Kliënt-toepassingsleutel: $1\n; Kliënt-toepassingsgeheim: $2\n; Toegangstoken: $3\n; <em> Teken dit asseblief vir toekomstige verwysing aan. </em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "\nU OAuth 2.0-verbruikerstoken is teruggestel. Die nuwe tekens is:\n; Verbruikerstoken:$1\n; Verbruikersgeheim:$2\n; Toegangstoken:$3\n<em> Teken dit asseblief vir toekomstige verwysing aan.</em>",
+ "oauthlistconsumers": "Lys van OAuth-toepassings",
+ "oauthmanagemygrants": "Beheer gekoppelde toepassings",
+ "mwoauthmanagemygrants-confirm-legend": "Bestuur gekoppelde toepassing",
+ "mwoauth-oauth-version": "OAuth protokolweergawe",
+ "mwoauth-oauth2-is-confidential": "Client is confidential",
+ "mwoauth-oauth2-is-confidential-help": "'N Vertroulike kliënt is 'n toepassing wat die kliëntwagwoord vir die wêreld vertroulik kan hou.Nie-verttroulike kliënte is minder veilig.",
+ "mwoauth-oauth2-granttypes": "Toegestane OAuth2-toekenningstipes",
+ "mwoauth-oauth2-granttype-auth-code": "Matigingskode",
+ "mwoauth-oauth2-granttype-refresh-token": "Verfris token",
+ "mwoauth-oauth2-granttype-client-credentials": "kredietbewyse",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Kan nie toegangstoken skep nie,die gebruiker het nie die toegangtoken uitgereik nie",
+ "mwoauth-oauth2-error-user-approval-deny": "Gebruiker het die versoek van die kliëntaansoek afgekeur",
+ "mwoauth-oauth-unsupported-version": "Hierdie eindpunt is nie toegelaat vir OAuth weergawe $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "Omvang \"$1\" is nie toegelaat vir hierdie toepassing nie",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "\nKliënte slegs deur eienaars moet toegelaat word om kliënt_bewyse te gebruik",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Kon nie toegangstoken kry nie: $1",
+ "mwoauth-oauth2-error-server-error": "Die magtingingsbediener het 'n onverwagte voorwaarde ondervind wat verhoed het dat hy aan die versoek voldoen.",
+ "mwoauth-oauth2-error-invalid-request": "Die versoek ontbreek 'n vereiste parameter,bevat 'n ongeldige parameterwaarde,bevat 'n parameter meer as een keer of is andersins misvorm",
+ "mwoauth-oauth2-error-unauthorized-client": "\nDie kliënt is nie gemagtig om 'n magtigingskode op hierdie metode aan te vra nie.",
+ "mwoauth-oauth2-error-access-denied": "\nDie hulpbronneienaar of magtigingsbediener het die versoek geweier.",
+ "mwoauth-oauth2-error-unsupported-response-type": "Die matigingsbediener ondersteun nie die verkryging van 'n magtigingskode op herdie metode nie.",
+ "mwoauth-oauth2-error-invalid-scope": "Die versoekte omvang is ongeldig,onbekend of verkeerd gevorm.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "Die matigingsbediener kan tans nie die versoek hanteer nie weens die tydelike oorlading of instandhouding van die bediener.",
+ "mwoauth-oauth2-error-invalid-client": "\nKliëntverifikasie het misluk (bv. Onbekende kliënt, geen kliëntverifikasie ingesluit nie, of nie-ondersteunde stawingsmetode)",
+ "mwoauth-oauth2-error-request-not-verified": "Probeer om die geverifieerdie eiendom terug te kry voordat die versoek geverifieer word",
+ "mwoauth-oauth2-invalid-access-token": "Ongeldige toegangsteken",
+ "mwoauth-consumer-access-no-user": "Goedkeuring deur die verbruiker moet aan 'n geldige gebruiker gebonde wees, met die gebruiker met ID 0 gegee"
+}
diff --git a/OAuth/i18n/ar.json b/OAuth/i18n/ar.json
new file mode 100644
index 00000000..1555f91a
--- /dev/null
+++ b/OAuth/i18n/ar.json
@@ -0,0 +1,318 @@
+{
+ "@metadata": {
+ "authors": [
+ "Claw eg",
+ "Ibrahim.ID",
+ "Maqu630",
+ "Meno25",
+ "Moud hosny",
+ "Tarawneh",
+ "ديفيد",
+ "علاء",
+ "محمد أحمد عبد الفتاح",
+ "مشعل الحربي"
+ ]
+ },
+ "oauth": "أو أوث",
+ "mwoauth-desc": "يسمح باستخدام OAuth 1.0a و OAuth 2.0 لتفويض API",
+ "mwoauth-verified": "تم السماح لهذا التطبيق بالولوج الى الميدياويكي نيابة عنك.\n\nلإتمام هذه العملية، الرجاء نقل رمز التأكيد الى التطبيق : '''$1'''",
+ "mwoauth-db-readonly": "يتم قفل قاعدة بيانات OAuth مؤقتا; تُرجَى المحاولة مرة أخرى خلال بضع دقائق.",
+ "mwoauth-missing-field": "لم يتم إدخال قيمة في حقل \"$1\"",
+ "mwoauth-invalid-field": "قيمة غير صحيحة ضمن حقل :$1\"",
+ "mwoauth-invalid-field-generic": "تم إعطاء قيمة غير صحيحة",
+ "mwoauth-field-hidden": "(هذه المعلومة مخفية)",
+ "mwoauth-field-private": "(هذه المعلومات خاصة)",
+ "mwoauth-prefs-managegrants": "التطبيقات المتصلة:",
+ "mwoauth-prefs-managegrantslink": "تحكم {{PLURAL:$1|بالتطبيق المتصل|بالتطبيقات ال$1 المتصلة|0=بالتطبيقات المتصلة}}",
+ "mwoauth-consumer-allwikis": "جميع المشاريع ضمن هذا الموقع",
+ "mwoauth-consumer-key": "رمز المستهلك:",
+ "mwoauth-consumer-name": "اسم التطبيق:",
+ "mwoauth-consumer-version": "إصدار المستهلك:",
+ "mwoauth-consumer-user": "النّاشر:",
+ "mwoauth-consumer-stage": "الوضع الحالي:",
+ "mwoauth-consumer-email": "البريد الإلكتروني:",
+ "mwoauth-consumer-email-help": "مرئية فقط لأولئك الذين يوافقون على المستهلكين الجدد",
+ "mwoauth-consumer-owner-only-label": "المالك فقط:",
+ "mwoauth-consumer-owner-only": "هذا المستهلك للاستخدام فقط بواسطة $1.",
+ "mwoauth-consumer-owner-only-help": "سيؤدي تحديد هذا الخيار إلى الموافقة على المستهلك وقبوله تلقائيا للاستخدام بواسطة $1، لن يكون قابلا للاستخدام من قِبل أي مستخدم آخر، ولن يعمل تدفق التفويض المعتاد، لن يتم وسم الإجراءات المتخذة باستخدام هذا المستهلك.",
+ "mwoauth-consumer-description": "وصف للتطبيق:",
+ "mwoauth-consumer-callbackurl": "مسار \"معاودة اتصال\" OAuth:",
+ "mwoauth-consumer-callbackisprefix": "السماح للمستهلك بتحديد رد اتصال في الطلبات واستخدام مسار \"معاودة الاتصال\" أعلاه كبادئة مطلوبة.",
+ "mwoauth-consumer-granttypes": "أنواع المنح المطلوبة:",
+ "mwoauth-consumer-grantsneeded": "المنح القابلة للتطبيق:",
+ "mwoauth-consumer-required-grant": "يمكن تطبيقها على المستهلك",
+ "mwoauth-consumer-wiki": "مشروع قابل للتطبيق:",
+ "mwoauth-consumer-wiki-thiswiki": "الويكي الحالية ($1)",
+ "mwoauth-consumer-restrictions": "قيود الاستخدام:",
+ "mwoauth-consumer-restrictions-json": "قيود الاستخدام (جسون):",
+ "mwoauth-consumer-rsakey": "مفتاح RSA العام (اختياري):",
+ "mwoauth-consumer-rsakey-help": "أدخل مفتاحا عاما لاستخدام طريقة توقيع RSA-SHA1، اتركه فارغا لاستخدام HMAC-SHA1 بسر عشوائي، إذا لم تكن متأكدا من ذلك، فاتركه فارغا.",
+ "mwoauth-consumer-secretkey": "الرمز السري للمستهلك:",
+ "mwoauth-consumer-accesstoken": "رمز الوصول:",
+ "mwoauth-consumer-reason": "السبب:",
+ "mwoauth-consumer-developer-agreement": "بواسطة تقديم هذا الطلب، فإنك تقر بأننا نحتفظ بالحق في تعطيل طلبك، أو إزالة أو تقييد وصولك أو وصول تطبيقك إلى هذا الموقع، ومتابعة أي مسار عمل آخر نراه مناسبا إذا كنا نعتقد، في حكمنا الوحيد، بأنك أو تطبيقك ينتهك أية سياسة، وتوجيه، ومبدأ توجيه لهذا الموقع، يمكننا تغيير \"سياسة التطبيق\" هذه في أي وقت دون إشعار مسبق، حسب تقديرنا وحسبما نراه ضروريا، يمثل استمرار استخدامك لـOAuth قبولا لهذه التغييرات.",
+ "mwoauth-consumer-email-unconfirmed": "بريدك الألكتروني لم يتم تفعيله بعد.",
+ "mwoauth-consumer-email-mismatched": "يجب أن يتطابق البريد الإلكتروني مع الحساب الخاص بك.",
+ "mwoauth-consumer-alreadyexists": "يوجد بالفعل مستهلك يحتوي على هذا الاسم/الإصدار/الناشر",
+ "mwoauth-consumer-alreadyexistsversion": "يوجد بالفعل مستهلك يحتوي على تركيبة الاسم / الناشر هذه مع إصدار مساوٍ أو أعلى (\"$1\")",
+ "mwoauth-consumer-not-accepted": "لا يمكن تحديث المعلومات لطلب مستهلك معلق",
+ "mwoauth-consumer-not-proposed": "المستهلك ليس مقترحا حاليا",
+ "mwoauth-consumer-not-disabled": "المستهلك غير معطل حاليا",
+ "mwoauth-consumer-not-approved": "المستهلك غير معتمد (ربما تم تعطيله)",
+ "mwoauth-missing-consumer-key": "لم يتم تقديم مفتاح مستهلك.",
+ "mwoauth-invalid-consumer-key": "لا يوجد مستهلك بالمفتاح المحدد.",
+ "mwoauth-invalid-access-token": "لا يوجد رمز وصول بالمفتاح المحدد.",
+ "mwoauth-invalid-access-wrongwiki": "لا يمكن استخدام المستهلك إلا في مشروع \"$1\".",
+ "mwoauth-consumer-conflict": "غيَّر شخص ما سمات هذا المستهلك أثناء مشاهدته; حاول مرة اخرى، قد ترغب في التحقق من سجل التغيير.",
+ "mwoauth-consumer-grantshelp": "تمنح كل منحة حق الوصول إلى صلاحيات المستخدم المدرجة التي يمتلكها حساب المستخدم بالفعل، انظر [[Special:ListGrants|جدول المنح]] لمزيد من المعلومات.",
+ "mwoauth-consumer-stage-proposed": "المقترحة",
+ "mwoauth-consumer-stage-rejected": "المرفوضة",
+ "mwoauth-consumer-stage-expired": "منتهية الصلاحية",
+ "mwoauth-consumer-stage-approved": "المعتمدة",
+ "mwoauth-consumer-stage-disabled": "المعطلة",
+ "mwoauth-consumer-stage-suppressed": "المخفية",
+ "oauthconsumerregistration": "تسجيل مستهلك OAuth",
+ "mwoauthconsumerregistration-navigation": "تصفح:",
+ "mwoauthconsumerregistration-propose": "اقتراح مستهلك جديد",
+ "mwoauthconsumerregistration-list": "قائمة المستهلكين الخاصة بي",
+ "mwoauthconsumerregistration-main": "الرئيسي",
+ "mwoauthconsumerregistration-propose-text": "يجب على المطورين استخدام النموذج أدناه لاقتراح مستهلك OAuth جديد (راجع [//www.mediawiki.org/wiki/Extension:OAuth توثيق التمديد] لمزيد من التفاصيل)، بعد إرسال هذا النموذج، ستتلقى رمزا سيستخدمه تطبيقك لتعريف نفسه بميدياويكي، سيحتاج إداري OAuth إلى الموافقة على طلب اشتراكك قبل تفويضه من قبل مستخدمين آخرين.\n\nبعض التوصيات والملاحظات: \n* حاول استخدام عدد قليل من المنح قدر الإمكان، تجنب المنح التي ليست مطلوبة فعليا الآن. \n* تكون الإصدارات على شكل \"major.minor.release\" (الخياران الآخران اختياريان) وتزداد الحاجة إلى إجراء تغييرات على المنحة. \n* يُرجَى تقديم مفتاح RSA عام (بتنسيق PEM) إذا أمكن، وإلا يجب استخدام رمز سري (أقل أمانا).\n* يمكنك استخدام معرف مشروع لتقييد المستهلك إلى مشروع واحد في هذا الموقع (استخدم \"*\" لجميع المشاريع).",
+ "mwoauthconsumerregistration-update-text": "استخدم النموذج أدناه لتحديث جوانب عميل OAuth الذي تتحكم فيه.\n\nجميع القيم هنا ستستبدل أي قيم سابقة، لا تترك حقولا فارغة إلا إذا كنت تنوي مسح هذه القيم.",
+ "mwoauthconsumerregistration-maintext": "تهدف هذه الصفحة إلى السماح لمطوري البرامج باقتراح وتحديث تطبيقات مستهلكي OAuth في سجل هذا الموقع. \n\nمن هنا، يمكنك:\n* [[Special:OAuthConsumerRegistration/propose|طلب رمز مميز لمستهلك جديد]].\n* [[Special:OAuthConsumerRegistration/list|إدارة عملائك الحاليين]].",
+ "mwoauthconsumerregistration-propose-legend": "تطبيق مستهلك جديد لـOAuth",
+ "mwoauthconsumerregistration-update-legend": "تحديث تطبيق مستهلك OAuth",
+ "mwoauthconsumerregistration-propose-submit": "اقتراح مستهلك",
+ "mwoauthconsumerregistration-update-submit": "تحديث مستهلك",
+ "mwoauthconsumerregistration-none": "أنت لا تتحكم في أي من عملاء OAuth.",
+ "mwoauthconsumerregistration-name": "المستهلك",
+ "mwoauthconsumerregistration-user": "الناشر",
+ "mwoauthconsumerregistration-description": "الوصف",
+ "mwoauthconsumerregistration-email": "تواصل بالبريد الإكتروني",
+ "mwoauthconsumerregistration-consumerkey": "مفتاح المستهلك",
+ "mwoauthconsumerregistration-stage": "الحالة",
+ "mwoauthconsumerregistration-lastchange": "آخر تغيير",
+ "mwoauthconsumerregistration-manage": "إدارة",
+ "mwoauthconsumerregistration-resetsecretkey": "إعادة تعيين المفتاح السري إلى قيمة جديدة",
+ "mwoauthconsumerregistration-proposed": "تم استلام طلب مستهلك OAuth الخاص بك.\n\nلقد تم تعيين رمز مستهلك لـ'''$1''' ورمز سري لـ'''$2'''. ''الرجاء تسجيلهما للرجوع إليها مستقبلا.''",
+ "mwoauthconsumerregistration-created-owner-only": "تم إنشاء مستهلك OAuth الخاص بك.\n\nرموزك المميزة هي:\n; رمز المستهلك: $1\n; سر المستهلك: $2\n; رمز الوصول: $3\n; سر الوصول: $4\n<em>الرجاء تسجيلها للرجوع إليها مستقبلا.</em>",
+ "mwoauthconsumerregistration-updated": "تم تحديث سجل مستهلك OAuth الخاص بك.",
+ "mwoauthconsumerregistration-secretreset": "لقد تم تعيين الرمز السري للمستهلك '''$1'''، ''الرجاء تسجيله للرجوع إليه في المستقبل.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "تمت إعادة تعيين الرموز المميزة للمستهلك في OAuth، الرموز الجديدة هي:\n\nرموزك المميزة هي:\n; رمز المستهلك: $1\n; سر المستهلك: $2\n; رمز الوصول: $3\n; سر الوصول: $4\n<em>الرجاء تسجيلها للرجوع إليها مستقبلا.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "يجب عليك تأكيد عنوان بريدك الإلكتروني قبل إنشاء تطبيقات OAuth.\nيُرجَى تعيين عنوان بريدك الإلكتروني والتحقق من صحته من خلال [[Special:Preferences|تفضيلات المستخدم]] الخاصة بك.",
+ "oauthmanageconsumers": "التحكم بمستهلكي OAuth",
+ "mwoauthmanageconsumers-notloggedin": "يجب عليك تسجيل الدخول للوصول إلى هذه الصفحة.",
+ "mwoauthmanageconsumers-type": "الطوابير:",
+ "mwoauthmanageconsumers-showproposed": "الطلبات المقترحة",
+ "mwoauthmanageconsumers-showrejected": "الطلبات المرفوضة",
+ "mwoauthmanageconsumers-showexpired": "الطلبات منتهية الصلاحية",
+ "mwoauthmanageconsumers-linkproposed": "الطلبات المقترحة",
+ "mwoauthmanageconsumers-linkrejected": "الطلبات المرفوضة",
+ "mwoauthmanageconsumers-linkexpired": "الطلبات منتهية الصلاحية",
+ "mwoauthmanageconsumers-linkapproved": "الطلبات المعتمدة",
+ "mwoauthmanageconsumers-linkdisabled": "الطلبات المعطلة",
+ "mwoauthmanageconsumers-main": "الرئيسية",
+ "mwoauthmanageconsumers-maintext": "تهدف هذه الصفحة إلى التعامل مع طلبات تقديم طلبات OAuth (انظر http://oauth.net) للمستهلكين وإدارة مستهلكي OAuth مؤكدين.",
+ "mwoauthmanageconsumers-queues": "حدد قائمة انتظار تأكيد مستهلك من الأسفل:",
+ "mwoauthmanageconsumers-q-proposed": "قائمة انتظار طلبات المستهلك المقترحة",
+ "mwoauthmanageconsumers-q-rejected": "قائمة انتظار طلبات المستهلك المرفوضة",
+ "mwoauthmanageconsumers-q-expired": "قائمة انتظار طلبات المستهلك منتهية الصلاحية",
+ "mwoauthmanageconsumers-lists": "حدد قائمة حالة المستهلك من الأسفل:",
+ "mwoauthmanageconsumers-l-approved": "قائمة المستهلكين المعتمدين حاليا",
+ "mwoauthmanageconsumers-l-disabled": "قائمة المستهلكين المعطلين حاليا",
+ "mwoauthmanageconsumers-none-proposed": "لا يوجد مستهلكون مقترحون في هذه القائمة.",
+ "mwoauthmanageconsumers-none-rejected": "لا يوجد مستهلكون مقترحون في هذه القائمة.",
+ "mwoauthmanageconsumers-none-expired": "لا يوجد مستهلكون مقترحون في هذه القائمة.",
+ "mwoauthmanageconsumers-none-approved": "لا يوجد مستهلكون يستوفون هذه المعايير.",
+ "mwoauthmanageconsumers-none-disabled": "لا يوجد مستهلكون يستوفون هذه المعايير.",
+ "mwoauthmanageconsumers-name": "المستهلك",
+ "mwoauthmanageconsumers-user": "الناشر",
+ "mwoauthmanageconsumers-description": "الوصف",
+ "mwoauthmanageconsumers-email": "تواصل بالبريد الإكتروني",
+ "mwoauthmanageconsumers-consumerkey": "مفتاح المستهلك",
+ "mwoauthmanageconsumers-lastchange": "آخر تغيير",
+ "mwoauthmanageconsumers-review": "مراجعة/إدارة",
+ "mwoauthmanageconsumers-confirm-text": "استخدم هذا النموذج للموافقة على هذا المستهلك أو رفضه أو تعطيله أو إعادة تمكينه.",
+ "mwoauthmanageconsumers-confirm-legend": "التحكم بمستهلك OAuth",
+ "mwoauthmanageconsumers-action": "حالة التغيير:",
+ "mwoauthmanageconsumers-approve": "معتمد",
+ "mwoauthmanageconsumers-reject": "مرفوض",
+ "mwoauthmanageconsumers-rsuppress": "رُفِض وأُخفِي",
+ "mwoauthmanageconsumers-disable": "معطل",
+ "mwoauthmanageconsumers-dsuppress": "عُطِّل وأُخفِي",
+ "mwoauthmanageconsumers-reenable": "معتمد",
+ "mwoauthmanageconsumers-reason": "السبب:",
+ "mwoauthmanageconsumers-confirm-submit": "تحديث حالة المستهلك",
+ "mwoauthmanageconsumers-success-approved": "تمت الموافقة على الطلب.",
+ "mwoauthmanageconsumers-success-rejected": "تم رفض الطلب.",
+ "mwoauthmanageconsumers-success-disabled": "تم تعطيل المستهلك.",
+ "mwoauthmanageconsumers-success-reanable": "تمت إعادة تمكين المستهلك.",
+ "mwoauthmanageconsumers-search-name": "مستهلكون بهذا الاسم",
+ "mwoauthmanageconsumers-search-publisher": "مستهلكون من قبل هذا المستخدم",
+ "oauthlistconsumers": "عرض قائمة تطبيقات OAuth",
+ "mwoauthlistconsumers-legend": "تصفح تطبيقات OAuth",
+ "mwoauthlistconsumers-view": "التفاصيل",
+ "mwoauthlistconsumers-none": "لم يتم العثور على أية تطبيقات تستوفي هذه المعايير.",
+ "mwoauthlistconsumers-name": "اسم التطبيق",
+ "mwoauthlistconsumers-version": "إصدار المستهلك",
+ "mwoauthlistconsumers-user": "الناشر",
+ "mwoauthlistconsumers-description": "الوصف",
+ "mwoauthlistconsumers-wiki": "مشروع قابل للتطبيق",
+ "mwoauthlistconsumers-callbackurl": "\"مسار معاودة اتصال\" OAuth:",
+ "mwoauthlistconsumers-callbackisprefix": "السماح للمستهلك بتحديد رد اتصال في الطلبات واستخدام مسار \"معاودة الاتصال\" أعلاه كبادئة مطلوبة.",
+ "mwoauthlistconsumers-grants": "المنح القابلة للتطبيق",
+ "mwoauthlistconsumers-basicgrantsonly": "(الوصول الأساسي فقط)",
+ "mwoauthlistconsumers-status": "الحالة",
+ "mwoauth-consumer-stage-any": "أي",
+ "mwoauthlistconsumers-status-proposed": "مقترح",
+ "mwoauthlistconsumers-status-approved": "مقبول",
+ "mwoauthlistconsumers-status-disabled": "معطل",
+ "mwoauthlistconsumers-status-rejected": "مرفوض",
+ "mwoauthlistconsumers-status-expired": "منتهي الصلاحية",
+ "mwoauthlistconsumers-navigation": "التصفح:",
+ "mwoauthlistconsumers-grants-link": "إدارة المنح",
+ "mwoauthlistconsumers-rclink": "أحدث التغييرات بواسطة هذا التطبيق",
+ "oauthmanagemygrants": "التحكم بالتطبيقات المرتبطة",
+ "mwoauthmanagemygrants-text": "تسرد هذه الصفحة أية تطبيقات يمكنها استخدام حسابك، بالنسبة لأي تطبيق من هذا القبيل، فإن نطاق وصوله يكون محدودا بالأذونات التي منحتها للتطبيق عندما أذنت له بالتصرف نيابة عنك، إذا قمت بتفويض أحد التطبيقات بشكل منفصل للوصول إلى مشاريع شقيقة مختلفة نيابة عنك، فسترى تكوينا منفصلا لكل مشروع من هذا القبيل أدناه.\n\nتصل التطبيقات المتصلة إلى حسابك باستخدام بروتوكول OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth معرفة المزيد حول التطبيقات المتصلة])</span>",
+ "mwoauthmanagemygrants-navigation": "تصفح:",
+ "mwoauthmanagemygrants-showlist": "قائمة التطبيقات المتصلة",
+ "mwoauthmanagemygrants-none": "لا توجد تطبيقات متصلة بحسابك.",
+ "mwoauthmanagemygrants-user": "الناشر:",
+ "mwoauthmanagemygrants-description": "الوصف",
+ "mwoauthmanagemygrants-wikiallowed": "مسموح به في مشروع:",
+ "mwoauthmanagemygrants-grants": "المنح القابلة للتطبيق",
+ "mwoauthmanagemygrants-grantsallowed": "المنح المسموح بها",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "المنح القابلة للتطبيق المسموح بها:",
+ "mwoauthmanagemygrants-review": "إدارة الوصول",
+ "mwoauthmanagemygrants-revoke": "إبطال الوصول",
+ "mwoauthmanagemygrants-grantaccept": "مُنِحت",
+ "mwoauthmanagemygrants-update-text": "استخدم النموذج أدناه لتعديل الأذونات الممنوحة للتطبيق للتصرف نيابة عنك.",
+ "mwoauthmanagemygrants-revoke-text": "استخدم النموذج أدناه لإبطال الوصول إلى أحد التطبيقات للتصرف بالنيابة عنك.",
+ "mwoauthmanagemygrants-confirm-legend": "التحكم بالتطبيق المرتبط",
+ "mwoauthmanagemygrants-update": "تحديث المنح",
+ "mwoauthmanagemygrants-renounce": "إلغاء الاعتماد",
+ "mwoauthmanagemygrants-action": "حالة التغيير:",
+ "mwoauthmanagemygrants-confirm-submit": "تحديث حالة الرمز المميز للوصول",
+ "mwoauthmanagemygrants-success-update": "تم تحديث تفضيلاتك لهذا التطبيق.",
+ "mwoauthmanagemygrants-success-renounce": "لقد تم إبطال دخول التطبيق إلى حسابك.",
+ "mwoauthmanagemygrants-basic-tooltip": "لماذا لا يمكنني تحديث هذه المنحة؟ تمنح هذه المنحة الأذونات الأساسية لتطبيقك المتصل والتي تتطلب عملها بشكل صحيح، إذا كنت لا تريد لهذا التطبيق المتصل الحصول على هذه الحقوق، فيجب عليك إلغاء الوصول إلى التطبيق.",
+ "mwoauthmanagemygrants-authonly-tooltip": "لماذا لا يمكنني تحديث هذه المنحة؟ إذا لم تكن ترغب في أن يكون لهذا التطبيق المتصل هذا الحق، فعليك إلغاء الوصول إلى التطبيق.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|تعديلاتك}} بواسطة هذا التطبيق",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|أفعالك}} بواسطة هذا التطبيق",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2||اقترح|اقترحت}} مستهلك OAuth (مفتاح العميل $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2||حدث|حدثت}} مستهلك OAuth (مفتاح العميل $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|وافق|وافقت}} على مستهلك OAuth بواسطة $3 (مفتاح المستهلك $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|رفض|رفضت}} مستهلك OAuth بواسطة $3 (مفتاح المستهلك $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|عطل|عطلت}} مستهلك OAuth بواسطة $3 (مفتاح المستهلك $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|أعاد|أعادت}} تمكين مستهلك OAuth بواسطة $3 (مفتاح المستهلك $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2||أنشأ|أنشأت}} مستهلك OAuth مالك فقط (مفتاح العميل $4)",
+ "log-action-filter-mwoauthconsumer": "نوع فعل مستهلك OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "موافقة مستهلك OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "إنشاء مستهلك OAuth للمالك فقط",
+ "log-action-filter-mwoauthconsumer-disable": "تعطيل مستهلك OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "مقترح مستهلك OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "إعادة تفعيل OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "رفض مستهلك OAuth",
+ "log-action-filter-mwoauthconsumer-update": "تحديث مستهلك OAuth",
+ "mwoauthconsumer-consumer-logpage": "سجل عميل OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "سجل موافقات ورفض وتعطيل عملاء OAuth المسجلين.",
+ "mwoauth-bad-request-missing-params": "عذرا، حدث خطأ ما في تهيئة هذا التطبيق المتصل; اتصل بمطور التطبيق.\n\n<span class=\"plainlinks mw-mwoautherror-details\">وسيط OAuth مفقود: $1</span>",
+ "mwoauth-bad-request-invalid-action": "عذرا، حدث خطأ ما; ستحتاج إلى الاتصال بكاتب التطبيق للحصول على مساعدة بشأن ذلك.\n\n<span class=\"plainlinks mw-mwoautherror-details\">مسار غير معروف: $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "عذرا، هناك خطأ ما; ستحتاج إلى [$1 الاتصال] بكاتب التطبيق للحصول على مساعدة بشأن ذلك. \n\n<span class=\"plainlinks mw-mwoautherror-details\">مسار غير معروف: $2</span>",
+ "mwoauthdatastore-access-token-not-found": "لم يتم العثور على منحة معتمدة لهذا الرمز المميز للرخصة.",
+ "mwoauthdatastore-request-token-not-found": "عذرًا، حدث خطأ ما في الاتصال بهذا التطبيق; عُد وحاول ربط حسابك مرة أخرى، أو اتصل بكاتب التطبيق.\n\n<span class=\"plainlinks mw-mwoautherror-details\"> لم يتم العثور على رمز OAuth : $1</span>",
+ "mwoauthdatastore-callback-not-found": "لم يتم العثور على مسار لاستعادة OAuth في ذاكرة التخزين المؤقت، ربما هذا خطأ في كيفية تقديم التطبيق طلبات إلى الخادم.",
+ "mwoauthdatastore-request-token-already-used": "اكتمل هذا الطلب بالفعل ولا يمكن إعادة تقديمه، ارجع إلى التطبيق وحاول ربط حسابك مرة أخرى، أو اتصل بكاتب التطبيق.\n\n<span class=\"plainlinks mw-mwoautherror-details\">رمز OAuth مستخدم بالفعل: $1</span>",
+ "mwoauthdatastore-bad-token": "لم يتم العثور على رمز مميز يطابق طلبك.",
+ "mwoauthdatastore-bad-source-ip": "جاء الطلب من عنوان أيبي غير صالح.",
+ "mwoauthdatastore-bad-verifier": "رمز التحقق المقدم غير صالح.",
+ "mwoauthdatastore-invalid-token-type": "نوع الرمز المميز المطلوب غير صالح.",
+ "mwoauthgrants-general-error": "حدث خطأ في طلب OAuth.",
+ "mwoauthserver-bad-consumer": "لم تتم الموافقة على \"$1\" كتطبيق متصل; [$2 اتصل] بكاتب الطلب للمساعدة.\n\n<span class=\"plainlinks mw-mwoautherror-details\">تطبيق OAuth المتصل غير معتمد: $3</span>",
+ "mwoauthserver-bad-consumer-key": "عذرا، حدث خطأ ما في الاتصال بهذا التطبيق.\n\n<span class=\"plainlinks mw-mwoautherror-details\">مفتاح OAuth غير معروف: $1</span>",
+ "mwoauthserver-insufficient-rights": "لا يُسمَح لحسابك باستخدام التطبيقات المتصلة; اتصل بإداري في موقعك لمعرفة السبب.\n\n<span class=\"plainlinks mw-mwoautherror-details\">صلاحيات مستخدم OAuth غير كافية: $1</span>",
+ "mwoauthserver-invalid-request-token": "رمز غير صالح في طلبك.",
+ "mwoauthserver-invalid-user": "لاستخدام التطبيقات المتصلة; في هذا الموقع; يجب أن يكون لديك حساب عبر جميع المشاريع، عندما يكون لديك حساب في جميع المشاريع، يمكنك محاولة الاتصال بـ \"$1\" مرة أخرى.\n\n<span class=\"plainlinks mw-mwoautherror-details\">مطلوب تسجيل الدخول الموحد $2</span>",
+ "mwoauthserver-consumer-no-secret": "عذرا ، حدث خطأ ما في الاتصال بهذا التطبيق.\n\n<span class=\"plainlinks mw-mwoautherror-details\">لا يوجد لدى العميل مفتاح سري: $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" هو تطبيق متصل بالمالك فقط، لجلب رمز الدخول; اطلع على [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">المستهلك مالك فقط: $3</span>",
+ "mwoauth-invalid-authorization-title": "خطأ في تفويض OAuth",
+ "mwoauth-invalid-authorization": "رؤوس التفويض في طلبك غير صالحة: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "رؤوس التفويض في طلبك غير صالحة لـ: $1",
+ "mwoauth-invalid-authorization-invalid-user": "رؤوس التفويض في طلبك مخصصة لمستخدم غير موجود هنا",
+ "mwoauth-invalid-authorization-wrong-user": "رؤوس التفويض في طلبك مخصصة لمستخدم مختلف",
+ "mwoauth-invalid-authorization-not-approved": "يبدو أن التطبيق الذي تحاول الاتصال به غير صحيح; اتصل بكاتب \"$1\" للحصول على المساعدة.",
+ "mwoauth-invalid-authorization-blocked-user": "رؤوس التراخيص في طلبك لمستخدم تم منعه",
+ "mwoauth-form-description-allwikis": "مرحبا $1،\n\nلإكمال طلبك; يحتاج '''$2''' إلى إذن لتنفيذ الإجراءات التالية نيابة عنك في جميع مشاريع هذا الموقع:\n\n$4",
+ "mwoauth-form-description-onewiki": "مرحبا $1،\n\nلإكمال طلبك; يحتاج '''$2''' إلى إذن لتنفيذ الإجراءات التالية نيابة عنك في ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "مرحبا $1،\n\nلإكمال طلبك; يحتاج '''$2''' إلى إذن للوصول إلى معلومات حول جميع مشاريع هذا الموقع نيابة عنك، لن يتم إجراء أي تغييرات على حسابك.",
+ "mwoauth-form-description-onewiki-nogrants": "مرحبا $1،\n\nلإكمال طلبك; يحتاج '''$2''' إلى إذن للوصول إلى معلومات عن ''$4'' نيابة عنك، لن يتم إجراء أي تغييرات على حسابك.",
+ "mwoauth-form-description-allwikis-privateinfo": "مرحبا $1،\n\nلإكمال طلبك; يحتاج '''$2''' إلى إذن للوصول إلى معلومات عنك، بما في ذلك اسمك الحقيقي وعنوان بريدك الإلكتروني، في جميع مشاريع هذا الموقع، لن يتم إجراء أي تغييرات على حسابك.",
+ "mwoauth-form-description-onewiki-privateinfo": "مرحبا $1،\n\nلإكمال طلبك; يحتاج '''$2''' إلى إذن للوصول إلى معلومات عنك، بما في ذلك اسمك الحقيقي وعنوان بريدك الإلكتروني، في ''$4''، في جميع مشاريع هذا الموقع، لن يتم إجراء أي تغييرات على حسابك.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "مرحبا $1،\n\nلإكمال طلبك؛ يحتاج \"$2\" إلى إذن للوصول إلى معلومات عنك، بما في ذلك عنوان بريدك الإلكتروني، في جميع مشاريع هذا الموقع، لن يتم إجراء أية تغييرات على حسابك.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "مرحبا $1،\n\nلإكمال طلبك؛ يحتاج \"$2\" إلى إذن للوصول إلى معلومات، بما في ذلك عنوان بريدك الإلكتروني، على $4، لن يتم إجراء أية تغييرات على حسابك.",
+ "mwoauth-form-button-approve": "السماح",
+ "mwoauth-form-button-cancel": "إلغاء",
+ "mwoauth-error": "خطأ في اتصال التطبيق",
+ "mwoauth-grants-heading": "الأذونات المطلوبة:",
+ "mwoauth-grants-nogrants": "لم يطلب التطبيق أية أذونات.",
+ "mwoauth-acceptance-cancelled": "لقد اخترت عدم السماح لـ\"$1\" بالوصول إلى حسابك; لن يعمل \"$1\" ما لم تسمح له بالوصول، يمكنك الرجوع إلى \"$1\" أو [[Special:OAuthManageMyGrants|إدارة]] لطبيقاتك المتصلة.",
+ "mwoauth-granttype-normal": "طلب تفويض لأذونات محددة.",
+ "grant-mwoauth-authonly": "التحقق من هوية المستخدم فقط، وعدم القدرة على قراءة الصفحات أو التصرف بالنيابة عن المستخدم.",
+ "grant-mwoauth-authonlyprivate": "التحقق من هوية المستخدم فقط مع الوصول إلى الاسم الحقيقي وعنوان البريد الإلكتروني، وعدم القدرة على قراءة الصفحات أو التصرف نيابةً عن المستخدم.",
+ "mwoauth-listgrants-extra-summary": "== منح OAuth الخاصة ==\n\nهذه المنح الإضافية قابلة للتطبيق على مستهلك OAuth.",
+ "mwoauth-oauth-exception": "حدث خطأ في بروتوكول OAuth: $1",
+ "mwoauth-callback-not-oob": "يجب تعيين oauth_callback، ويجب تعيينها على \"oob\" (حساس لحالة الأحرف)",
+ "mwoauth-callback-not-oob-or-prefix": "يجب تعيين oauth_callback، ويجب تعيينها على \"oob\" (حساس لحالة الأحرف)، أو يجب أن يكون رد الاتصال المكون بادئة لاستدعاء معاودة الاتصال.",
+ "right-mwoauthproposeconsumer": "اقتراح مستهلكي OAuth جدد",
+ "right-mwoauthupdateownconsumer": "تحديث مستهلكي OAuth الذين تتحكم بهم",
+ "right-mwoauthmanageconsumer": "التحكم بمستهلكي OAuth",
+ "right-mwoauthsuppress": "إخفاء مستهلكي OAuth",
+ "right-mwoauthviewsuppressed": "رؤية مستهلكي OAuth المخفيين",
+ "right-mwoauthviewprivate": "رؤية بيانات OAuth السرية",
+ "right-mwoauthmanagemygrants": "التحكم بمنح OAuth",
+ "action-mwoauthmanageconsumer": "التحكم بمستهلكي OAuth",
+ "action-mwoauthsuppress": "إخفاء مستهلكي OAuth",
+ "action-mwoauthmanagemygrants": "إدارة منح OAuth الخاصة بك",
+ "action-mwoauthproposeconsumer": "اقتراح مستهلكي OAuth جدد",
+ "action-mwoauthupdateownconsumer": "تحديث مستهلكي OAuth الذين تتحكم بهم",
+ "action-mwoauthviewprivate": "رؤية بيانات OAuth السرية",
+ "action-mwoauthviewsuppressed": "عرض مستهلكي OAuth المخفيين",
+ "mwoauth-tag-reserved": "يتم حفظ الوسوم التي تبدأ بـ<code>OAuth CID:</code> للاستخدام بواسطة OAuth.",
+ "mwoauth-botpasswords-note": "<strong>ملاحظة:</strong> <span class=\"plainlinks\"> [$1 أوث]</span> أكثر أمانًا من كلمات سر البوت ويجب تفضيل استعماله كلما كان البوت يدعم ذلك.",
+ "mwoauth-api-module-disabled": "الوحدة \"$1\" غير متاحة مع OAuth.",
+ "echo-category-title-oauth-owner": "تطوير OAuth",
+ "echo-pref-tooltip-oauth-owner": "اخطرني بالأحداث المتعلقة بتطبيقات OAuth التي قمت بإنشائها.",
+ "echo-category-title-oauth-admin": "إداري OAuth",
+ "echo-pref-tooltip-oauth-admin": "أبلغني بالأحداث المتعلقة بمراجعة تطبيقات OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|اقترح|اقترحت}} تطبيق OAuth جديد: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|حدث|احدثت}} تطبيق OAuth: $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|اعتمد|اعتمدت}} تطبيق OAuth الخاص {{GENDER:$3|بك}} : $2",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|رفض|رفضت}} تطبيق OAuth الخاص {{GENDER:$3|بك}} : $2",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|عطل|عطلت}} تطبيق OAuth الخاص {{GENDER:$3|بك}} : $2",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|أعاد|أعادت}} تمكين تطبيق OAuth الخاص {{GENDER:$3|بك}} : $2",
+ "notification-oauth-app-propose-subject": "{{GENDER:$1|اقترح|اقترحت}} تطبيق OAuth جديد في {{SITENAME}}",
+ "notification-oauth-app-update-subject": "{{GENDER:$1|حدث|حدثت}} تطبيق OAuth جديد في {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|اعتمد|اعتمدت}} تطبيق OAuth الخاص {{GENDER:$3|بك}} في {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|رفض|رفضت}} تطبيق OAuth الخاص {{GENDER:$3|بك}} في {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|عطل|عطلت}} تطبيق OAuth الخاص {{GENDER:$3|بك}} في {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|أعاد|أعادت}} تمكين تطبيق OAuth الخاص {{GENDER:$3|بك}} في {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "مراجعة التطبيق",
+ "notification-oauth-app-update-primary-link": "مراجعة التطبيق",
+ "notification-oauth-app-approve-primary-link": "عرض التطبيق",
+ "notification-oauth-app-reject-primary-link": "اعرض التطبيق",
+ "notification-oauth-app-disable-primary-link": "اعرض التطبيق",
+ "notification-oauth-app-reenable-primary-link": "اعرض التطبيق",
+ "notification-oauth-app-body": "السبب: $1",
+ "mwoauth-oauth-version": "نسخة بروتوكول OAuth",
+ "mwoauth-oauth2-is-confidential": "العميل سري",
+ "mwoauth-oauth2-granttypes": "أنواع منح OAuth2 المسموح بها",
+ "mwoauth-oauth2-granttype-auth-code": "كود التحقق",
+ "mwoauth-oauth2-granttype-refresh-token": "تحديث الرمز",
+ "mwoauth-oauth2-granttype-client-credentials": "شهادات العميل",
+ "mwoauth-oauth2-error-user-approval-deny": "المستخدم رفض الطلب من تطبيق العميل",
+ "mwoauth-oauth-unsupported-version": "نقطة النهاية هذه غير مسموح بها لنسخة OAuth $1",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "لم يمكن الحصول على رمز الوصول: $1",
+ "mwoauth-oauth2-error-access-denied": "مالك المورد أو خادم التحقق رفض الطلب.",
+ "mwoauth-oauth2-error-unsupported-response-type": "خادم التحقق لا يدعم الحصول على كود تحقق باستخدام هذه الطريقة.",
+ "mwoauth-oauth2-error-request-not-verified": "محاولة الحصول على خاصية متحقق منها قبل التحقق من الطلب",
+ "mwoauth-oauth2-invalid-access-token": "رمز وصول غير صحيح"
+}
diff --git a/OAuth/i18n/as.json b/OAuth/i18n/as.json
new file mode 100644
index 00000000..e9541bee
--- /dev/null
+++ b/OAuth/i18n/as.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gitartha.bordoloi",
+ "Simbu123"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "সংযুক্ত এপ্লিকেছনসমূহৰ",
+ "mwoauth-prefs-managegrantslink": "সংযুক্ত $1 {{PLURAL:$1|এপ্লিকেছনৰ|এপ্লিকেছনসমূহৰ}} পৰিচালনা কৰক",
+ "oauthmanagemygrants": "সংযোজিত এপ্লিকেছনসমূহৰ ব্যৱস্থাপনা কৰক"
+}
diff --git a/OAuth/i18n/ast.json b/OAuth/i18n/ast.json
new file mode 100644
index 00000000..d0537902
--- /dev/null
+++ b/OAuth/i18n/ast.json
@@ -0,0 +1,313 @@
+{
+ "@metadata": {
+ "authors": [
+ "Xuacu"
+ ]
+ },
+ "mwoauth-desc": "Permite usar OAuth 1.0a y OAuth 2.0 pa la autorización de la API",
+ "mwoauth-nosubpage-explanation": "OAuth ye un mecanismu que permite a les aplicaciones esternes identificar a un usuariu de {{SITENAME}} o actuar nel so nome, tres recibir permisu d'esi usuariu.\n\nPa qu'esta páxina faiga daqué, ríquense más parámetros. Si t'unvió equí una aplicación esterna, probablemente debióse a un error d'esa aplicación; tendríes de comunicate col autor.",
+ "mwoauth-verified": "Agora, esta aplicación tien permisu pa acceder a MediaWiki nel so nome.\n\nPa completar el procesu, de-y esti valor de comprobación a la aplicación: '''$1'''",
+ "mwoauth-db-readonly": "La base de datos d'OAuth ta bloquiada temporalmente. Vuelve a intentalo n'unos minutos.",
+ "mwoauth-missing-field": "Falta el valor del campu \"$1\"",
+ "mwoauth-invalid-field": "Diose un valor inválidu pal campu \"$1\"",
+ "mwoauth-invalid-field-generic": "Dióse un valor inválidu",
+ "mwoauth-field-hidden": "(esta información ta tapecida)",
+ "mwoauth-field-private": "(esta información ye privada)",
+ "mwoauth-prefs-managegrants": "Aplicaciones coneutaes:",
+ "mwoauth-prefs-managegrantslink": "Alministrar {{PLURAL:$1|$1 aplicación coneutada|$1 aplicaciones coneutaes|0=les aplicaciones coneutaes}}",
+ "mwoauth-consumer-allwikis": "Tolos proyeutos d'esti sitiu",
+ "mwoauth-consumer-key": "Clave del consumidor:",
+ "mwoauth-consumer-name": "Nome d'aplicación:",
+ "mwoauth-consumer-version": "Versión de consumidor:",
+ "mwoauth-consumer-user": "Editorial:",
+ "mwoauth-consumer-stage": "Estáu actual:",
+ "mwoauth-consumer-email": "Direición de corréu-e de contautu:",
+ "mwoauth-consumer-email-help": "Visible namái pa los qu'aprueben consumidores nuevos",
+ "mwoauth-consumer-owner-only-label": "Sólo el propietariu:",
+ "mwoauth-consumer-owner-only": "Esti consumidor ye sólo pa usar por $1.",
+ "mwoauth-consumer-owner-only-help": "Seleicionar esta opción causará que'l consumidor s'apruebe y aceute automáticamente pal usu por $1. Nun se podrá usar por otru usuariu, y nun funcionará'l fluxu normal d'autorización. Les aiciones feches usando esti consumidor nun van etiquetase.",
+ "mwoauth-consumer-description": "Descripción de l'aplicación:",
+ "mwoauth-consumer-callbackurl": "URL de \"callback\" OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Permite al consumidor especificar un retornu de llamada nes solicitúes y usar la URL de \"retornu de llamada\" de más arriba como prefixu riquíu.",
+ "mwoauth-consumer-granttypes": "Tipos de permisos que van pidise:",
+ "mwoauth-consumer-grantsneeded": "Concesiones aplicables:",
+ "mwoauth-consumer-required-grant": "Aplicable al consumidor",
+ "mwoauth-consumer-wiki": "Proyeutu aplicable:",
+ "mwoauth-consumer-wiki-thiswiki": "Proyeutu actual ($1)",
+ "mwoauth-consumer-restrictions": "Torgues d'usu:",
+ "mwoauth-consumer-restrictions-json": "Torgues d'usu (JSON):",
+ "mwoauth-consumer-rsakey": "Clave pública RSA (opcional):",
+ "mwoauth-consumer-rsakey-help": "Escribe una clave pública pa usar el métodu de robla RSA-SHA1. Dexa en blanco pa usar HMAC-SHA1 con un secretu al debalu. Si nun tas seguru, déxalo en blanco.",
+ "mwoauth-consumer-secretkey": "Pase secretu del consumidor:",
+ "mwoauth-consumer-accesstoken": "Pase d'accesu:",
+ "mwoauth-consumer-reason": "Motivu:",
+ "mwoauth-consumer-developer-agreement": "Al unviar esta aplicación, aceptes qu'acutamos el derechu a desactivar la to aplicación, a retirar o torgar l'accesu al sitiu a ti o a la to aplicación, y a realizar cualquier otru tipu d'aición que veamos apropiada si creemos, al nuesu criteriu esclusivu, que tu o la to aplicación frañen dalguna política, norma o principiu d'esti sitiu. Podemos cambiar esta política d'aplicaciones en cualquier momentu sin avisu previu, según el nuesu criteriu y como creamos necesario. Si sigues usando OAuth significa qu'aceptes esos cambios.",
+ "mwoauth-consumer-email-unconfirmed": "La direición de corréu de la to cuenta inda nun se confirmó.",
+ "mwoauth-consumer-email-mismatched": "La direición de corréu proporcionada tien de casar cola de la to cuenta.",
+ "mwoauth-consumer-alreadyexists": "Yá esiste un consumidor con esta combinación de nome/versión/editor",
+ "mwoauth-consumer-alreadyexistsversion": "Yá esiste un consumidor con esta combinación de nome/editor con una versión igual o mayor (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Nun se pue anovar la información d'una solicitú de consumidor pendiente",
+ "mwoauth-consumer-not-proposed": "El consumidor nun ta propuestu actualmente",
+ "mwoauth-consumer-not-disabled": "El consumidor nun ta desactiváu actualmente",
+ "mwoauth-consumer-not-approved": "El consumidor nun ta aprobáu (seique, desactivóse)",
+ "mwoauth-missing-consumer-key": "Nun se dio nenguna clave de consumidor.",
+ "mwoauth-invalid-consumer-key": "Nun esiste dengún consumidor cola clave dada.",
+ "mwoauth-invalid-access-token": "Nun esiste dengún pase d'accesu cola clave dada.",
+ "mwoauth-invalid-access-wrongwiki": "El consumidor solo pue usase nel proyeutu «$1».",
+ "mwoauth-consumer-conflict": "Dalguién camudó los atributos d'esti consumidor mentanto lu vía. Por favor, vuelva a intentalo. Pue comprobar el rexistru de cambios.",
+ "mwoauth-consumer-grantshelp": "Cada permisu da accesu a los permisos de usuario llistaos que yá tenga la cuenta. Mira la [[Special:ListGrants|tabla de permisos]] pa más información.",
+ "mwoauth-consumer-stage-proposed": "propuestu",
+ "mwoauth-consumer-stage-rejected": "refugáu",
+ "mwoauth-consumer-stage-expired": "caducáu",
+ "mwoauth-consumer-stage-approved": "aprobáu",
+ "mwoauth-consumer-stage-disabled": "desactiváu",
+ "mwoauth-consumer-stage-suppressed": "suprimíu",
+ "oauthconsumerregistration": "Rexistru de consumidor d'OAuth",
+ "mwoauthconsumerregistration-navigation": "Navegación:",
+ "mwoauthconsumerregistration-propose": "Proponer un consumidor nuevu",
+ "mwoauthconsumerregistration-list": "La mio llista de consumidores",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-text": "Los desendolcadores tendríen d'usar el formulariu de más abaxo pa proponer un nuevu consumidor d'OAuth (ver la [//www.mediawiki.org/wiki/Extension:OAuth documentación de la estensión] pa más detalles). Dempués d'unviar esti formulariu, recibirás un pase qu'usará la aplicación pa identificase con MediaWiki. Un alministrador d'OAuth tendrá d'aprobar la aplicación enantes de qu'otros usuarios puedan autorizala.\n\nDelles recomendaciones y comentarios:\n* Intenta usar les mínimes concesiones posibles. Evita les concesiones que nun se necesiten realmente nesti momentu.\n* Les versiones tienen la forma \"mayor.menor.versión\" (les dos últimes son opcionales) y s'incrementen según se necesiten cambios na concesión.\n* Ufre una clave pública RSA (en formatu PEM) si ye posible; d'otra manera tendrá d'usase un pase secretu (menos seguru).\n* Usa'l campu JSON de torgues pa llendar l'accesu d'esti consumidor a direiciones IP d'esos rangos CIDR.\n* Puedes usar una ID de proyeutu pa restrinxir el consumidor a un único proyeutu d'esti sitiu (usa \"*\" pa tolos proyeutos).\n* La direición de corréu dada tien de casar cola de la so cuenta (que tien de tar confirmada).",
+ "mwoauthconsumerregistration-update-text": "Utilice'l formulariu d'abaxo p'anovar aspeutos d'un consumidor d'OAuth que controle.\n\nTolos valores d'equí sobreescribirán a cualquiera anterior. Nun dexe campos baleros a menos que quiera llimpiar eses valores.",
+ "mwoauthconsumerregistration-maintext": "Esta páxina ye pa permitir que los desendolcadores propongan y anueven les aplicaciones consumidores d'OAuth del rexistru d'esti sitiu.\n\nDende equí, puedes:\n* [[Special:OAuthConsumerRegistration/propose|Solicitar un pase pa un consumidor nuevu]].\n* [[Special:OAuthConsumerRegistration/list|Alministrar los consumidores esistentes]].\n\nPa más información tocante a OAuth, ver la [//www.mediawiki.org/wiki/Extension:OAuth documentación de la estensión].",
+ "mwoauthconsumerregistration-propose-legend": "Nueva aplicación consumidora d'OAuth",
+ "mwoauthconsumerregistration-update-legend": "Anovar una aplicación consumidora d'OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Proponer un consumidor",
+ "mwoauthconsumerregistration-update-submit": "Anovar un consumidor",
+ "mwoauthconsumerregistration-none": "Nun controla nengún consumidor d'OAuth.",
+ "mwoauthconsumerregistration-name": "Consumidor",
+ "mwoauthconsumerregistration-user": "Editor",
+ "mwoauthconsumerregistration-description": "Descripción",
+ "mwoauthconsumerregistration-email": "Corréu de contactu",
+ "mwoauthconsumerregistration-consumerkey": "Clave del consumidor",
+ "mwoauthconsumerregistration-stage": "Estáu",
+ "mwoauthconsumerregistration-lastchange": "Cambéu postreru",
+ "mwoauthconsumerregistration-manage": "alministrar",
+ "mwoauthconsumerregistration-resetsecretkey": "Reaniciar la clave secreta a un valor nuevu",
+ "mwoauthconsumerregistration-proposed": "Recibióse la so solicitú de consumidor d'OAuth.\n\nDióse-y el pase de consumidor '''$1''' ya'l pase secretu '''$2'''. ''Por favor, guarde estos datos pa futures consultes.''",
+ "mwoauthconsumerregistration-created-owner-only": "Creóse'l to consumidor OAuth.\n\nLos tos pases son:\n; Pase del consumidor: $1\n; Secretu del consumidor: $2\n; Pase d'accesu: $3\n; Secretu d'accesu: $4\n<em>Guarda esta información pa futures consultes.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "Creóse'l to cliente OAuth 2.0.\n\nLos tos pases son:\n; Clave d'aplicación cliente: $1\n; Secretu d'aplicación cliente: $2\n; Pase d'accesu: $3\n;<em>Por favor, guarda estos pa futures referencies.</em>",
+ "mwoauthconsumerregistration-updated": "Anovóse'l to rexistru de consumidor d'OAuth.",
+ "mwoauthconsumerregistration-secretreset": "Dióse-y un pase secretu de consumidor '''$1'''. ''Por favor, guarde esto pa futures consultes.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Reaniciáronse los pases del to consumidor OAuth. Los nuevos pases son:\n; Pase del consumidor: $1\n; Secretu del consumidor: $2\n; Pase d'accesu: $3\n; Secretu d'accesu: $4\n<em>Guarda esta información pa futures consultes.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Reaniciáronse los tos pases de consumidor OAuth 2.0. Los nuevos pases son:\n; Pase de consumidor: $1\n; Secretu de consumidor: $2\n; Pase d'accesu: $3\n<em>Por favor guarda estos pa futures referencies.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Tienes de confirmar la direición de corréu electrónicu enantes de crear aplicaciones OAuth.\nPor favor, configura y valida la direición de corréu nes [[Special:Preferences|preferencies d'usuariu]].",
+ "oauthmanageconsumers": "Xestionar consumidores d'Oauth",
+ "mwoauthmanageconsumers-notloggedin": "Tien d'aniciar sesión pa entrar nesta páxina.",
+ "mwoauthmanageconsumers-type": "Coles:",
+ "mwoauthmanageconsumers-showproposed": "Solicitúes propuestes",
+ "mwoauthmanageconsumers-showrejected": "Solicitúes refugaes",
+ "mwoauthmanageconsumers-showexpired": "Solicitúes caducaes",
+ "mwoauthmanageconsumers-linkproposed": "solicitúes propuestes",
+ "mwoauthmanageconsumers-linkrejected": "solicitúes refugaes",
+ "mwoauthmanageconsumers-linkexpired": "solicitúes caducaes",
+ "mwoauthmanageconsumers-linkapproved": "solicitúes aprobaes",
+ "mwoauthmanageconsumers-linkdisabled": "solicitúes desactivaes",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-maintext": "Esta páxina tien l'envís de xestionar solicitúes d'aplicaciones de consumidor d'OAuth (ver http://oauth.net) y alministrar los consumidores d'OAuth establecíos.",
+ "mwoauthmanageconsumers-queues": "Seleicione una cola de confirmación de consumidor d'abaxo:",
+ "mwoauthmanageconsumers-q-proposed": "Cola de solicitúes de consumidor propuestes",
+ "mwoauthmanageconsumers-q-rejected": "Cola de solicitúes de consumidor refugaes",
+ "mwoauthmanageconsumers-q-expired": "Cola de solicitúes de consumidor caducaes",
+ "mwoauthmanageconsumers-lists": "Seleicione una llista d'estáu de consumidor d'abaxo:",
+ "mwoauthmanageconsumers-l-approved": "Llista de consumidores aprobaos actualmente",
+ "mwoauthmanageconsumers-l-disabled": "Llista de consumidores desactivaos actualmente",
+ "mwoauthmanageconsumers-none-proposed": "Nun hai consumidores propuestos nesta llista.",
+ "mwoauthmanageconsumers-none-rejected": "Nun hai consumidores propuestos nesta llista.",
+ "mwoauthmanageconsumers-none-expired": "Nun hai consumidores propuestos nesta llista.",
+ "mwoauthmanageconsumers-none-approved": "Dengún consumidor cumple estos criterios.",
+ "mwoauthmanageconsumers-none-disabled": "Dengún consumidor cumple estos criterios.",
+ "mwoauthmanageconsumers-name": "Consumidor",
+ "mwoauthmanageconsumers-user": "Editor",
+ "mwoauthmanageconsumers-description": "Descripción",
+ "mwoauthmanageconsumers-email": "Corréu de contactu",
+ "mwoauthmanageconsumers-consumerkey": "Clave del consumidor",
+ "mwoauthmanageconsumers-lastchange": "Cambéu postreru",
+ "mwoauthmanageconsumers-review": "revisar/alministrar",
+ "mwoauthmanageconsumers-confirm-text": "Use esti formulariu p'aprobar, refugar, desactivar o reactivar esti consumidor.",
+ "mwoauthmanageconsumers-confirm-legend": "Xestionar consumidor d'Oauth",
+ "mwoauthmanageconsumers-action": "Cambiar l'estáu:",
+ "mwoauthmanageconsumers-approve": "Aprobáu",
+ "mwoauthmanageconsumers-reject": "Refugáu",
+ "mwoauthmanageconsumers-rsuppress": "Refugáu y desaniciáu",
+ "mwoauthmanageconsumers-disable": "Desactiváu",
+ "mwoauthmanageconsumers-dsuppress": "Desactiváu y desaniciáu",
+ "mwoauthmanageconsumers-reenable": "Aprobáu",
+ "mwoauthmanageconsumers-reason": "Motivu:",
+ "mwoauthmanageconsumers-confirm-submit": "Anovar l'estáu del consumidor",
+ "mwoauthmanageconsumers-success-approved": "Aprobóse la solicitú.",
+ "mwoauthmanageconsumers-success-rejected": "Refugóse la solicitú.",
+ "mwoauthmanageconsumers-success-disabled": "Desactivóse'l consumidor.",
+ "mwoauthmanageconsumers-success-reanable": "Reactivóse'l consumidor.",
+ "mwoauthmanageconsumers-search-name": "consumidores con esti nome",
+ "mwoauthmanageconsumers-search-publisher": "consumidores d'esti usuariu",
+ "oauthlistconsumers": "Llista d'aplicaciones OAuth",
+ "mwoauthlistconsumers-legend": "Ver les aplicaciones OAuth",
+ "mwoauthlistconsumers-view": "detalles",
+ "mwoauthlistconsumers-none": "Nun s'alcontraron aplicaciones que cumplan estos criterios.",
+ "mwoauthlistconsumers-name": "Nome d'aplicación",
+ "mwoauthlistconsumers-version": "Versión de consumidor",
+ "mwoauthlistconsumers-user": "Editor",
+ "mwoauthlistconsumers-description": "Descripción",
+ "mwoauthlistconsumers-wiki": "Proyeutu aplicable",
+ "mwoauthlistconsumers-callbackurl": "URL de \"callback\" OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Permite al consumidor especificar un retornu de llamada nes solicitúes y usar la URL de \"retornu de llamada\" de más arriba como prefixu riquíu.",
+ "mwoauthlistconsumers-grants": "Concesiones aplicables",
+ "mwoauthlistconsumers-basicgrantsonly": "(sólo accesu basicu)",
+ "mwoauthlistconsumers-status": "Estáu",
+ "mwoauth-consumer-stage-any": "cualesquiera",
+ "mwoauthlistconsumers-status-proposed": "propuesta",
+ "mwoauthlistconsumers-status-approved": "aprobada",
+ "mwoauthlistconsumers-status-disabled": "desactivada",
+ "mwoauthlistconsumers-status-rejected": "refugada",
+ "mwoauthlistconsumers-status-expired": "caducada",
+ "mwoauthlistconsumers-navigation": "Navegación:",
+ "mwoauthlistconsumers-update-link": "Anovar consumidor",
+ "mwoauthlistconsumers-manage-link": "Alministrar consumidor",
+ "mwoauthlistconsumers-grants-link": "Alministrar permisos",
+ "mwoauthlistconsumers-rclink": "Cambeos recién fechos por esta aplicación",
+ "oauthmanagemygrants": "Alministrar les aplicaciones coneutaes",
+ "mwoauthmanagemygrants-text": "Esta páxina recueye toles aplicaciones que puen usar la so cuenta. Pa cualquier aplicación, l'ámbitu d'accesu ta llendáu polos permisos que-y concediera cuando la autorizó a actuar nel so nome. Si autorizó separadamente a una aplicación l'accesu nel so nome a otros proyeutos rellacionaos, más abaxo verá configuraciones separaes pa caún d'esos proyeutos.\n\nLes aplicaciones coneutaes anicien sesión na so cuenta usando'l protocolu OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Más información sobro aplicaciones coneutaes])</span>",
+ "mwoauthmanagemygrants-navigation": "Navegación:",
+ "mwoauthmanagemygrants-showlist": "Llista d'aplicaciones coneutaes",
+ "mwoauthmanagemygrants-none": "Nun tien aplicaciones coneutaes cola so cuenta.",
+ "mwoauthmanagemygrants-user": "Editorial:",
+ "mwoauthmanagemygrants-description": "Descripción",
+ "mwoauthmanagemygrants-wikiallowed": "Permitida nel proyeutu:",
+ "mwoauthmanagemygrants-grants": "Permisos aplicables",
+ "mwoauthmanagemygrants-grantsallowed": "Permisos concedíos",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Permisos aplicables concedíos:",
+ "mwoauthmanagemygrants-review": "alministrar l'accesu",
+ "mwoauthmanagemygrants-revoke": "torgar accesu",
+ "mwoauthmanagemygrants-grantaccept": "Permitíu",
+ "mwoauthmanagemygrants-update-text": "Use'l formulariu de más abaxo pa camudar los permisos concedíos a una aplicación p'actuar nel so nome.",
+ "mwoauthmanagemygrants-revoke-text": "Use'l formulariu de más abaxo pa torgar l'accesu a una aplicación p'actuar nel so nome.",
+ "mwoauthmanagemygrants-confirm-legend": "Alministrar aplicación coneutada",
+ "mwoauthmanagemygrants-update": "Anovar permisos",
+ "mwoauthmanagemygrants-renounce": "Desautorizar",
+ "mwoauthmanagemygrants-action": "Cambiar l'estáu:",
+ "mwoauthmanagemygrants-confirm-submit": "Anovar l'estáu del pase d'accesu",
+ "mwoauthmanagemygrants-success-update": "Anováronse les preferencies pa esta aplicación.",
+ "mwoauthmanagemygrants-success-renounce": "Desanicióse l'accesu d'esta aplicación a la to cuenta.",
+ "mwoauthmanagemygrants-basic-tooltip": "¿Por qué nun puedo actualizar esti permisu? Esti permisu da a la to aplicación conectada los permisos básicos que necesita pa funcionar correutamente. Si nun quies qu'esta aplicación conectada tenga esos permisos, tienes d'anular l'accesu de la aplicación.",
+ "mwoauthmanagemygrants-authonly-tooltip": "¿Por qué nun puedo actualizar esti permisu? Si nun quies qu'esta aplicación conectada tenga esti permisu, tienes d'anular l'accesu de la aplicación.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Les tos}} ediciones feches por esta aplicación",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Les tos}} aiciones feches por esta aplicación",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|propunxo}} un consumidor OAuth (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|anovó}} un consumidor OAuth (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|aprobó}} un consumidor OAuth pa $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|refugó}} un consumidor OAuth pa $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|desactivó}} un consumidor OAuth pa $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|reactivó}} un consumidor OAuth pa $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|creó}} un consumidor OAuth sólo pal propietariu (clave de consumidor $4)",
+ "log-action-filter-mwoauthconsumer": "Tipu d'aición de consumidor OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Aprobación de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Creación de consumidor sólo del propietariu",
+ "log-action-filter-mwoauthconsumer-disable": "Desactivación de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Propuesta de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Reactivación de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Refugu de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Anovamientu de consumidor OAuth",
+ "mwoauthconsumer-consumer-logpage": "Rexistru de consumidor OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Rexistru d'aprobaciones, refugos y desactivaciones de los consumidores OAuth rexistraos.",
+ "mwoauth-bad-request-missing-params": "Asocedió un error al configurar esta aplicación conectada. Comunícate col desendolcador de la aplicación.\n\n\n<span class=\"plainlinks mw-mwoautherror-details\">Falten los parámetros de OAuth, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Daqué salió mal, vas tener que comunicate col autor de l'aplicación pa llograr ayuda con esto.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconocida, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Daqué salió mal, vas tener que [$1 comunicate] col autor de l'aplicación pa llograr ayuda con esto.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconocida, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Nun s'alcontró nenguna concesión aprobada para esi pase d'autorización.",
+ "mwoauthdatastore-request-token-not-found": "Sentímoslo, daqué funcionó mal al conectar con esta aplicación.\nVuelve atrás y tenta coneutar nuevamente la to cuenta, o comunícate col autor de la aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nun s'alcontró el pase OAuth, $1</span>",
+ "mwoauthdatastore-callback-not-found": "La URL d'OAuth de devolución de llamada nun s'alcontró na caché. Esto probablemente ye un error na forma na que la aplicación fai solicitúes al sirvidor.",
+ "mwoauthdatastore-request-token-already-used": "Esta solicitú yá se completó y nun puede volver unviase.\nVuelve a l'aplicación y prueba conectate a la to cuenta nuevamente, o comunícate col autor.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Yá s'usó el pase OAuth, $1</span>",
+ "mwoauthdatastore-bad-token": "Nun s'alcontró dengún pase que case cola so solicitú",
+ "mwoauthdatastore-bad-source-ip": "La solicitú vien d'una dirección IP inválida.",
+ "mwoauthdatastore-bad-verifier": "El códigu de comprobación que se dio nun ye válidu",
+ "mwoauthdatastore-invalid-token-type": "O tipu de pase solicitáu ye inválidu.",
+ "mwoauthgrants-general-error": "Hebo un error na so solicitú OAuth.",
+ "mwoauthserver-bad-consumer": "«$1» nun tá aprobada como App Conectada. [$2 Comunícate] col autor de l'aplicación pa pidir ayuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">La app OAuth coneutada ta sin aprobar, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Sentímoslo pero daqué funcionó mal al conectar esta aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Clave OAuth desconocida, $1</span>",
+ "mwoauthserver-insufficient-rights": "La to cuenta nun tien permisu pa usar Apps Coneutaes, comunícate col alministrador del sitiu pa saber por qué.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nun tienes permisos OAuth d'usuariu bastantes, $1</span>",
+ "mwoauthserver-invalid-request-token": "Pase non válidu na solicitú.",
+ "mwoauthserver-invalid-user": "Pa usar Aplicaciones coneutaes nesti sitiu, tienes de tener una cuenta en tolos proyeutos. Cuando tengas una cuenta en tolos proyeutos, puedes tentar coneutar «$1» nuevamente.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Precises l'aniciu de sesión unificáu, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Sentímoslo pero daqué funcionó mal al coneutar esta aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">El consumidor nun tien una clave secreta, $1</span>",
+ "mwoauthserver-consumer-owner-only": "«$1» ye una Aplicación Conectada acutada pal propietariu. Pa buscar el pase d'accesu, visita [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">El consumidor ta acutáu pal propietariu, $3</span>",
+ "mwoauth-invalid-authorization-title": "Error d'autorización d'OAuth",
+ "mwoauth-invalid-authorization": "Les cabeceres d'autorización de la solicitú nun son válides: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Les cabeceres d'autorización de la solicitú nun son válides pa $1",
+ "mwoauth-invalid-authorization-invalid-user": "Les cabeceres d'autorización de la solicitú son pa un usuariu que nun esiste equí",
+ "mwoauth-invalid-authorization-wrong-user": "Les cabeceres d'autorización de la solicitú son pa un usuariu distintu",
+ "mwoauth-invalid-authorization-not-approved": "L'aplicación a la que tas intentando coneutar paez que ta configurada de forma incorreuta. Comunícate col autor de «$1» pa llograr ayuda.",
+ "mwoauth-invalid-authorization-blocked-user": "Les cabeceres d'autorización de la solicitú son pa un usuariu que ta bloquiáu",
+ "mwoauth-form-description-allwikis": "Bones, $1:\n\nPa completar la to solicitú, '''$2''' precisa permisu pa realizar les aiciones siguientes nel to nome en tolos proyeutos d'esti sitiu:\n\n$4",
+ "mwoauth-form-description-onewiki": "Bones, $1:\n\nPa completar la to solicitú, '''$2''' precisa permisu pa realizar les aiciones siguientes nel to nome en ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Bones, $1:\n\nPa completar la to solicitú, '''$2''' precisa permisu d'accesu nel to nome a información de tolos proyectos d'esti sitiu. Nun van facese cambios cola to cuenta.",
+ "mwoauth-form-description-onewiki-nogrants": "Bones, $1:\n\nPa completar la to solicitú, '''$2''' precisa permisu d'accesu nel to nome a información de ''$4''. Nun van facese cambios cola to cuenta.",
+ "mwoauth-form-description-allwikis-privateinfo": "Bones, $1:\n\nPa completar la to solicitú, '''$2''' precisa permisu d'accesu a la to información, incluyendo'l nome real y direición de corréu, en tolos proyeutos d'esti sitiu. Nun van facese cambios cola to cuenta.",
+ "mwoauth-form-description-onewiki-privateinfo": "Bones, $1:\n\nPa completar la solicitú, '''$2''' precisa permisu d'accesu a la to información, incluyendo'l nome real y la direición de corréu electrónicu, en ''$4''. Nun va facese nengún cambiu cola to cuenta.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Bones $1,\n\nPa completar la to solicitú, '''$2''' precisa permisu d'accesu a la to información, incluyendo la direición de corréu, en tolos proyeutos d'esti sitiu. Nun van facese cambios cola to cuenta.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Bones $1,\n\nPa completar la to solicitú, '''$2''' precisa permisu d'accesu a la to información, incluyendo la direición de corréu, en ''$4''. Nun van facese cambios cola to cuenta.",
+ "mwoauth-form-button-approve": "Permitir",
+ "mwoauth-form-button-cancel": "Encaboxar",
+ "mwoauth-error": "Error de conexón de l'aplicación",
+ "mwoauth-grants-heading": "Permisos solicitaos:",
+ "mwoauth-grants-nogrants": "L'aplicación nun solicitó nengún permisu.",
+ "mwoauth-acceptance-cancelled": "Escoyisti nun autorizar l'accesu de «$1» a la to cuenta. «$1» nun funcionará mentanto nun-y permitas l'accesu. Puedes volver a «$1» o [[Special:OAuthManageMyGrants|alministrar]] les aplicaciones coneutaes.",
+ "mwoauth-granttype-normal": "Solicitar autorización pa permisos específicos.",
+ "grant-mwoauth-authonly": "Solamente pa comprobar la identidá del usuariu, ensin capacidá pa lleer páxines nin p'actuar nel nome d'un usuariu.",
+ "grant-mwoauth-authonlyprivate": "Sólo pa comprobar la identidá del usuariu, con accesu al nome real y a la direición de corréu electrónicu, ensin capacidá pa lleer páxines nin p'actuar nel nome d'un usuariu.",
+ "mwoauth-listgrants-extra-summary": "== Permisos específicos pa OAuth ==\n\nEstes autorizaciones estra son aplicables a los consumidores OAuth.",
+ "mwoauth-oauth-exception": "Hebo un error nel protocolu OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback tien de tar configuráu y tener el valor «oob» (estrema mayúscules y minúscules)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback tien de tar configuráu y tener el valor «oob» (estrema mayúscules y minúscules), o la devolución de llamada configurada tien de ser un prefixu de la devolución de llamada proporcionada.",
+ "right-mwoauthproposeconsumer": "Proponer consumidores OAuth nuevos",
+ "right-mwoauthupdateownconsumer": "Actualizar consumidores OAuth que controles",
+ "right-mwoauthmanageconsumer": "Xestionar consumidores d'Oauth",
+ "right-mwoauthsuppress": "Desaniciar consumidores OAuth",
+ "right-mwoauthviewsuppressed": "Ver los consumidores d'OAuth desaniciaos",
+ "right-mwoauthviewprivate": "Ver datos OAuth privaos",
+ "right-mwoauthmanagemygrants": "Alministrar les autorizaciones d'OAuth",
+ "action-mwoauthmanageconsumer": "alministrar os consumidores d'OAuth",
+ "action-mwoauthsuppress": "desaniciar consumidores OAuth",
+ "action-mwoauthmanagemygrants": "alministrar les tos autorizaciones d'OAuth",
+ "action-mwoauthproposeconsumer": "proponer nuevos consumidores d'OAuth",
+ "action-mwoauthupdateownconsumer": "actualizar los consumidores d'OAuth que controles",
+ "action-mwoauthviewprivate": "ver datos OAuth privaos",
+ "action-mwoauthviewsuppressed": "ver los consumidores d'OAuth desaniciaos",
+ "mwoauth-tag-reserved": "Les etiquetes que principien por <code>OAuth CID:</code> tán acutaes pa usar con OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Nota:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> ye más seguru que les contraseñes de bots, y tendría de ser la opción prioritaria si'l bot lo permite.",
+ "mwoauth-api-module-disabled": "El módulu «$1» nun ta disponible con OAuth.",
+ "echo-category-title-oauth-owner": "Desendolcu d'OAuth",
+ "echo-pref-tooltip-oauth-owner": "Avisame sobro actividaes rellacionaes con aplicaciones OAuth creaes por min.",
+ "echo-category-title-oauth-admin": "Alministrador d'OAuth",
+ "echo-pref-tooltip-oauth-admin": "Avisame sobro actividaes rellacionaes cola revisión d'aplicaciones OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|propunxo}} una aplicación OAuth nueva: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|anovó}} l'aplicación OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|aprobó}} {{GENDER:$3|la to}} aplicación OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|refugó}} {{GENDER:$3|la to}} aplicación OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|desactivó}} {{GENDER:$3|la to}} aplicación OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reactivó}} {{GENDER:$3|la to}} aplicación OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|propunxo}} una aplicación OAuth nueva en {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|anovó}} una aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|aprobó}} {{GENDER:$3|la to}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|refugó}} {{GENDER:$3|la to}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|desactivó}} {{GENDER:$3|la to}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reactivó}} {{GENDER:$3|la to}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Revisar app",
+ "notification-oauth-app-update-primary-link": "Revisar app",
+ "notification-oauth-app-approve-primary-link": "Ver app",
+ "notification-oauth-app-reject-primary-link": "Ver app",
+ "notification-oauth-app-disable-primary-link": "Ver app",
+ "notification-oauth-app-reenable-primary-link": "Ver app",
+ "notification-oauth-app-body": "Motivu: $1",
+ "mwoauth-oauth-version": "Versión de protocolu OAuth",
+ "mwoauth-oauth2-is-confidential": "El cliente ye secretu",
+ "mwoauth-oauth2-is-confidential-help": "Un cliente secretu ye una aplicación que ye capaz de caltener la contraseña del cliente secreta pal mund. Los clientes non-secretos son menos seguros",
+ "mwoauth-oauth2-granttypes": "Tipos de permisu que permite OAuth2",
+ "mwoauth-oauth2-granttype-auth-code": "Códigu d'autorización",
+ "mwoauth-oauth2-granttype-refresh-token": "Enfrescar pase",
+ "mwoauth-oauth2-granttype-client-credentials": "Credenciales del cliente",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Nun pudo crease'l pase d'accesu, l'usuariu nun aprobó emitir esti pase d'accesu",
+ "mwoauth-oauth2-error-user-approval-deny": "L'usuariu refugó la solicitú dende la aplicación cliente",
+ "mwoauth-oauth-unsupported-version": "Esti puntu final nun se permite pa OAuth versión $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "L'ámbitu «$1» nun se permite pa esta aplicación",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Los clientes sólo del propietariu han tener permisu pa usar client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Nun pudo recuperase'l pase d'accesu: $1"
+}
diff --git a/OAuth/i18n/atj.json b/OAuth/i18n/atj.json
new file mode 100644
index 00000000..6ca11fe9
--- /dev/null
+++ b/OAuth/i18n/atj.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Benoit Rochon"
+ ]
+ },
+ "mwoauth-consumer-stage-disabled": "manisinaha",
+ "mwoauthconsumerregistration-description": "E witcikemakak",
+ "mwoauthmanageconsumers-description": "E witcikemakak",
+ "mwoauthmanageconsumers-disable": "Manisinaha",
+ "mwoauthlistconsumers-description": "E witcikemakak",
+ "mwoauthlistconsumers-status-disabled": "manisinaha",
+ "mwoauthmanagemygrants-description": "E witcikemakak"
+}
diff --git a/OAuth/i18n/awa.json b/OAuth/i18n/awa.json
new file mode 100644
index 00000000..a8391cd4
--- /dev/null
+++ b/OAuth/i18n/awa.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "1AnuraagPandey"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "जोडल ऐप"
+}
diff --git a/OAuth/i18n/az.json b/OAuth/i18n/az.json
new file mode 100644
index 00000000..5c1dd441
--- /dev/null
+++ b/OAuth/i18n/az.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "Serkanland",
+ "Wertuose"
+ ]
+ },
+ "mwoauth-missing-field": "\"$1\" üçün heç bir göstərici qeyd edilməyib",
+ "mwoauth-invalid-field": "\"$1\" üçün yanlış göstərici daxil etmisiniz",
+ "mwoauth-invalid-field-generic": "Yanlış göstərici daxil etmisiniz",
+ "mwoauth-field-hidden": "(bu məlumat gizlədilib)",
+ "mwoauth-field-private": "(bu məlumat konfidensialdır)",
+ "mwoauth-prefs-managegrants": "Əlaqələndirilmiş tətbiqlər:",
+ "mwoauth-prefs-managegrantslink": "$1 qoşulu tətbiqi idarə et",
+ "mwoauth-consumer-stage-disabled": "söndürülüb",
+ "mwoauthconsumerregistration-stage": "Vəziyyəti",
+ "mwoauthconsumerregistration-lastchange": "Son redaktə",
+ "mwoauthconsumerregistration-manage": "idarəetmə",
+ "mwoauthconsumerregistration-resetsecretkey": "Gizli açarı sıfırlayıb, yenisini təyin et",
+ "mwoauthmanageconsumers-disable": "Söndürülüb",
+ "mwoauthlistconsumers-status-disabled": "söndürülüb",
+ "oauthmanagemygrants": "Qoşulmuş tətbiqləri idarə et",
+ "mwoauthmanagemygrants-confirm-legend": "Qoşulmuş tətbiqin idarə edilməsi"
+}
diff --git a/OAuth/i18n/azb.json b/OAuth/i18n/azb.json
new file mode 100644
index 00000000..aba6a3cb
--- /dev/null
+++ b/OAuth/i18n/azb.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alp Er Tunqa",
+ "Ebrahimi-amir",
+ "Ilğım",
+ "Koroğlu"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "باغلی پروقرملر:",
+ "mwoauth-prefs-managegrantslink": "$1 باغلی {{PLURAL:$1|اۇیغولامانی}} ایداره ائت"
+}
diff --git a/OAuth/i18n/bcc.json b/OAuth/i18n/bcc.json
new file mode 100644
index 00000000..524ce01b
--- /dev/null
+++ b/OAuth/i18n/bcc.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Baloch Afghanistan"
+ ]
+ },
+ "mwoauthdatastore-bad-source-ip": "ریکوسٹ شه یک نامعترین IP ادره سی آته."
+}
diff --git a/OAuth/i18n/bcl.json b/OAuth/i18n/bcl.json
new file mode 100644
index 00000000..b97b6ed5
--- /dev/null
+++ b/OAuth/i18n/bcl.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Geopoet"
+ ]
+ },
+ "mwoauth-grant-editmycssjs": "Liwaton an saimong paragamit CSS/JavaScript",
+ "mwoauth-grant-editmyoptions": "Liwaton an saimong paragamit na mga kamuyahan",
+ "mwoauth-grant-viewdeleted": "Tanawon an pinagpurang mga sagunson asin pahina"
+}
diff --git a/OAuth/i18n/be-tarask.json b/OAuth/i18n/be-tarask.json
new file mode 100644
index 00000000..8ba45755
--- /dev/null
+++ b/OAuth/i18n/be-tarask.json
@@ -0,0 +1,30 @@
+{
+ "@metadata": {
+ "authors": [
+ "Red Winged Duck",
+ "Renessaince"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Падключаныя дадаткі:",
+ "mwoauth-prefs-managegrantslink": "Кіраваць $1 {{PLURAL:$1|падключаным дадаткам|падключанымі дадаткамі}}",
+ "mwoauth-consumer-allwikis": "Усе праекты на гэтым сайце",
+ "mwoauth-consumer-name": "Назва праграмы:",
+ "mwoauth-consumer-stage": "Актуальны статус:",
+ "oauthlistconsumers": "Сьпіс праграмаў OAuth",
+ "mwoauthlistconsumers-legend": "Прагляд OAuth-праграмаў",
+ "oauthmanagemygrants": "Кіраваньне падключанымі праграмамі",
+ "mwoauthmanagemygrants-navigation": "Навігацыя:",
+ "mwoauthmanagemygrants-showlist": "Сьпіс падключаных праграмаў",
+ "mwoauthmanagemygrants-none": "Няма праграмаў, падключаных да вашага рахунку.",
+ "mwoauthmanagemygrants-wikiallowed": "Дазволена ў праекце:",
+ "mwoauthmanagemygrants-review": "кіраваньне доступам",
+ "mwoauthmanagemygrants-revoke": "скасаваць доступ",
+ "mwoauthmanagemygrants-revoke-text": "Дзеля скасаваньня доступу праграме на дзеяньні ад вашага імя скарыстайцеся формай ніжэй.",
+ "mwoauthmanagemygrants-success-renounce": "Доступ праграмы да вашага ўліковага запісу быў адкліканы.",
+ "mwoauthmanagemygrants-basic-tooltip": "Чаму я не магу абнавіць гэты дазвол? Гэты дазвол дае вашым злучаным праграмам асноўныя правы, неабходныя дзеля належнай работы. Калі вы ня хочаце, каб злучаныя праграмы мелі гэткія правы, вы мусіце адклікаць ейны доступ.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Чаму я не магу абнавіць гэты дазвол? Калі вы ня хочаце, каб злучаныя праграмы мелі гэткія правы, вы мусіце адклікаць ейны доступ.",
+ "right-mwoauthproposeconsumer": "прапаноўваць новых спажыўцоў OAuth",
+ "right-mwoauthupdateownconsumer": "абнаўленьне кантраляваных вамі OAuth-дадаткаў",
+ "right-mwoauthmanagemygrants": "кіраваньне OAuth-дазволамі",
+ "action-mwoauthproposeconsumer": "прапанову новых спажыўцоў OAuth"
+}
diff --git a/OAuth/i18n/be.json b/OAuth/i18n/be.json
new file mode 100644
index 00000000..f227094e
--- /dev/null
+++ b/OAuth/i18n/be.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Artsiom91"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Падключаныя дадаткі:",
+ "mwoauth-prefs-managegrantslink": "Кіраванне {{PLURAL:$1|$1 падключаным дадаткам|$1 падключанымі дадаткамі|0=падключанымі дадаткамі}}",
+ "oauthlistconsumers": "Спіс OAuth-дадаткаў",
+ "oauthmanagemygrants": "Кіраванне падключанымі дадаткамі",
+ "mwoauthmanagemygrants-confirm-legend": "Кіраванне падключаным дадаткам",
+ "echo-category-title-oauth-owner": "Распрацоўка OAuth",
+ "echo-pref-tooltip-oauth-owner": "Паведамляць мне пра падзеі, звязаныя са створанымі мной дадаткамі OAuth."
+}
diff --git a/OAuth/i18n/bg.json b/OAuth/i18n/bg.json
new file mode 100644
index 00000000..ef267296
--- /dev/null
+++ b/OAuth/i18n/bg.json
@@ -0,0 +1,158 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dicto23456",
+ "ShockD",
+ "Spiritia",
+ "StanProg",
+ "Ted Masters",
+ "Termininja",
+ "Vlad5250"
+ ]
+ },
+ "mwoauth-desc": "Позволява употреба на OAuth 1.0а и OAuth 2.0 за удостоверяване за API",
+ "mwoauth-verified": "Приложението има права за осъществяване на достъп до МедияУики от Ваше име.\n\nЗа да завършите процеса, предоставете тази потвърждаваща стойност на приложението: '''$1'''",
+ "mwoauth-prefs-managegrants": "Свързани приложения:",
+ "mwoauth-prefs-managegrantslink": "Управление на {{PLURAL:$1|$1 свързано приложение|$1 свързани приложения|0=свързани приложения}}",
+ "mwoauth-consumer-allwikis": "Всички проекти на този сайт",
+ "mwoauth-consumer-key": "Клиентски ключ:",
+ "mwoauth-consumer-name": "Име на приложението:",
+ "mwoauth-consumer-version": "Версия на клиента:",
+ "mwoauth-consumer-user": "Издател:",
+ "mwoauth-consumer-stage": "Настоящ статут:",
+ "mwoauth-consumer-description": "Описание на приложението:",
+ "mwoauth-consumer-grantsneeded": "Приложими разрешения:",
+ "mwoauth-consumer-required-grant": "Приложимо към клиента",
+ "mwoauth-consumer-wiki": "Приложим проект:",
+ "mwoauth-consumer-wiki-thiswiki": "Текущ проект ($1)",
+ "mwoauth-consumer-restrictions": "Ограничения на употребата:",
+ "mwoauth-consumer-restrictions-json": "Ограничения на употребата (JSON):",
+ "mwoauth-consumer-rsakey": "Публичен RSA-ключ (незадължителен):",
+ "mwoauth-consumer-reason": "Причина:",
+ "mwoauth-consumer-stage-proposed": "предложено",
+ "mwoauth-consumer-stage-rejected": "отхвърлено",
+ "mwoauth-consumer-stage-expired": "изтекло",
+ "mwoauth-consumer-stage-approved": "одобрено",
+ "mwoauth-consumer-stage-disabled": "изключено",
+ "mwoauth-consumer-stage-suppressed": "потиснато",
+ "oauthconsumerregistration": "Регистрация на клиент на OAuth",
+ "mwoauthconsumerregistration-navigation": "Навигация:",
+ "mwoauthconsumerregistration-propose": "Предложи нов клиент",
+ "mwoauthconsumerregistration-list": "Списък на мои клиенти",
+ "mwoauthconsumerregistration-main": "Главна",
+ "mwoauthconsumerregistration-propose-legend": "Ново клиентско приложение за OAuth",
+ "mwoauthconsumerregistration-update-legend": "Обновяване на клиентско приложение за OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Предложи клиент",
+ "mwoauthconsumerregistration-update-submit": "Обновяване на клиента",
+ "mwoauthconsumerregistration-name": "Клиент",
+ "mwoauthconsumerregistration-user": "Издател",
+ "mwoauthconsumerregistration-description": "Описание",
+ "mwoauthconsumerregistration-consumerkey": "Ключ на клиента",
+ "mwoauthconsumerregistration-stage": "Статус",
+ "mwoauthconsumerregistration-lastchange": "Последна промяна",
+ "mwoauthconsumerregistration-manage": "управление",
+ "mwoauthmanageconsumers-showproposed": "Предложени заявки",
+ "mwoauthmanageconsumers-showrejected": "Отказани заявки",
+ "mwoauthmanageconsumers-showexpired": "Изтекли заявки",
+ "mwoauthmanageconsumers-linkproposed": "предложени заявки",
+ "mwoauthmanageconsumers-linkrejected": "отказани заявки",
+ "mwoauthmanageconsumers-linkexpired": "изтекли заявки",
+ "mwoauthmanageconsumers-linkapproved": "одобрени заявки",
+ "mwoauthmanageconsumers-linkdisabled": "деактивирани заявки",
+ "mwoauthmanageconsumers-main": "Главна",
+ "mwoauthmanageconsumers-none-approved": "Няма клиенти, отговарящи на дадените условия.",
+ "mwoauthmanageconsumers-none-disabled": "Няма клиенти, отговарящи на дадените условия.",
+ "mwoauthmanageconsumers-name": "Клиент",
+ "mwoauthmanageconsumers-user": "Издател",
+ "mwoauthmanageconsumers-description": "Описание",
+ "mwoauthmanageconsumers-consumerkey": "Ключ на клиента",
+ "mwoauthmanageconsumers-lastchange": "Последна промяна",
+ "mwoauthmanageconsumers-review": "проверка/управление",
+ "mwoauthmanageconsumers-confirm-text": "Формулярът служи за одобряване, отхвърляне, деактивиране или реактивиране на клиента.",
+ "mwoauthmanageconsumers-confirm-legend": "Управление на клиент на OAuth",
+ "mwoauthmanageconsumers-action": "Статус на промяната:",
+ "mwoauthmanageconsumers-approve": "Одобрено",
+ "mwoauthmanageconsumers-reject": "Отхвърлено",
+ "mwoauthmanageconsumers-rsuppress": "Отхвърлено и потиснато",
+ "mwoauthmanageconsumers-disable": "Изключено",
+ "mwoauthmanageconsumers-dsuppress": "Изключено и потиснато",
+ "mwoauthmanageconsumers-reenable": "Одобрено",
+ "mwoauthmanageconsumers-reason": "Причина:",
+ "mwoauthmanageconsumers-success-approved": "Заявката е одобрена.",
+ "mwoauthmanageconsumers-success-rejected": "Заявката е отхвърлена.",
+ "mwoauthmanageconsumers-success-disabled": "Клиентът е деактивиран.",
+ "mwoauthmanageconsumers-success-reanable": "Клиентът е активиран повторно.",
+ "oauthlistconsumers": "Списък на свързаните приложения",
+ "mwoauthlistconsumers-legend": "Преглед на приложенията, ползващи OAuth",
+ "mwoauthlistconsumers-view": "детайли",
+ "mwoauthlistconsumers-none": "Няма приложения, които отговарят на дадените условия.",
+ "mwoauthlistconsumers-name": "Име на приложението",
+ "mwoauthlistconsumers-version": "Версия на клиента",
+ "mwoauthlistconsumers-user": "Издател",
+ "mwoauthlistconsumers-description": "Описание",
+ "mwoauthlistconsumers-wiki": "Приложим проект",
+ "mwoauthlistconsumers-grants": "Приложими разрешения",
+ "mwoauthlistconsumers-basicgrantsonly": "(само основен достъп)",
+ "mwoauthlistconsumers-status": "Статус",
+ "mwoauth-consumer-stage-any": "всяко",
+ "mwoauthlistconsumers-status-proposed": "предложено",
+ "mwoauthlistconsumers-status-approved": "одобрено",
+ "mwoauthlistconsumers-status-disabled": "изключено",
+ "mwoauthlistconsumers-status-rejected": "отхвърлено",
+ "mwoauthlistconsumers-status-expired": "изтекло",
+ "oauthmanagemygrants": "Управление на свързани приложения",
+ "mwoauthmanagemygrants-text": "Тази страница показва всички приложения, които имат достъп до Вашата сметка. За всяко приложение, обхватът на достъпа му е ограничен от разрешенията, които сте му дали, когато сте го упълномощили да действа от Ваше име. Ако сте упълномощили отделно приложение с достъп до различни сродни проекти, тогава ще видите по-долу различна конфигурация за всеки такъв проект.\n\nСвързаните приложения имат достъп до сметката Ви чрез протокола OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Научете повече за свързаните приложения])</span>",
+ "mwoauthmanagemygrants-navigation": "Навигация:",
+ "mwoauthmanagemygrants-showlist": "Списък със свързани приложения",
+ "mwoauthmanagemygrants-none": "Към сметката Ви няма свързани приложения.",
+ "mwoauthmanagemygrants-user": "Издател:",
+ "mwoauthmanagemygrants-description": "Описание",
+ "mwoauthmanagemygrants-wikiallowed": "Позволено на проект:",
+ "mwoauthmanagemygrants-grants": "Приложими разрешения",
+ "mwoauthmanagemygrants-grantsallowed": "Позволени разрешения",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Приложими разрешения:",
+ "mwoauthmanagemygrants-review": "управление на достъпа",
+ "mwoauthmanagemygrants-revoke": "отмяна на достъпа",
+ "mwoauthmanagemygrants-grantaccept": "Разрешено",
+ "mwoauthmanagemygrants-update-text": "Използвайте формата по-долу, за да промените дадените права на приложение, което действа от Ваше име.",
+ "mwoauthmanagemygrants-revoke-text": "Използвайте формата по-долу, за да отмените достъпа на приложение, което действа от Ваше име.",
+ "mwoauthmanagemygrants-confirm-legend": "Управление на свързаното приложение",
+ "mwoauthmanagemygrants-update": "Актуализация на разрешенията",
+ "mwoauthmanagemygrants-renounce": "Отмяна на упълномощаването",
+ "mwoauthmanagemygrants-action": "Промяна на статус:",
+ "mwoauthmanagemygrants-success-update": "Вашите настройки за това приложение са обновени.",
+ "mwoauthmanagemygrants-success-renounce": "Достъпът на приложението до Вашата сметка бе отменен.",
+ "mwoauthmanagemygrants-basic-tooltip": "Защо не мога да обновя това разрешение? Това разрешение дава права на Вашите основни свързани приложения, които имат нужда от него, за да функционират както трябва. Ако не искате това свързано приложение да има тези права, тогава отменете достъпа на приложението.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Вашите}} редакции с това приложение",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Вашите}} действия с това приложение",
+ "log-action-filter-mwoauthconsumer": "Вид клиентско действие с OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Клиентско одобряване с OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Създаване на клиенти с OAuth само за собственика",
+ "log-action-filter-mwoauthconsumer-disable": "Клиентско деактивиране с OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Клиентско предложение с OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Клиентско реактивиране на OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Клиентско отхвърляне с OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Клиентско обновяване на OAuth",
+ "mwoauthconsumer-consumer-logpage": "Клиентски дневник за OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Дневник на одобрявания, отхвърляния и изключвания на регистрирани клиенти на OAuth.",
+ "mwoauth-form-description-allwikis": "Здравейте, $1,\n\nЗа да бъде изпълнена вашата заявка, '''$2''' се нуждае от разрешение да изпълни следните дейности от ваше име във всички проекти на този сайт: \n\n$4",
+ "mwoauth-form-description-onewiki": "Здравейте, $1,\n\nЗа да бъде изпълнена вашата заявка, '''$2''' иска разрешение да изпълни следните дейности от ваше име в ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Здравейте, $1,\n\nЗа да бъде изпълнена вашата заявка, '''$2''' се нуждае от разрешение да получи от ваше име достъп до всички проекти в този сайт. Няма да бъдат правени промени през потребителската ви сметка.",
+ "mwoauth-form-description-onewiki-nogrants": "Здравейте, $1,\n\nЗа да бъде изпълнена вашата заявка, '''$2''' се нуждае от разрешение да получи от ваше име достъп до информация на „$4“. Няма да бъдат правени промени през потребителската ви сметка.",
+ "mwoauth-form-description-allwikis-privateinfo": "Здравейте, $1,\n\nЗа да бъде изпълнена вашата заявка, '''$2''' се нуждае от разрешение да получи информация за вас, включително истинското ви име и имейл адрес, във всички проекти в този сайт. Няма да бъдат правени промени през потребителската ви сметка.",
+ "mwoauth-form-description-onewiki-privateinfo": "Здравейте, $1,\n\nЗа да бъде изпълнена вашата заявка, '''$2''' се нуждае от разрешение да получи достъп до информация в ''$4'', включваща истинското ви име и имейл адрес. Няма да бъдат правени промени през потребителската ви сметка.",
+ "mwoauth-form-button-approve": "Разрешаване",
+ "mwoauth-form-button-cancel": "Отказ",
+ "right-mwoauthproposeconsumer": "Предлагане на нови клиенти на OAuth",
+ "right-mwoauthupdateownconsumer": "Промяна на клиенти на OAuth",
+ "right-mwoauthmanageconsumer": "Управление на клиенти на OAuth",
+ "right-mwoauthsuppress": "Потискане на клиенти на OAuth",
+ "right-mwoauthviewsuppressed": "Преглед на потиснати клиенти на OAuth",
+ "right-mwoauthviewprivate": "Преглед на личните данни за OAuth",
+ "right-mwoauthmanagemygrants": "Управление на разрешения за OAuth",
+ "echo-category-title-oauth-owner": "Разработка на OAuth",
+ "echo-pref-tooltip-oauth-owner": "Известяване, когато има ново събитие свързано приложенията използващи OAuth, които съм разработил.",
+ "echo-category-title-oauth-admin": "Администратор на OAuth",
+ "notification-oauth-app-body": "Причина: $1",
+ "mwoauth-oauth-version": "Версия на OAuth протокола"
+}
diff --git a/OAuth/i18n/bn.json b/OAuth/i18n/bn.json
new file mode 100644
index 00000000..f57e6eec
--- /dev/null
+++ b/OAuth/i18n/bn.json
@@ -0,0 +1,125 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aftab1995",
+ "Aftabuzzaman",
+ "Elias Ahmmad",
+ "Gitartha.bordoloi",
+ "Nokib Sarkar",
+ "Tauhid16",
+ "আফতাবুজ্জামান"
+ ]
+ },
+ "mwoauth-desc": "API অনুমোদনের জন্য OAuth 1.0a ও OAuth 2.0 ব্যবহার করার অনুমতি দেয়",
+ "mwoauth-invalid-field-generic": "অবৈধ মান দেয়া হয়েছে",
+ "mwoauth-field-hidden": "(এই তথ্য গোপন করা)",
+ "mwoauth-field-private": "(এই তথ্য ব্যক্তিগত)",
+ "mwoauth-prefs-managegrants": "সংযুক্ত অ্যাপগুলি:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|সংযুক্ত $1টি অ্যাপ্লিকেশন|0=সংযুক্ত অ্যাপ্লিকেশন}} পরিচালনা করুন",
+ "mwoauth-consumer-allwikis": "এই সাইটের সব প্রকল্প",
+ "mwoauth-consumer-key": "ভোক্তার চাবি:",
+ "mwoauth-consumer-name": "অ্যাপ্লিকেশনের নাম:",
+ "mwoauth-consumer-version": "ভোক্তার সংস্করণ:",
+ "mwoauth-consumer-user": "প্রকাশক:",
+ "mwoauth-consumer-stage": "বর্তমান অবস্থা:",
+ "mwoauth-consumer-email": "যোগাযোগের ইমেল ঠিকানা:",
+ "mwoauth-consumer-email-help": "শুধু তাদের কাছেই দৃশ্যমান যারা নতুন ভোক্তাদের অনুমোদন করছে",
+ "mwoauth-consumer-owner-only-label": "শুধুমাত্র মালিক:",
+ "mwoauth-consumer-description": "অ্যাপ্লিকেশনের বিবরণ:",
+ "mwoauth-consumer-callbackurl": "\"কলব্যাক\" ওআউথের URL:",
+ "mwoauth-consumer-callbackisprefix": "অনুরোধে ভোক্তাকে একটি কলব্যাক নির্দিষ্ট করার অনুমতি দেয় এবং একটি প্রয়োজনীয় উপসর্গ হিসাবে উপরের \"কলব্যাক\" URL ব্যবহার করে।",
+ "mwoauth-consumer-grantsneeded": "প্রয়োগযোগ্য মঞ্জুরি:",
+ "mwoauth-consumer-wiki-thiswiki": "বর্তমান প্রকল্প ($1)",
+ "mwoauth-consumer-restrictions": "ব্যবহারের সীমাবদ্ধতা:",
+ "mwoauth-consumer-restrictions-json": "ব্যবহারের সীমাবদ্ধতা (JSON):",
+ "mwoauth-consumer-rsakey": "পাবলিক RSA চাবি (ঐচ্ছিক):",
+ "mwoauth-consumer-rsakey-help": "RSA-SHA1ভস্বাক্ষর পদ্ধতি ব্যবহার করার জন্য একটি পাবলিক চাবি লিখুন। এলোমেলো গোপনসহ HMAC-SHA ব্যবহার করার জন্য খালি রাখুন। আপনি যদি নিশ্চিত না হন কোনটি ব্যবহার করবেন, তাহলে এটি ফাঁকা রাখুন।",
+ "mwoauth-consumer-reason": "কারণ:",
+ "mwoauth-consumer-email-unconfirmed": "আপনার অ্যাকাউন্টের ইমেল ঠিকানা এখনো নিশ্চিত করা হয় নি।",
+ "mwoauth-consumer-stage-proposed": "প্রস্তাবিত",
+ "mwoauth-consumer-stage-rejected": "প্রত্যাখ্যাত",
+ "mwoauth-consumer-stage-expired": "মেয়াদোত্তীর্ণ",
+ "mwoauth-consumer-stage-approved": "অনুমোদিত",
+ "mwoauth-consumer-stage-disabled": "নিস্ক্রিয়",
+ "mwoauth-consumer-stage-suppressed": "নিরুদ্ধ",
+ "mwoauthconsumerregistration-navigation": "পরিভ্রমণ:",
+ "mwoauthconsumerregistration-main": "প্রধান",
+ "mwoauthconsumerregistration-name": "ভোক্তা",
+ "mwoauthconsumerregistration-user": "প্রকাশক",
+ "mwoauthconsumerregistration-description": "বিবরণ",
+ "mwoauthconsumerregistration-email": "যোগাযোগের ইমেইল",
+ "mwoauthconsumerregistration-consumerkey": "ভোক্তার চাবি",
+ "mwoauthconsumerregistration-stage": "অবস্থা",
+ "mwoauthconsumerregistration-lastchange": "সর্বশেষ পরিবর্তন",
+ "mwoauthconsumerregistration-manage": "পরিচালনা করুন",
+ "mwoauthconsumerregistration-resetsecretkey": "একটি নতুন মানে গোপন চাবিটি পুনঃস্থাপন করুন",
+ "mwoauthconsumerregistration-need-emailconfirmed": "আপনাকে OAuth অ্যাপ্লিকেশন তৈরি করার আগে আপনার ই-মেইল ঠিকানা অবশ্যই নিশ্চিত করতে হবে। দয়া করে [[Special:Preferences|ব্যবহারকারীর পছন্দের]] মাধ্যমে আপনার ই-মেইল ঠিকানাটি বসান এবং যাচাই করুন।",
+ "mwoauthmanageconsumers-type": "সারি:",
+ "mwoauthmanageconsumers-main": "প্রধান",
+ "mwoauthmanageconsumers-name": "ভোক্তা",
+ "mwoauthmanageconsumers-user": "প্রকাশক",
+ "mwoauthmanageconsumers-description": "বিবরণ",
+ "mwoauthmanageconsumers-email": "যোগাযোগের ইমেইল",
+ "mwoauthmanageconsumers-consumerkey": "ভোক্তার চাবি",
+ "mwoauthmanageconsumers-lastchange": "সর্বশেষ পরিবর্তন",
+ "mwoauthmanageconsumers-review": "পর্যালোচনা/পরিচালনা করুন",
+ "mwoauthmanageconsumers-action": "অবস্থা পরিবর্তন:",
+ "mwoauthmanageconsumers-approve": "অনুমোদিত",
+ "mwoauthmanageconsumers-reject": "প্রত্যাখ্যাত",
+ "mwoauthmanageconsumers-disable": "নিস্ক্রিয়",
+ "mwoauthmanageconsumers-reenable": "অনুমোদিত",
+ "mwoauthmanageconsumers-reason": "কারণ:",
+ "oauthlistconsumers": "ওআউথ অ্যাপ্লিকেশনের তালিকা",
+ "mwoauthlistconsumers-legend": "ওআউথ অ্যাপ্লিকেশন ব্রাউজ করুন",
+ "mwoauthlistconsumers-view": "বিস্তারিত",
+ "mwoauthlistconsumers-name": "অ্যাপলিকেশনের নাম",
+ "mwoauthlistconsumers-version": "ভোক্তার সংস্করণ",
+ "mwoauthlistconsumers-user": "প্রকাশক",
+ "mwoauthlistconsumers-description": "বিবরণ",
+ "mwoauthlistconsumers-status": "অবস্থা",
+ "mwoauth-consumer-stage-any": "যেকোন",
+ "mwoauthlistconsumers-status-proposed": "প্রস্তাবিত",
+ "mwoauthlistconsumers-status-approved": "অনুমোদিত",
+ "mwoauthlistconsumers-status-disabled": "নিস্ক্রিয়",
+ "mwoauthlistconsumers-status-rejected": "প্রত্যাখ্যাত",
+ "mwoauthlistconsumers-status-expired": "মেয়াদোত্তীর্ণ",
+ "mwoauthlistconsumers-rclink": "এই অ্যাপ্লিকেশন দ্বারা সাম্প্রতিক পরিবর্তনগুলি",
+ "oauthmanagemygrants": "সংযুক্ত অ্যাপ্লিকেশন পরিচালনা",
+ "mwoauthmanagemygrants-text": "এই পাতাটি যে অ্যাপ্লিকেশনগুলি আপনার অ্যাকাউন্ট ব্যবহার করতে পারে তা তালিকাবদ্ধ করে। এই জাতীয় যে কোনও অ্যাপ্লিকেশনের প্রবেশের পরিধি, আপনি যখন অ্যাপ্লিকেশনটিকে আপনার পক্ষ থেকে কাজ করার অনুমতি দিয়েছিলেন তখন আপনি এটিকে যে অনুমতি দিয়েছিলেন তার উপর সীমাবদ্ধ। আপনি যদি আলাদাভাবে বিভিন্ন সহপ্রকল্পগুলিতে আপনার পক্ষ থেকে প্রবেশের জন্য কোনও অ্যাপ্লিকেশনকে অনুমোদন দিয়ে থাকেন, তবে নীচে প্রতিটি প্রকল্পের জন্য আপনি পৃথক কনফিগারেশন দেখতে পাবেন।\n\nসংযুক্ত অ্যাপ্লিকেশনগুলি OAuth প্রোটোকল ব্যবহার করে আপনার অ্যাকাউন্টে প্রবেশ করে। <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth সংযুক্ত অ্যাপ্লিকেশনগুলি সম্পর্কে আরও জানুন])</span>",
+ "mwoauthmanagemygrants-navigation": "পরিভ্রমণ:",
+ "mwoauthmanagemygrants-showlist": "সংযুক্ত অ্যাপ্লিকেশনের তালিকা",
+ "mwoauthmanagemygrants-none": "আপনার অ্যাকাউন্টের সাথে কোন অ্যাপ্লিকেশন সংযুক্ত নেই।",
+ "mwoauthmanagemygrants-user": "প্রকাশক:",
+ "mwoauthmanagemygrants-description": "বিবরণ",
+ "mwoauthmanagemygrants-wikiallowed": "প্রকল্পে অনুমোদিত:",
+ "mwoauthmanagemygrants-grants": "প্রয়োগযোগ্য মঞ্জুরি",
+ "mwoauthmanagemygrants-review": "প্রবেশাধিকার পরিচালনা করুন",
+ "mwoauthmanagemygrants-revoke": "প্রবেশাধিকার প্রত্যাহার করুন",
+ "mwoauthmanagemygrants-grantaccept": "মঞ্জুর",
+ "mwoauthmanagemygrants-confirm-legend": "সংযুক্ত অ্যাপ্লিকেশন পরিচালনা করুন",
+ "mwoauthmanagemygrants-action": "অবস্থা পরিবর্তন করুন:",
+ "mwoauthmanagemygrants-editslink": "এই অ্যাপ্লিকেশন দ্বারা {{GENDER:$1|আপনার}} সম্পাদনাগুলি",
+ "mwoauthmanagemygrants-actionslink": "এই অ্যাপ্লিকেশন দ্বারা {{GENDER:$1|আপনার}} কার্যগুলি",
+ "mwoauthdatastore-request-token-not-found": "দুঃখিত, এই অ্যাপ্লিকেশনটিতে সংযুক্ত হবার সময় কিছু ভুল হয়েছিল।\nফিরে যান এবং আপনার অ্যাকাউন্টে আবার সংযোগ করার চেষ্টা করুন, অথবা অ্যাপ্লিকেশন কর্তৃপক্ষের সাথে যোগাযোগ করুন।\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth টোকেন পাওয়া যায়নি, $1</span>",
+ "mwoauth-form-description-allwikis": "প্রিয় $1,\n\nআপনার অনুরোধ সম্পূর্ণ করার জন্য, এই সাইটের সব প্রকল্পে আপনার পক্ষে নিম্নলিখিত কর্ম সঞ্চালনের জন্য '''$2'''-এর অনুমতি প্রয়োজন:\n\n$4",
+ "mwoauth-form-description-onewiki": "প্রিয় $1,\n\nআপনার অনুরোধ সম্পূর্ণ করার জন্য, ''$4-এ'' আপনার পক্ষে নিম্নলিখিত কর্ম সঞ্চালনের জন্য '''$2'''-এর অনুমতি প্রয়োজন:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "প্রিয় $1,\n\nআপনার অনুরোধ সম্পূর্ণ করার জন্য, '''$2''' এই সাইটের সব প্রকল্পের তথ্যে আপনার পক্ষে প্রবেশাধিকারের অনুমতি নিতে চায়। আপনার অ্যাকাউন্টে কোন পরিবর্তন করা হবে না।",
+ "mwoauth-form-description-onewiki-nogrants": "প্রিয় $1,\n\nআপনার অনুরোধ সম্পূর্ণ করার জন্য, আপনার পক্ষে ''$4''-এর তথ্যে প্রবেশাধিকার পেতে '''$2'''-এর অনুমতি প্রয়োজন। আপনার অ্যাকাউন্টে কোন পরিবর্তন করা হবে না।",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "সুপ্রিয় $1,\nআপনার অনুরোধটি সম্পূর্ণ করার জন্য, ''$2'' কর্তৃক সাইটের সকল প্রকল্পে, আপনার ইমেল ঠিকানাসহ আপনার সম্পর্কে তথ্যে প্রবেশাধিকার পাবার অনুমতি প্রয়োজন। আপনার অ্যাকাউন্টে কোন পরিবর্তন করা হবে না।",
+ "mwoauth-form-button-approve": "মঞ্জুর",
+ "mwoauth-form-button-cancel": "বাতিল",
+ "mwoauth-error": "অ্যাপ্লিকেশন সংযোগ ত্রুটি",
+ "mwoauth-grants-heading": "অনুরোধকৃত অনুমতি:",
+ "grant-mwoauth-authonlyprivate": "শুধুমাত্র প্রকৃত নাম ও ই-মেইল ঠিকানায় প্রবেশাধিকারসহ ব্যবহারকারী পরিচয় যাচাই করে, ব্যবহারকারীর পক্ষে পড়া বা কাজ করার ক্ষমতা নেই।",
+ "echo-category-title-oauth-owner": "OAuth উন্নয়ন",
+ "echo-category-title-oauth-admin": "OAuth প্রশাসক",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$3|আপনার}} OAuth অ্যাপ ($2) {{GENDER:$1|অনুমোদন করেছেন}}",
+ "notification-oauth-app-propose-primary-link": "অ্যাপ পর্যালোচনা করুন",
+ "notification-oauth-app-update-primary-link": "অ্যাপ পর্যালোচনা করুন",
+ "notification-oauth-app-approve-primary-link": "অ্যাপ দেখুন",
+ "notification-oauth-app-reject-primary-link": "অ্যাপ দেখুন",
+ "notification-oauth-app-disable-primary-link": "অ্যাপ দেখুন",
+ "notification-oauth-app-reenable-primary-link": "অ্যাপ দেখুন",
+ "notification-oauth-app-body": "কারণ: $1",
+ "mwoauthconsumer-application-view": "এই অ্যাপ্লিকেশনটি দেখুন"
+}
diff --git a/OAuth/i18n/br.json b/OAuth/i18n/br.json
new file mode 100644
index 00000000..a80e745b
--- /dev/null
+++ b/OAuth/i18n/br.json
@@ -0,0 +1,92 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fohanno",
+ "Huñvreüs",
+ "Y-M D"
+ ]
+ },
+ "mwoauth-field-hidden": "(kuzhet eo an titour-mañ)",
+ "mwoauth-field-private": "(prevez eo an titour-mañ)",
+ "mwoauth-prefs-managegrants": "Arloadoù kevreet :",
+ "mwoauth-prefs-managegrantslink": "Merañ $1 arload kevreet",
+ "mwoauth-consumer-allwikis": "An holl raktresoù war al lec'hienn-mañ",
+ "mwoauth-consumer-key": "Alc'hwez bevezer :",
+ "mwoauth-consumer-name": "Anv an arload :",
+ "mwoauth-consumer-version": "Stumm bevezer :",
+ "mwoauth-consumer-user": "Embanner :",
+ "mwoauth-consumer-stage": "Statud a vremañ :",
+ "mwoauth-consumer-description": "Deskrivadur an arload :",
+ "mwoauth-consumer-restrictions": "Strishadennoù implij :",
+ "mwoauth-consumer-accesstoken": "Jedouar moned :",
+ "mwoauth-consumer-reason": "Abeg :",
+ "mwoauth-consumer-email-unconfirmed": "Chomlec'h postel ho kont n'eo ket bet kadarnaet c'hoazh.",
+ "mwoauth-consumer-not-proposed": "Ar bevezer n'eo ket kinniget evit bremañ",
+ "mwoauth-consumer-not-disabled": "Ar bevezet n'eo ket diweredekaet evit bremañ",
+ "mwoauth-missing-consumer-key": "N'eus bet pourchaset alc'hwez ebvezer ebet.",
+ "mwoauth-invalid-consumer-key": "N'eus bevezer ebet gant an alc'hwez a zo bet pourchaset.",
+ "mwoauth-consumer-stage-proposed": "kinniget",
+ "mwoauth-consumer-stage-rejected": "distaolet",
+ "mwoauth-consumer-stage-expired": "aet d'e dermen",
+ "mwoauth-consumer-stage-approved": "aprouet",
+ "mwoauth-consumer-stage-disabled": "diweredekaet",
+ "mwoauth-consumer-stage-suppressed": "lamet",
+ "mwoauthconsumerregistration-navigation": "Merdeiñ :",
+ "mwoauthconsumerregistration-propose": "Kinnig ur bevezer nevez",
+ "mwoauthconsumerregistration-list": "Ma roll bevezerien",
+ "mwoauthconsumerregistration-propose-submit": "Kinnig ur bevezer",
+ "mwoauthconsumerregistration-update-submit": "Hizivaat ur bevezer",
+ "mwoauthconsumerregistration-user": "Embanner",
+ "mwoauthconsumerregistration-description": "Deskrivadur",
+ "mwoauthconsumerregistration-consumerkey": "Alc'hwez bevezer",
+ "mwoauthconsumerregistration-stage": "Statud",
+ "mwoauthconsumerregistration-lastchange": "Kemm diwezhañ",
+ "mwoauthconsumerregistration-manage": "merañ",
+ "oauthmanageconsumers": "Merañ ar vevezerien OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Ret eo deoc'h bezañ kevreet evit mont d'ar bajenn-mañ.",
+ "mwoauthmanageconsumers-type": "Lostennadoù :",
+ "mwoauthmanageconsumers-showproposed": "Rekedoù kinniget",
+ "mwoauthmanageconsumers-showrejected": "Rekedoù distaolet",
+ "mwoauthmanageconsumers-name": "Bevezer",
+ "mwoauthmanageconsumers-user": "Embanner",
+ "mwoauthmanageconsumers-description": "Deskrivadur",
+ "mwoauthmanageconsumers-lastchange": "Kemm diwezhañ",
+ "mwoauthmanageconsumers-review": "adwelet/merañ",
+ "mwoauthmanageconsumers-action": "Cheñch statud :",
+ "mwoauthmanageconsumers-approve": "Aprouet",
+ "mwoauthmanageconsumers-reject": "Distaolet",
+ "mwoauthmanageconsumers-rsuppress": "Distaolet ha lamet",
+ "mwoauthmanageconsumers-disable": "Diweredekaet",
+ "mwoauthmanageconsumers-dsuppress": "Diweredekaet ha lamet",
+ "mwoauthmanageconsumers-reenable": "Aprouet",
+ "mwoauthmanageconsumers-reason": "Abeg :",
+ "mwoauthmanageconsumers-confirm-submit": "Hizivaat statud ar bevezer",
+ "mwoauthmanageconsumers-success-approved": "Apouet eo bet ar reked.",
+ "mwoauthmanageconsumers-success-rejected": "Distaolet eo bet ar reked.",
+ "mwoauthmanageconsumers-success-disabled": "Diweredekaet eo bet ar bevezer.",
+ "mwoauthmanageconsumers-success-reanable": "Adweredekaet eo bet ar bevezer.",
+ "mwoauthlistconsumers-view": "munudoù",
+ "mwoauthlistconsumers-name": "Anv ar poellad",
+ "mwoauthlistconsumers-user": "Embanner",
+ "mwoauthlistconsumers-description": "Deskrivadur",
+ "mwoauthlistconsumers-status": "Statud",
+ "mwoauth-consumer-stage-any": "forzh pehini",
+ "mwoauthlistconsumers-status-proposed": "kinniget",
+ "mwoauthlistconsumers-status-approved": "aprouet",
+ "mwoauthlistconsumers-status-disabled": "diweredekaet",
+ "mwoauthlistconsumers-status-rejected": "distaolet",
+ "mwoauthlistconsumers-status-expired": "aet d'e dermen",
+ "oauthmanagemygrants": "Merañ an arloadoù kevreet",
+ "mwoauthmanagemygrants-navigation": "Merdeiñ :",
+ "mwoauthmanagemygrants-showlist": "Roll an arloadoù kevreet",
+ "mwoauthmanagemygrants-none": "N'eus arload ebet kevreet ouzh ho kont.",
+ "mwoauthmanagemygrants-user": "Embanner :",
+ "mwoauthmanagemygrants-description": "Deskrivadur",
+ "mwoauthmanagemygrants-action": "Cheñch statud :",
+ "mwoauthserver-invalid-request-token": "Jedouar direizh en ho reked.",
+ "mwoauth-form-button-approve": "Aotren",
+ "mwoauth-form-button-cancel": "Nullañ",
+ "right-mwoauthproposeconsumer": "Kinnig bevezerien OAuth nevez",
+ "right-mwoauthupdateownconsumer": "Hizivaat ar vevezerien OAuth a gontrollit",
+ "right-mwoauthmanageconsumer": "Merañ ar vevezerien OAuth"
+}
diff --git a/OAuth/i18n/bs.json b/OAuth/i18n/bs.json
new file mode 100644
index 00000000..52734885
--- /dev/null
+++ b/OAuth/i18n/bs.json
@@ -0,0 +1,38 @@
+{
+ "@metadata": {
+ "authors": [
+ "Srdjan m"
+ ]
+ },
+ "mwoauth-desc": "Omogućava upotrebu OAutha 1.0a za autentifikaciju API-a",
+ "mwoauth-prefs-managegrants": "Povezane aplikacije:",
+ "mwoauth-prefs-managegrantslink": "Upravljaj s ukupno $1 {{PLURAL:$1|aplikacijom|aplikacije|aplikacija}}",
+ "mwoauth-consumer-allwikis": "svim projektima na ovoj stranici",
+ "mwoauth-consumer-name": "Ime aplikacije:",
+ "mwoauth-consumer-user": "Izdavač:",
+ "mwoauth-consumer-description": "Opis aplikacije:",
+ "mwoauthlistconsumers-grants": "Primjenjive dozvole",
+ "oauthmanagemygrants": "Upravljaj povezanim aplikacijama",
+ "mwoauthmanagemygrants-text": "Ovo je spisak svih aplikacija koje mogu koristiti Vaš račun. Raspon djelovanja svake aplikacije ograničen je dopuštenjima koja ste odobrili prilikom ovlaštenja aplikacije da djeluje u Vaše ime. Ako ste posebno ovlastili aplikaciju da ima pristup drugim projektima u Vaše ime, ispod ćete vidjeti posebne postavke za svaki projekt.\n\nPovezane aplikacije za pristupanje Vašem korisničkom računu koriste protokol OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Saznajte više o povezanim aplikacijama])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigacija:",
+ "mwoauthmanagemygrants-showlist": "Spisak povezanih aplikacija",
+ "mwoauthmanagemygrants-user": "Izdavač:",
+ "mwoauthmanagemygrants-wikiallowed": "Dopušteno na:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Dozvoljena sljedeća prava:",
+ "mwoauthmanagemygrants-review": "upravljaj pristup",
+ "mwoauthmanagemygrants-revoke": "poništi pristup",
+ "mwoauthmanagemygrants-grantaccept": "Dozvoljeno",
+ "mwoauthmanagemygrants-update-text": "Koristite donji formular za promjenu dopuštenja koja su odobrena aplikaciji.",
+ "mwoauthmanagemygrants-revoke-text": "Koristite donji formular da biste poništili pristup aplikaciji i spriječili da djeluje u Vaše ime.",
+ "mwoauthmanagemygrants-confirm-legend": "Upravljanje povezanom aplikacijom",
+ "mwoauthmanagemygrants-update": "Ažuriraj dozvole",
+ "mwoauthmanagemygrants-renounce": "Poništi pristup",
+ "mwoauthmanagemygrants-action": "Izmijeni status:",
+ "mwoauthmanagemygrants-basic-tooltip": "Zašto ne mogu promijeniti ovu dozvolu? Ova dozvola daje povezanoj aplikaciji osnovno dopuštenje koje joj je neophodno za ispravni rad. Ako ne želite da aplikacija ima ova prava, poništite joj prava pristupa.",
+ "mwoauth-form-description-onewiki": "Zdravo, $1,\n\nDa bi se mogao ispuniti Vaš zahtjev, aplikaciji '''$2''' potrebna je dozvola da vrši sljedeće radnje u Vaše ime na ''$4'':\n\n$5",
+ "mwoauth-form-button-approve": "Dozvoli",
+ "mwoauth-form-button-cancel": "Otkaži",
+ "mwoauth-acceptance-cancelled": "Odlučili ste da aplikaciji \"$1\" ne dozvolite pristup računu. \"$1\" može raditi samo ako mu date dozvolu. Možete se vratiti na \"$1\" ili [[Special:OAuthManageMyGrants|upravljati]] povezanim aplikacijama.",
+ "right-mwoauthmanagemygrants": "Upravljanje dozvolama za OAuth",
+ "action-mwoauthmanagemygrants": "upravljate vlastitim dozvolama za OAuth"
+}
diff --git a/OAuth/i18n/ca.json b/OAuth/i18n/ca.json
new file mode 100644
index 00000000..3cc70493
--- /dev/null
+++ b/OAuth/i18n/ca.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fitoschido",
+ "Pginer",
+ "Townie"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplicacions connectades:",
+ "mwoauth-prefs-managegrantslink": "Gestiona $1 {{PLURAL:$1|aplicació connectada|aplicacions connectades}}",
+ "mwoauth-consumer-allwikis": "Tots els projectes d’aquest lloc",
+ "mwoauth-consumer-name": "Nom de l’aplicació:",
+ "mwoauthconsumerregistration-navigation": "Navegació:",
+ "mwoauthconsumerregistration-description": "Descripció",
+ "mwoauthconsumerregistration-stage": "Estat",
+ "mwoauthconsumerregistration-manage": "gestiona",
+ "mwoauthmanageconsumers-description": "Descripció",
+ "mwoauthlistconsumers-view": "detalls",
+ "oauthmanagemygrants": "Gestió d’aplicacions connectades",
+ "mwoauthmanagemygrants-navigation": "Navegació:",
+ "mwoauthmanagemygrants-description": "Descripció",
+ "mwoauthdatastore-bad-source-ip": "La sol·licitud provenia d’una adreça IP no vàlida.",
+ "action-mwoauthviewprivate": "veure dades privades de l’OAuth",
+ "notification-oauth-app-body": "Motiu: $1",
+ "mwoauthconsumer-application-view": "Mostra aquesta aplicació"
+}
diff --git a/OAuth/i18n/ce.json b/OAuth/i18n/ce.json
new file mode 100644
index 00000000..ac2adcd6
--- /dev/null
+++ b/OAuth/i18n/ce.json
@@ -0,0 +1,120 @@
+{
+ "@metadata": {
+ "authors": [
+ "Исмаил Садуев",
+ "Умар"
+ ]
+ },
+ "mwoauth-desc": "OAuth 1.0a лелича йиш хуьлуьйту API чудаха",
+ "mwoauth-db-readonly": "OAuth хаамийн базан хана блоктоьха. Масийтта минот яьлча ху хьажа.",
+ "mwoauth-missing-field": "МаьӀна дац «$1»",
+ "mwoauth-invalid-field": "Магийна доцу маьӀна «$1»",
+ "mwoauth-invalid-field-generic": "ТӀе цакхочу маьӀна",
+ "mwoauth-field-hidden": "(хӀара хаам къайлаха бу)",
+ "mwoauth-field-private": "(хӀара хаам къайлаха бу)",
+ "mwoauth-prefs-managegrants": "ТӀетесна тӀетохарш:",
+ "mwoauth-prefs-managegrantslink": "ТӀетесна $1 {{PLURAL:$1|1=тӀетохаран урхалладар|тӀетохаршан урхалладар}}",
+ "mwoauth-consumer-allwikis": "ХӀокху сайтан массо проекташ",
+ "mwoauth-consumer-key": "Клиентан идентификатор:",
+ "mwoauth-consumer-name": "ТӀетохаран цӀе:",
+ "mwoauth-consumer-version": "Клиентан верси:",
+ "mwoauth-consumer-user": "Арахецархо:",
+ "mwoauth-consumer-stage": "Карара хьал:",
+ "mwoauth-consumer-email": "Контактан электронан поштан адрес:",
+ "mwoauth-consumer-owner-only-label": "Йолчуьна бен:",
+ "mwoauth-consumer-description": "Цуьнах лаьцна:",
+ "mwoauth-consumer-callbackurl": "«URL-адрес юха кхайкхаман» OAuth:",
+ "mwoauth-consumer-granttypes": "Йоьхуш йолу бакъонаш:",
+ "mwoauth-consumer-grantsneeded": "Лелош йолу гранташ:",
+ "mwoauth-consumer-required-grant": "Клиентан лело мега",
+ "mwoauth-consumer-wiki": "Проектан лело мега:",
+ "mwoauth-consumer-wiki-thiswiki": "Карара проект ($1)",
+ "mwoauth-consumer-restrictions": "Лелоран доза тохар:",
+ "mwoauth-consumer-restrictions-json": "Лелоран (JSON) доза тохар:",
+ "mwoauth-consumer-rsakey": "Гуш долу RSA-догӀа (тӀехь дац):",
+ "mwoauth-consumer-secretkey": "Клиентан къайлаха догӀа:",
+ "mwoauth-consumer-accesstoken": "Код (токен) тӀекхочуш ю:",
+ "mwoauth-consumer-reason": "Бахьана:",
+ "mwoauth-consumer-email-unconfirmed": "Хьан электроннан поштан адрес хӀинца а бакъдина дац.",
+ "mwoauth-consumer-stage-proposed": "кховдийна",
+ "mwoauth-consumer-stage-rejected": "дӀаяйина",
+ "mwoauth-consumer-stage-expired": "чекхъяла",
+ "mwoauth-consumer-stage-approved": "бакъйина",
+ "mwoauth-consumer-stage-disabled": "дӀадайина",
+ "mwoauth-consumer-stage-suppressed": "дохина",
+ "oauthconsumerregistration": "Клиентан OAuth дӀаяздар кхоллар",
+ "mwoauthconsumerregistration-navigation": "Навигациː",
+ "mwoauthconsumerregistration-main": "Коьрта",
+ "mwoauthconsumerregistration-name": "Клиент",
+ "mwoauthconsumerregistration-user": "Арахецархо",
+ "mwoauthconsumerregistration-description": "Цуьнах лаьцна",
+ "mwoauthconsumerregistration-email": "Контактан электронан поштан адрес",
+ "mwoauthconsumerregistration-consumerkey": "Клиентан идентификатор",
+ "mwoauthconsumerregistration-stage": "Хьал",
+ "mwoauthconsumerregistration-lastchange": "ТӀаьххьара хийцамаш",
+ "mwoauthconsumerregistration-manage": "урхалладар",
+ "mwoauthmanageconsumers-type": "РогӀеш:",
+ "mwoauthmanageconsumers-showproposed": "Кховдийна дехарш",
+ "mwoauthmanageconsumers-showrejected": "ТӀецалаьцна дехарш",
+ "mwoauthmanageconsumers-showexpired": "Шира делла дехарш",
+ "mwoauthmanageconsumers-linkproposed": "кховдийна дехарш",
+ "mwoauthmanageconsumers-linkrejected": "тӀецалаьцна дехарш",
+ "mwoauthmanageconsumers-linkexpired": "шира делла дехарш",
+ "mwoauthmanageconsumers-linkapproved": "магийна дехарш",
+ "mwoauthmanageconsumers-linkdisabled": "тӀецалаьцна дехарш",
+ "mwoauthmanageconsumers-main": "Коьрта",
+ "mwoauthmanageconsumers-name": "Клиент",
+ "mwoauthmanageconsumers-user": "Арахецархо",
+ "mwoauthmanageconsumers-description": "Цуьнах лаьцна",
+ "mwoauthmanageconsumers-email": "Контактан электронан поштан адрес",
+ "mwoauthmanageconsumers-consumerkey": "Клиентан идентификатор",
+ "mwoauthmanageconsumers-lastchange": "ТӀаьххьара хийцамаш",
+ "mwoauthmanageconsumers-review": "гар/урхалладар",
+ "mwoauthmanageconsumers-action": "Хийца хьал:",
+ "mwoauthmanageconsumers-approve": "Магийна",
+ "mwoauthmanageconsumers-reject": "ДӀаяйина",
+ "mwoauthmanageconsumers-disable": "ДӀадайина",
+ "mwoauthmanageconsumers-reenable": "Магийна",
+ "mwoauthmanageconsumers-reason": "Бахьана:",
+ "oauthlistconsumers": "OAuth тӀетохарийн могӀам",
+ "mwoauthlistconsumers-legend": "OAuth-тӀетохаршка хьажар",
+ "mwoauthlistconsumers-view": "мадарра",
+ "mwoauthlistconsumers-name": "ТӀетохаран цӀе:",
+ "mwoauthlistconsumers-version": "Клиентан верси",
+ "mwoauthlistconsumers-user": "Арахецархо",
+ "mwoauthlistconsumers-description": "Цуьнах лаьцна",
+ "mwoauthlistconsumers-wiki": "Проектан лело мега",
+ "mwoauthlistconsumers-callbackurl": "«URL-адрес юха кхайкхаман» OAuth:",
+ "mwoauthlistconsumers-grants": "Лелош йолу шоралаш:",
+ "mwoauthlistconsumers-basicgrantsonly": "(базан тӀекхачар бен)",
+ "mwoauthlistconsumers-status": "Хьал",
+ "mwoauth-consumer-stage-any": "муьлха",
+ "mwoauthlistconsumers-status-proposed": "кховдийна",
+ "mwoauthlistconsumers-status-approved": "магийна",
+ "mwoauthlistconsumers-status-disabled": "юхатоьхна",
+ "mwoauthlistconsumers-status-rejected": "юхатоьхна",
+ "mwoauthlistconsumers-status-expired": "чекхъяла",
+ "oauthmanagemygrants": "ТӀетесна тӀетохаршан урхалладар",
+ "mwoauthmanagemygrants-text": "ХӀокху агӀонгахь гойтуш ю хьан декъашхочун дӀаяздар лело йиш йолу тӀетохарш. ХӀора тӀетохар шордаро дихкина ду. Ахьа тӀетохаран дӀаяздар леладан бакъо елехь хьа йиш ю лахахь царна урхалла дан\n\nТӀетесна тӀетохаршна болх бан ло OAuth протоколан гӀоьнца. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Хьажа кхин тӀе тӀетесна йолу тӀетохаршах лаьца])</span>",
+ "mwoauthmanagemygrants-navigation": "Навигациː",
+ "mwoauthmanagemygrants-showlist": "ТӀетесна тӀетохаршан могӀа",
+ "mwoauthmanagemygrants-none": "Хьан декъашхочун дӀаяздаран хӀинца яц тӀетесна тӀетохарш.",
+ "mwoauthmanagemygrants-user": "Арахецархо",
+ "mwoauthmanagemygrants-description": "Цуьнах лаьцна",
+ "mwoauthmanagemygrants-wikiallowed": "Магийна проектехь:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Къобалйина магийнарш:",
+ "mwoauthmanagemygrants-review": "тӀекхачарна урхалладар",
+ "mwoauthmanagemygrants-revoke": "тӀекхачар дохо",
+ "mwoauthmanagemygrants-grantaccept": "Магийна",
+ "mwoauthmanagemygrants-update-text": "Лелае лахара форма, тӀетохарш хьай цӀарца дешдерг маго.",
+ "mwoauthmanagemygrants-revoke-text": "Лелае лахара форма, хьайн цӀарца болх беш йолу тӀетохарш саца ян.",
+ "mwoauthmanagemygrants-confirm-legend": "ТӀетесна тӀетохаршан урхалладар",
+ "mwoauthmanagemygrants-update": "Карладаха магийнарш",
+ "mwoauthmanagemygrants-renounce": "ДӀахадае авторизаци",
+ "mwoauth-form-description-allwikis-nogrants": "Маршалла $1,\n\n'''$2''' хьан цӀарца больхбан хьан дӀаяздаран чугӀо.",
+ "mwoauth-form-button-approve": "Магаде",
+ "mwoauth-form-button-cancel": "Цаоьшу",
+ "mwoauth-grants-heading": "Магийтар деха:",
+ "mwoauth-callback-not-oob": "oauth_callback хӀоттина хила еза «oob» маьӀна а долуш (регистр лоруш)",
+ "notification-oauth-app-body": "Бахьана: $1"
+}
diff --git a/OAuth/i18n/ckb.json b/OAuth/i18n/ckb.json
new file mode 100644
index 00000000..e8169a13
--- /dev/null
+++ b/OAuth/i18n/ckb.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arya sarhan",
+ "Calak",
+ "Lost Whispers",
+ "Sarchia",
+ "Épine"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "بەرنامە لکێنراوەکان:",
+ "mwoauth-prefs-managegrantslink": "بەڕێوەبردنی $1 بەرنامەی بەستراو بەم ھەژمارەوە",
+ "oauthmanageconsumers": "بەڕێوەبەردنی بەکاربەرانی OAuth",
+ "oauthmanagemygrants": "بینین و دەستکاریکردنی ئاپە بەستراوەکان بەم ھەژمارەوە",
+ "right-mwoauthproposeconsumer": "پێشنیاری بەکاربەرانی OAuthی نوێ",
+ "right-mwoauthupdateownconsumer": "نوێکردنەوەی بەکاربەرانی OAuthی تاوتوێی دەکەی",
+ "right-mwoauthmanageconsumer": "بەڕێوەبەردنی بەکاربەرانی OAuth",
+ "right-mwoauthmanagemygrants": "بەڕێوەبەردنی مافەکانی OAuth",
+ "action-mwoauthmanageconsumer": "بەڕێوەبەردنی بەکاربەرانی OAuth",
+ "action-mwoauthmanagemygrants": "بەڕێوەبەردنی مافەکانی OAuthەکەت",
+ "action-mwoauthproposeconsumer": "پێشنیاری بەکاربەرانی OAuthی نوێ",
+ "action-mwoauthupdateownconsumer": "نوێکردنەوەی بەکاربەرانی OAuthی تاوتوێی دەکەی",
+ "mwoauthconsumer-application-view": "بینینی ئەم نەرمەواڵەیە"
+}
diff --git a/OAuth/i18n/cs.json b/OAuth/i18n/cs.json
new file mode 100644
index 00000000..10b5719f
--- /dev/null
+++ b/OAuth/i18n/cs.json
@@ -0,0 +1,277 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cvanca",
+ "Dvorapa",
+ "Luky001",
+ "Matěj Grabovský",
+ "Matěj Suchánek",
+ "Mormegil",
+ "MrJaroslavik",
+ "Patriccck",
+ "Tchoř"
+ ]
+ },
+ "mwoauth-desc": "Umožňuje využití OAuth 1.0a pro autorizaci přístupu k API",
+ "mwoauth-verified": "Tato aplikace má nyní oprávnění přistupovat k MediaWiki vaším jménem.\n\nPro dokončení procesu poskytněte aplikaci tuto ověřovací hodnotu: '''$1'''",
+ "mwoauth-db-readonly": "Databáze OAuth je dočasně uzamčena. Zkuste to prosím znovu za několik minut.",
+ "mwoauth-missing-field": "Chybějící hodnota pole „$1“",
+ "mwoauth-invalid-field": "Uvedena neplatná hodnota pole „$1“",
+ "mwoauth-invalid-field-generic": "Zadána neplatná hodnota",
+ "mwoauth-field-hidden": "(tato informace je skryta)",
+ "mwoauth-field-private": "(tato informace je soukromá)",
+ "mwoauth-prefs-managegrants": "Připojené aplikace:",
+ "mwoauth-prefs-managegrantslink": "Spravovat {{PLURAL:$1|$1 připojenou aplikaci|$1 připojené aplikace|$1 připojených aplikací|0=připojené aplikace}}",
+ "mwoauth-consumer-allwikis": "Všechny projekty na tomto webu",
+ "mwoauth-consumer-key": "Klíč konzumenta:",
+ "mwoauth-consumer-name": "Název aplikace:",
+ "mwoauth-consumer-version": "Verze konzumenta:",
+ "mwoauth-consumer-user": "Vydavatel:",
+ "mwoauth-consumer-stage": "Aktuální stav:",
+ "mwoauth-consumer-email": "Kontaktní e-mailová adresa:",
+ "mwoauth-consumer-email-help": "Viditelná jen schvalovatelům nových konzumentů",
+ "mwoauth-consumer-owner-only-label": "Jen pro vlastníka:",
+ "mwoauth-consumer-owner-only": "Tento konzument je určen jen pro použití uživatelem $1.",
+ "mwoauth-consumer-owner-only-help": "Zaškrtnutí této možnosti způsobí, že tento konzument bude automaticky schválen a přijat pro použití uživatelem $1. Nebude ho moci použít žádný jiný uživatel a běžný autentizační postup nebude fungovat. Akce provedené tímto konzumentem nebudou označeny.",
+ "mwoauth-consumer-description": "Popis aplikace:",
+ "mwoauth-consumer-callbackurl": "URL pro OAuth „callback“:",
+ "mwoauth-consumer-callbackisprefix": "Umožnit konzumentovi, aby v požadavcích uvedl callback a URL „callback“ výše používat jako povinný prefix.",
+ "mwoauth-consumer-granttypes": "Typy požadovaných oprávnění:",
+ "mwoauth-consumer-grantsneeded": "Použitelná oprávnění:",
+ "mwoauth-consumer-required-grant": "Použitelné konzumentem",
+ "mwoauth-consumer-wiki": "Použitelný projekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Tento projekt ($1)",
+ "mwoauth-consumer-restrictions": "Omezení užití:",
+ "mwoauth-consumer-restrictions-json": "Omezení užití (JSON):",
+ "mwoauth-consumer-rsakey": "Veřejný RSA klíč (nepovinný):",
+ "mwoauth-consumer-rsakey-help": "Pro použití podpisové metody RSA-SHA1 zadejte veřejný klíč. Pro použití HMAC-SHA1 s náhodným tajemstvím ponechte prázdné. Pokud si nejste jisti, ponechte prázdné.",
+ "mwoauth-consumer-secretkey": "Tajný token konzumenta:",
+ "mwoauth-consumer-accesstoken": "Přístupový token:",
+ "mwoauth-consumer-reason": "Důvod:",
+ "mwoauth-consumer-developer-agreement": "Odesláním této žádosti berete na vědomí, že si vyhrazujeme právo vaši aplikaci vypnout, odebrat či omezit vám či vaší aplikaci přístup k tomuto webu nebo provést libovolné jiné opatření, které budeme považovat za vhodné, pokud se budeme domnívat, čistě podle svého uvážení, že vy nebo vaše aplikace porušujete jakékoli pravidlo, doporučení či zvyklost na tomto webu. Tato Pravidla aplikací můžeme kdykoli změnit bez předchozího upozornění, dle našeho výhradního rozhodnutí, jakkoli budeme považovat za potřebné. Pokračováním ve využívání OAuth vyjadřujete s takovými změnami souhlas.",
+ "mwoauth-consumer-email-unconfirmed": "E-mailová adresa vašeho uživatelského účtu dosud nebyla potvrzena.",
+ "mwoauth-consumer-email-mismatched": "Uvedená e-mailová adresa musí odpovídat té ve vašem uživatelském účtu.",
+ "mwoauth-consumer-alreadyexists": "Konzument s touto kombinací název/verze/vydavatel již existuje",
+ "mwoauth-consumer-alreadyexistsversion": "Konzument s touto kombinací název/vydavatel již existuje ve stejné či vyšší verzi („$1“)",
+ "mwoauth-consumer-not-accepted": "Nelze změnit údaje u probíhajícího požadavku na konzumenta",
+ "mwoauth-consumer-not-proposed": "Tento konzument není momentálně navržen",
+ "mwoauth-consumer-not-disabled": "Tento konzument není momentálně zakázán",
+ "mwoauth-consumer-not-approved": "Tento konzument není schválen (mohl být zakázán)",
+ "mwoauth-missing-consumer-key": "Nebyl poskytnut klíč konzumenta.",
+ "mwoauth-invalid-consumer-key": "Žádný konzument s daným klíčem neexistuje.",
+ "mwoauth-invalid-access-token": "Žádný přístupový token s daným klíčem neexistuje.",
+ "mwoauth-invalid-access-wrongwiki": "Tohoto konzumenta lze používat pouze na projektu „$1“.",
+ "mwoauth-consumer-conflict": "Zatímco jste si tohoto konzumenta {{GENDER:|prohlížel|prohlížela|prohlíželi}}, někdo změnil jeho atributy. Možná si budete chtít prohlédnout protokol změn.",
+ "mwoauth-consumer-grantshelp": "Každé oprávnění přiděluje přístup k uvedeným uživatelským právům, která příslušný uživatelský účet již má. Více informací najdete v [[Special:ListGrants|tabulce oprávnění]].",
+ "mwoauth-consumer-stage-proposed": "navržený",
+ "mwoauth-consumer-stage-rejected": "odmítnutý",
+ "mwoauth-consumer-stage-expired": "propadlý",
+ "mwoauth-consumer-stage-approved": "schválený",
+ "mwoauth-consumer-stage-disabled": "zakázaný",
+ "mwoauth-consumer-stage-suppressed": "utajený",
+ "oauthconsumerregistration": "Registrace konzumenta OAuth",
+ "mwoauthconsumerregistration-navigation": "Navigace:",
+ "mwoauthconsumerregistration-propose": "Navrhnout nového konzumenta",
+ "mwoauthconsumerregistration-list": "Seznam mých konzumentů",
+ "mwoauthconsumerregistration-main": "Hlavní",
+ "mwoauthconsumerregistration-propose-text": "Vývojáři by měli používat níže zobrazený formulář k navržení nového konzumenta OAuth (podrobnosti najdete v [//www.mediawiki.org/wiki/Extension:OAuth?uselang=cs dokumentaci rozšíření]). Po odeslání tohoto formuláře obdržíte token, pomocí kterého se vaše aplikace bude identifikovat MediaWiki. Předtím, než budou moci ostatní uživatelé autorizovat vaši aplikaci, bude ji muset schválit některý správce OAuth.\n\nNěkolik doporučení a poznámek:\n* Snažte se používat co nejméně oprávnění. Vyhněte se těm, která ve skutečnosti zatím nepotřebujete.\n* Verze má tvar „major.minor.release“ (poslední dvě části jsou nepovinné) a zvyšuje se, když jsou potřeba změny oprávnění.\n* Pokud je to možné, poskytněte veřejný klíč RSA (ve formátu PEM), jinak se musí používat (méně bezpečný) tajný token.\n* Pomocí ID wiki můžete omezit tohoto konzumenta na jediný projekt na tomto serveru (pro všechny projekty uveďte „*“).",
+ "mwoauthconsumerregistration-update-text": "Pomocí níže uvedeného formuláře můžete změnit vlastnosti konzumenta OAuth, kterého spravujete.\n\nVšechny uvedené hodnoty přepíšou ty původní. Neponechávejte žádná pole prázdná, pokud nechcete jejich hodnoty smazat.",
+ "mwoauthconsumerregistration-maintext": "Tato stránka slouží k navrhování a změnám konzumentských aplikací OAuth (vizte http://oauth.net) v registru tohoto serveru.\n\nMůžete zde [[Special:OAuthConsumerRegistration/propose|navrhnout nového konzumenta]] nebo [[Special:OAuthConsumerRegistration/list|spravovat své existující konzumenty]].",
+ "mwoauthconsumerregistration-propose-legend": "Nová konzumentská aplikace OAuth",
+ "mwoauthconsumerregistration-update-legend": "Změna konzumentské aplikace OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Navrhnout konzumenta",
+ "mwoauthconsumerregistration-update-submit": "Upravit konzumenta",
+ "mwoauthconsumerregistration-none": "Nespravujete žádné konzumenty OAuth.",
+ "mwoauthconsumerregistration-name": "Konzument",
+ "mwoauthconsumerregistration-user": "Vydavatel",
+ "mwoauthconsumerregistration-description": "Popis",
+ "mwoauthconsumerregistration-email": "Kontaktní e-mail",
+ "mwoauthconsumerregistration-consumerkey": "Klíč konzumenta",
+ "mwoauthconsumerregistration-stage": "Stav",
+ "mwoauthconsumerregistration-lastchange": "Poslední změna",
+ "mwoauthconsumerregistration-manage": "spravovat",
+ "mwoauthconsumerregistration-resetsecretkey": "Resetovat tajný klíč na novou hodnotu",
+ "mwoauthconsumerregistration-proposed": "Vaše žádost o konzumenta OAuth byla přijata.\n\nByl vám přidělen token konzumenta '''$1''' a tajný token '''$2'''. ''Zaznamenejte si je pro budoucí použití.''",
+ "mwoauthconsumerregistration-created-owner-only": "Váš konzument OAuth byl vytvořen.\n\nVaše tokeny jsou:\n; Token konzumenta: $1\n; Tajemství konzumenta: $2\n; Přístupový token: $3\n; Přístupové tajemství: $4\n<em>Zaznamenejte si je pro budoucí použití.</em>",
+ "mwoauthconsumerregistration-updated": "Vaše registrace konzumenta OAuth byla upravena.",
+ "mwoauthconsumerregistration-secretreset": "Byl vám přidělen tajný token konzumenta '''$1'''. ''Zaznamenejte si ho pro budoucí použití.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Tokeny vašeho konzument OAuth byly resetovány. Nové tokeny jsou:\n; Token konzumenta: $1\n; Tajemství konzumenta: $2\n; Přístupový token: $3\n; Přístupové tajemství: $4\n<em>Zaznamenejte si je pro budoucí použití.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Před vytvořením OAuth aplikace musíte ověřit svou e-mailovou adresu.\nZadejte a ověřte svou e-mailovou adresu v [[Special:Preferences|nastavení]].",
+ "oauthmanageconsumers": "Správa konzumentů OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Pro přístup k této stránce musíte být přihlášen(a).",
+ "mwoauthmanageconsumers-type": "Fronty:",
+ "mwoauthmanageconsumers-showproposed": "Navržené žádosti",
+ "mwoauthmanageconsumers-showrejected": "Odmítnuté žádosti",
+ "mwoauthmanageconsumers-showexpired": "Propadlé žádosti",
+ "mwoauthmanageconsumers-main": "Hlavní",
+ "mwoauthmanageconsumers-maintext": "Tato stránka slouží k řešení požadavků na konzumentské aplikace OAuth (vizte http://oauth.net) a správě existujících konzumentů OAuth.",
+ "mwoauthmanageconsumers-queues": "Níže si vyberte frontu potvrzení konzumentů:",
+ "mwoauthmanageconsumers-q-proposed": "Fronta navržených žádostí o konzumenta",
+ "mwoauthmanageconsumers-q-rejected": "Fronta odmítnutých žádostí o konzumenta",
+ "mwoauthmanageconsumers-q-expired": "Fronta propadlých žádostí o konzumenta",
+ "mwoauthmanageconsumers-lists": "Níže si vyberte seznam konzumentů podle stavu:",
+ "mwoauthmanageconsumers-l-approved": "Seznam schválených konzumentů",
+ "mwoauthmanageconsumers-l-disabled": "Seznam zakázaných konzumentů",
+ "mwoauthmanageconsumers-none-proposed": "V tomto seznamu nejsou žádní navržení konzumenti.",
+ "mwoauthmanageconsumers-none-rejected": "V tomto seznamu nejsou žádní navržení konzumenti.",
+ "mwoauthmanageconsumers-none-expired": "V tomto seznamu nejsou žádní navržení konzumenti.",
+ "mwoauthmanageconsumers-none-approved": "Těmto kritériím nevyhovuje žádný konzument.",
+ "mwoauthmanageconsumers-none-disabled": "Těmto kritériím nevyhovuje žádný konzument.",
+ "mwoauthmanageconsumers-name": "Konzument",
+ "mwoauthmanageconsumers-user": "Vydavatel",
+ "mwoauthmanageconsumers-description": "Popis",
+ "mwoauthmanageconsumers-email": "Kontaktní e-mail",
+ "mwoauthmanageconsumers-consumerkey": "Klíč konzumenta",
+ "mwoauthmanageconsumers-lastchange": "Poslední změna",
+ "mwoauthmanageconsumers-review": "zkontrolovat/spravovat",
+ "mwoauthmanageconsumers-confirm-text": "Pomocí tohoto formuláře můžete tohoto konzumenta schválit, odmítnout, zakázat nebo znovu povolit.",
+ "mwoauthmanageconsumers-confirm-legend": "Správa konzumenta OAuth",
+ "mwoauthmanageconsumers-action": "Změnit stav:",
+ "mwoauthmanageconsumers-approve": "Schválený",
+ "mwoauthmanageconsumers-reject": "Odmítnutý",
+ "mwoauthmanageconsumers-rsuppress": "Odmítnutý a utajený",
+ "mwoauthmanageconsumers-disable": "Zakázaný",
+ "mwoauthmanageconsumers-dsuppress": "Zakázaný a utajený",
+ "mwoauthmanageconsumers-reenable": "Schválený",
+ "mwoauthmanageconsumers-reason": "Důvod:",
+ "mwoauthmanageconsumers-confirm-submit": "Aktualizovat stav konzumenta",
+ "mwoauthmanageconsumers-success-approved": "Žádost byla schválena.",
+ "mwoauthmanageconsumers-success-rejected": "Žádost byla zamítnuta.",
+ "mwoauthmanageconsumers-success-disabled": "Konzument byl zakázán.",
+ "mwoauthmanageconsumers-success-reanable": "Konzument byl znovu povolen.",
+ "mwoauthmanageconsumers-search-name": "konzumenti s tímto názvem",
+ "mwoauthmanageconsumers-search-publisher": "konzumenti tohoto uživatele",
+ "oauthlistconsumers": "Seznam aplikací OAuth",
+ "mwoauthlistconsumers-legend": "Procházet aplikace OAuth",
+ "mwoauthlistconsumers-view": "podrobnosti",
+ "mwoauthlistconsumers-none": "Nenalezena žádná aplikace odpovídající těmto kritériím.",
+ "mwoauthlistconsumers-name": "Název aplikace",
+ "mwoauthlistconsumers-version": "Verze konzumenta",
+ "mwoauthlistconsumers-user": "Vydavatel",
+ "mwoauthlistconsumers-description": "Popis",
+ "mwoauthlistconsumers-wiki": "Použitelný projekt",
+ "mwoauthlistconsumers-callbackurl": "URL pro OAuth „callback“",
+ "mwoauthlistconsumers-callbackisprefix": "Umožnit konzumentovi, aby v požadavcích uvedl callback a URL „callback“ výše používat jako povinný prefix.",
+ "mwoauthlistconsumers-grants": "Použitelná oprávnění",
+ "mwoauthlistconsumers-basicgrantsonly": "(pouze základní přístup)",
+ "mwoauthlistconsumers-status": "Stav",
+ "mwoauth-consumer-stage-any": "všechny",
+ "mwoauthlistconsumers-status-proposed": "navržený",
+ "mwoauthlistconsumers-status-approved": "schválený",
+ "mwoauthlistconsumers-status-disabled": "zakázaný",
+ "mwoauthlistconsumers-status-rejected": "odmítnutý",
+ "mwoauthlistconsumers-status-expired": "propadlý",
+ "mwoauthlistconsumers-rclink": "Poslední změny provedené touto aplikací",
+ "oauthmanagemygrants": "Správa připojených aplikací",
+ "mwoauthmanagemygrants-text": "Tato stránka obsahuje seznam aplikací, které mohou využívat váš účet. U každé takové aplikace je rozsah jejího přístupu omezen oprávněními, která jste aplikaci {{GENDER:|přidělil|přidělila|přidělili}} v okamžiku, kdy jste jí {{GENDER:|dovolil|dovolila|dovolili}} jednat vaším jménem. Pokud jste aplikaci {{GENDER:|dovolil|dovolila|dovolili}} jednat vaším jménem nezávisle na různých sesterských projektech, uvidíte níže oddělené konfigurace pro každý takový projekt.\n\nPřipojené aplikace přistupují k vašemu účtu pomocí protokolu OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth?uselang=cs Více informací o připojených aplikacích])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigace:",
+ "mwoauthmanagemygrants-showlist": "Seznam připojených aplikací",
+ "mwoauthmanagemygrants-none": "K vašemu účtu nejsou připojeny žádné aplikace.",
+ "mwoauthmanagemygrants-user": "Vydavatel:",
+ "mwoauthmanagemygrants-description": "Popis",
+ "mwoauthmanagemygrants-wikiallowed": "Povoleno na projektu:",
+ "mwoauthmanagemygrants-grants": "Použitelná oprávnění",
+ "mwoauthmanagemygrants-grantsallowed": "Přidělená oprávnění",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Přidělená použitelná oprávnění:",
+ "mwoauthmanagemygrants-review": "spravovat přístup",
+ "mwoauthmanagemygrants-revoke": "odebrat přístup",
+ "mwoauthmanagemygrants-grantaccept": "Přiděleno",
+ "mwoauthmanagemygrants-update-text": "Pomocí níže zobrazeného formuláře můžete změnit oprávnění přidělená aplikaci, aby mohla jednat vaším jménem.",
+ "mwoauthmanagemygrants-revoke-text": "Pomocí níže zobrazeného formuláře můžete odvolat oprávnění aplikaci jednat vaším jménem.",
+ "mwoauthmanagemygrants-confirm-legend": "Správa připojené aplikace",
+ "mwoauthmanagemygrants-update": "Aktualizovat oprávnění",
+ "mwoauthmanagemygrants-renounce": "Zrušit autorizaci",
+ "mwoauthmanagemygrants-action": "Změnit stav:",
+ "mwoauthmanagemygrants-confirm-submit": "Aktualizovat stav přístupového tokenu",
+ "mwoauthmanagemygrants-success-update": "Vaše nastavení této aplikace byla aktualizována.",
+ "mwoauthmanagemygrants-success-renounce": "Přístup této aplikace k vašemu účtu byl zrušen.",
+ "mwoauthmanagemygrants-basic-tooltip": "Proč nemohu změnit toto oprávnění? Toto oprávnění poskytuje vaší připojené aplikaci základní přístup, který potřebuje pro správnou funkci. Pokud nechcete, aby tato připojená aplikace tato oprávnění měla, měli byste jí odebrat přístup.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Proč nemohu změnit toto oprávnění? Pokud nechcete, aby tato připojená aplikace toto oprávnění měla, měli byste jí odebrat přístup.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Vaše}} editace provedené touto aplikací",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Vaše}} akce provedené touto aplikací",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|navrhl|navrhla}} konzumenta OAuth (klíč konzumenta $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|aktualizoval|aktualizovala}} konzumenta OAuth (klíč konzumenta $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|schválil|schválila}} konzumenta OAuth uživatele $3 (klíč konzumenta $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|zamítnul|zamítla}} konzumenta OAuth uživatele $3 (klíč konzumenta $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|zakázal|zakázala}} konzumenta OAuth uživatele $3 (klíč konzumenta $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 znovu {{GENDER:$2|povolil|povolila}} konzumenta OAuth uživatele $3 (klíč konzumenta $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|vytvořil|vytvořila}} soukromého konzumenta OAuth (klíč konzumenta $4)",
+ "log-action-filter-mwoauthconsumer": "Typ akce s konzumentem OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Schválení konzumenta OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Založení soukromého konzumenta OAuth",
+ "log-action-filter-mwoauthconsumer-disable": "Zakázání konzumenta OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Navržení konzumenta OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Znovupovolení konzumenta OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Zamítnutí konzumenta OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Aktualizace konzumenta OAuth",
+ "mwoauthconsumer-consumer-logpage": "Kniha konzumentů OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Protokol schválení, zamítnutí a zákazů registrovaných konzumentů OAuth.",
+ "mwoauth-bad-request-missing-params": "Omlouváme se, ale při konfiguraci této připojené aplikace se něco pokazilo. Pro pomoc s řešením kontaktujte vývojáře aplikace.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Chybí parametry OAuth, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Omlouváme se, něco se nepovedlo, pro pomoc s řešením budete muset kontaktovat autora aplikace.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Neznámé URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Omlouváme se, něco se pokazilo. Pro pomoc s řešením budete muset [$1 kontaktovat] autora aplikace.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Neznámé URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "K tomuto schválenému autorizačnímu tokenu nebylo nalezeno žádné schválené oprávnění.",
+ "mwoauthdatastore-request-token-not-found": "Omlouváme se, ale při připojování této aplikace se něco nepovedlo.\nVraťte se a zkuste znovu připojit svůj účet, nebo kontaktujte autora aplikace.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth token nebyl nalezen, $1</span>",
+ "mwoauthdatastore-callback-not-found": "Callback URL OAuth nebylo nalezeno v paměti. Bude to asi chyba ve způsobu, jakým aplikace posílá požadavky na server.",
+ "mwoauthdatastore-request-token-already-used": "Tento požadavek byl již dokončen a nelze ho odeslat podruhé. Vraťte se do aplikace a zkuste svůj účet připojit znovu nebo se obraťte na autora aplikace.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth token již použit, $1</span>",
+ "mwoauthdatastore-bad-token": "Žádný token odpovídající vašemu požadavku nebyl nalezen.",
+ "mwoauthdatastore-bad-source-ip": "Požadavek přišel z neplatné IP adresy.",
+ "mwoauthdatastore-bad-verifier": "Poskytnutý ověřovací kód nebyl platný.",
+ "mwoauthdatastore-invalid-token-type": "Požadovaný typ tokenu není platný.",
+ "mwoauthgrants-general-error": "Ve vašem OAuth požadavku byla chyba.",
+ "mwoauthserver-bad-consumer": "„$1“ není schválená Připojená aplikace. Pro pomoc [$2 kontaktujte] autora aplikace.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Připojená OAuth aplikace není schválena, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Omlouváme se, ale při připojování této aplikace se něco nepovedlo.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Neznámý klíč OAuth, $1</span>",
+ "mwoauthserver-insufficient-rights": "Váš účet nemá dovoleno využívat Připojené aplikace, pro zjištění důvodů kontaktujte správce vašeho serveru.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nedostatečná práva uživatele OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Váš požadavek obsahuje neplatný token.",
+ "mwoauthserver-invalid-user": "Abyste na tomto serveru {{GENDER:|mohl|mohla|mohli}} používat Připojené aplikace, musíte mít účet sjednocený přes všechny projekty. Jakmile budete mít sjednocený účet, můžete zkusit znovu připojit aplikaci „$1“.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Vyžadován sjednocený účet, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Omlouváme se, ale při připojování této aplikace se něco rozbilo.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konzument nemá tajný klíč, [https://www.mediawiki.org/wiki/Help:OAuth/Errors?uselang=cs#E009 E009]</span>",
+ "mwoauthserver-consumer-owner-only": "„$1“ je soukromá propojená aplikace. Chcete-li načíst přístupový token, vizte [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konzument je pouze pro vlastníka, $3</span>",
+ "mwoauth-invalid-authorization-title": "Chyba autorizace OAuth",
+ "mwoauth-invalid-authorization": "Autorizační hlavičky ve vašem požadavku nejsou platné: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Autorizační hlavičky ve vašem požadavku nejsou pro $1 platné",
+ "mwoauth-invalid-authorization-invalid-user": "Autorizační hlavičky ve vašem požadavku jsou pro uživatele, který zde neexistuje",
+ "mwoauth-invalid-authorization-wrong-user": "Autorizační hlavičky ve vašem požadavku jsou pro jiného uživatele",
+ "mwoauth-invalid-authorization-not-approved": "Aplikace, kterou chcete připojit, byla zřejmě chybně nastavena. Pro pomoc kontaktujte autora aplikace „$1“.",
+ "mwoauth-invalid-authorization-blocked-user": "Autorizační hlavičky ve vašem požadavku jsou pro uživatele, který je zablokován",
+ "mwoauth-form-description-allwikis": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\nAby mohla dokončit váš požadavek, potřebuje aplikace '''$2''' svolení, aby mohla vaším jménem na všech projektech tohoto webu provádět následující aktivity:\n\n$4",
+ "mwoauth-form-description-onewiki": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\nAby mohla dokončit váš požadavek, potřebuje aplikace '''$2''' svolení, aby mohla vaším jménem na ''{{grammar:6sg|$4}}'' provádět následující aktivity:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\nAby mohla dokončit váš požadavek, potřebuje aplikace '''$2''' svolení, aby mohla vaším jménem přistupovat k informacím na všech projektech tohoto webu. Vaším jménem nebudou prováděny žádné změny.",
+ "mwoauth-form-description-onewiki-nogrants": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\naplikace '''$2''' by chtěla získat základní přístup vaším jménem na ''{{grammar:6sg|$4}}''.\n\nAby mohla dokončit váš požadavek, potřebuje aplikace '''$2''' svolení, aby mohla vaším jménem přistupovat k informacím na ''{{grammar:6sg|$4}}''. Vaším jménem nebudou prováděny žádné změny.",
+ "mwoauth-form-description-allwikis-privateinfo": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\naby mohla aplikace '''$2''' dokončit váš požadavek, potřebuje svolení, aby směla na všech projektech tohoto webu přistupovat k informacím o vás zahrnujícím i vaše skutečné jméno a e-mailovou adresu. Vaším jménem nebudou prováděny žádné změny.",
+ "mwoauth-form-description-onewiki-privateinfo": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\naby mohla aplikace '''$2''' dokončit váš požadavek, potřebuje svolení, aby směla na „$4“ přistupovat k informacím zahrnujícím i vaše skutečné jméno a e-mailovou adresu. Vaším jménem nebudou prováděny žádné změny.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\naby mohla aplikace '''$2''' dokončit váš požadavek, potřebuje svolení, aby směla na všech projektech tohoto webu přistupovat k informacím o vás zahrnujícím i vaši e-mailovou adresu. Vaším jménem nebudou prováděny žádné změny.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "{{GENDER:$1|Uživateli|Uživatelko}} $1,\n\naby mohla aplikace '''$2''' dokončit váš požadavek, potřebuje svolení, aby směla na „$4“ přistupovat k informacím zahrnujícím i vaši e-mailovou adresu. Vaším jménem nebudou prováděny žádné změny.",
+ "mwoauth-form-button-approve": "Dovolit",
+ "mwoauth-form-button-cancel": "Storno",
+ "mwoauth-error": "Chyba připojení aplikace",
+ "mwoauth-grants-heading": "Vyžadovaná oprávnění:",
+ "mwoauth-grants-nogrants": "Tato aplikace nevyžaduje žádná oprávnění.",
+ "mwoauth-acceptance-cancelled": "{{GENDER:|Rozhodl|Rozhodla|Rozhodli}} jste se nedovolit aplikaci „$1“ přístup k vašemu účtu. „$1“ nebude fungovat, dokud jí přístup nedovolíte. Můžete jít zpět na „$1“, nebo [[Special:OAuthManageMyGrants|spravovat]] vaše připojené aplikace.",
+ "mwoauth-granttype-normal": "Žádat autorizaci pro konkrétní oprávnění.",
+ "grant-mwoauth-authonly": "Jen ověření identity uživatele, bez možnosti číst stránky či jednat uživatelovým jménem.",
+ "grant-mwoauth-authonlyprivate": "Jen ověření identity uživatele s přístupem ke skutečnému jménu a e-mailové adrese, bez možnosti číst stránky či jednat uživatelovým jménem.",
+ "mwoauth-listgrants-extra-summary": "== Svolení specifická pro OAuth ==\n\nTato doplňková svolení se používají pro konzumenty OAuth.",
+ "mwoauth-oauth-exception": "V protokolu OAuth došlo k chybě: $1",
+ "mwoauth-callback-not-oob": "oauth_callback musí být nastaven, a to na hodnotu „oob“ (malými písmeny)",
+ "mwoauth-callback-not-oob-or-prefix": "Musí být nastaven oauth_callback a buď musí mít hodnotu „oob“ (citlivé na velikost písmen), nebo musí být nakonfigurovaný callback prefixem uvedeného callbacku.",
+ "right-mwoauthproposeconsumer": "Navrhování nových konzumentů OAuth",
+ "right-mwoauthupdateownconsumer": "Upravování konzumentů OAuth, které spravujete",
+ "right-mwoauthmanageconsumer": "Správa konzumentů OAuth",
+ "right-mwoauthsuppress": "Utajování konzumentů OAuth",
+ "right-mwoauthviewsuppressed": "Zobrazování utajených konzumentů OAuth",
+ "right-mwoauthviewprivate": "Zobrazování soukromých dat OAuth",
+ "right-mwoauthmanagemygrants": "Správa přístupových oprávnění OAuth",
+ "action-mwoauthmanageconsumer": "spravovat konzumenty OAuth",
+ "action-mwoauthmanagemygrants": "spravovat vámi přidělená oprávnění OAuth",
+ "action-mwoauthproposeconsumer": "navrhovat nové konzumenty OAuth",
+ "action-mwoauthupdateownconsumer": "upravovat konzumenty OAuth, které spravujete",
+ "action-mwoauthviewprivate": "prohlížet si soukromé údaje OAuth",
+ "action-mwoauthviewsuppressed": "prohlížet si utajené konzumenty OAuth",
+ "mwoauth-tag-reserved": "Značky začínající <code>OAuth CID:</code> jsou vyhrazeny pro OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Poznámka:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> je bezpečnější než hesla robotů a měl by být preferován, kdykoliv to robot preferuje.",
+ "mwoauth-api-module-disabled": "Modul „$1“ není dostupný prostřednictvím OAuth.",
+ "echo-category-title-oauth-owner": "vývoj OAuth",
+ "echo-pref-tooltip-oauth-owner": "Upozorněte mě na události týkající aplikací OAuth, které jsem {{GENDER:|vytvořil|vytvořila}}.",
+ "echo-category-title-oauth-admin": "správu OAuth",
+ "echo-pref-tooltip-oauth-admin": "Upozorněte mě na události týkající se schvalování aplikací OAuth.",
+ "notification-oauth-app-body": "Zdůvodnění: $1"
+}
diff --git a/OAuth/i18n/da.json b/OAuth/i18n/da.json
new file mode 100644
index 00000000..94877d8e
--- /dev/null
+++ b/OAuth/i18n/da.json
@@ -0,0 +1,67 @@
+{
+ "@metadata": {
+ "authors": [
+ "Knud Winckelmann",
+ "Kranix",
+ "Saederup92",
+ "Sarrus",
+ "Weblars"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Forbundne apps:",
+ "mwoauth-prefs-managegrantslink": "Håndtér {{PLURAL:$1|forbundet applikation|$1 forbundne applikationer|0=forbundne applikationer}}",
+ "mwoauth-consumer-allwikis": "Alle projekter på denne side",
+ "mwoauth-consumer-name": "Applikationsnavn:",
+ "mwoauth-consumer-user": "Udgiver:",
+ "mwoauth-consumer-stage": "Nuværende status:",
+ "mwoauth-consumer-description": "Beskrivelse af applikationen:",
+ "mwoauth-consumer-wiki-thiswiki": "Nuværende projekt ($1)",
+ "mwoauth-consumer-reason": "Begrundelse:",
+ "mwoauth-consumer-stage-rejected": "afvist",
+ "mwoauth-consumer-stage-expired": "udløbet",
+ "mwoauth-consumer-stage-approved": "godkendt",
+ "mwoauth-consumer-stage-disabled": "deaktiveret",
+ "mwoauthconsumerregistration-navigation": "Navigering:",
+ "mwoauthconsumerregistration-user": "Udgiver",
+ "mwoauthconsumerregistration-description": "Beskrivelse",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Seneste ændring",
+ "mwoauthmanageconsumers-notloggedin": "Du skal være logget på for at tilgå denne side.",
+ "mwoauthmanageconsumers-type": "Køer:",
+ "mwoauthmanageconsumers-name": "Kunde",
+ "mwoauthmanageconsumers-user": "Udgiver",
+ "mwoauthmanageconsumers-description": "Beskrivelse",
+ "mwoauthmanageconsumers-lastchange": "Seneste ændring",
+ "mwoauthmanageconsumers-approve": "Godkendt",
+ "mwoauthmanageconsumers-reject": "Afvist",
+ "mwoauthmanageconsumers-disable": "Deaktiveret",
+ "mwoauthmanageconsumers-reason": "Begrundelse:",
+ "mwoauthlistconsumers-view": "detaljer",
+ "mwoauthlistconsumers-user": "Udgiver",
+ "mwoauthlistconsumers-description": "Beskrivelse",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauthlistconsumers-status-disabled": "deaktiveret",
+ "mwoauthlistconsumers-status-rejected": "afvist",
+ "mwoauthlistconsumers-status-expired": "udgået",
+ "oauthmanagemygrants": "Administrer forbundne applikationer",
+ "mwoauthmanagemygrants-text": "Denne side lister alle applikationer som kan anvende din konto. For enhver sådan applikation er tilgangen begrænset af de rettigheder som du godkendte, da du valgte at lade den agere på dine vegne. Hvis du separat har godkendt en applikation at tilgå forskellige søsterprojekter på dine vegne, vil du se en særskilt konfiguration for hver sådanne projekter nedenfor.\n\nForbundne applikationer kan få tilgang til din konto via OAuth-protokollen. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Læs mere om forbundne applikationer])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigering:",
+ "mwoauthmanagemygrants-showlist": "Liste over forbundne applikationer",
+ "mwoauthmanagemygrants-user": "Udgiver:",
+ "mwoauthmanagemygrants-description": "Beskrivelse",
+ "mwoauthmanagemygrants-wikiallowed": "Tilladt på projektet:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Aktuelt tilladte rettigheder:",
+ "mwoauthmanagemygrants-review": "håndter tilgang",
+ "mwoauthmanagemygrants-revoke": "annuller tilgang",
+ "mwoauthmanagemygrants-update-text": "Anvend formularboksen nedenfor til at ændre de rettigheder som en applikation har for at agere på dine vegne.",
+ "mwoauthmanagemygrants-confirm-legend": "Håndtér forbundet applikation",
+ "mwoauthmanagemygrants-update": "Opdater tilladelser",
+ "mwoauthmanagemygrants-renounce": "Annuller tilgang",
+ "mwoauthmanagemygrants-success-update": "Dine indstillinger for denne applikation er blevet opdateret.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Dine}} redigeringer via denne applikation",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Dine}} handlinger via denne applikation",
+ "mwoauthdatastore-bad-source-ip": "Forespørgslen kom fra en ugyldig IP-adresse.",
+ "mwoauth-form-button-approve": "Tillad",
+ "mwoauth-form-button-cancel": "Annullér",
+ "notification-oauth-app-body": "Begrundelse: $1"
+}
diff --git a/OAuth/i18n/de-formal.json b/OAuth/i18n/de-formal.json
new file mode 100644
index 00000000..c33df531
--- /dev/null
+++ b/OAuth/i18n/de-formal.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Umherirrender"
+ ]
+ },
+ "mwoauthmanagemygrants-none": "Es sind keine Anwendungen mit Ihrem Benutzerkonto verbunden."
+}
diff --git a/OAuth/i18n/de.json b/OAuth/i18n/de.json
new file mode 100644
index 00000000..5d06037a
--- /dev/null
+++ b/OAuth/i18n/de.json
@@ -0,0 +1,302 @@
+{
+ "@metadata": {
+ "authors": [
+ "ChrisiPK",
+ "Kghbln",
+ "Killarnee",
+ "Metalhead64",
+ "Se4598",
+ "Umherirrender",
+ "Wnme"
+ ]
+ },
+ "mwoauth-desc": "Ermöglicht die Nutzung von OAuth 1.0a und OAuth 2.0 zur API-Autorisierung",
+ "mwoauth-verified": "Die Anwendung ist jetzt berechtigt, in deinem Namen auf MediaWiki zuzugreifen.\n\nUm den Prozess abzuschließen, gib diesen Verifizierungswert an die Anwendung weiter: '''$1'''",
+ "mwoauth-db-readonly": "Die OAuth-Datenbank ist vorübergehend gesperrt. Bitte in einigen Minuten erneut versuchen.",
+ "mwoauth-missing-field": "Fehlender Wert für das Feld „$1“",
+ "mwoauth-invalid-field": "Für das Feld „$1“ wurde ein ungültiger Wert angegeben",
+ "mwoauth-invalid-field-generic": "Ungültigen Wert angegeben",
+ "mwoauth-field-hidden": "(diese Information ist versteckt)",
+ "mwoauth-field-private": "(diese Information ist privat)",
+ "mwoauth-prefs-managegrants": "Verbundene Anwendungen:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|Eine verbundene Anwendung|$1 verbundene Anwendungen|0=Verbundene Anwendungen}} verwalten",
+ "mwoauth-consumer-allwikis": "Alle Projekte dieser Website",
+ "mwoauth-consumer-key": "Verbraucherschlüssel:",
+ "mwoauth-consumer-name": "Anwendungsname:",
+ "mwoauth-consumer-version": "Verbraucherversion:",
+ "mwoauth-consumer-user": "Herausgeber:",
+ "mwoauth-consumer-stage": "Aktueller Status:",
+ "mwoauth-consumer-email": "Kontakt-E-Mail-Adresse:",
+ "mwoauth-consumer-email-help": "Nur sichtbar für jene, die neue Verbraucher bestätigen.",
+ "mwoauth-consumer-owner-only-label": "Nur Besitzer:",
+ "mwoauth-consumer-owner-only": "Dieser Verbraucher ist nur für die Verwendung durch $1.",
+ "mwoauth-consumer-owner-only-help": "Die Auswahl dieser Option verursacht die automatische Bestätigung und Akzeptierung des Verbrauchers für die Verwendung durch $1. Er wird für andere Benutzer nicht nutzbar sein und der übliche Autorisierungsablauf wird nicht funktionieren. Aufgenommene Aktionen mit diesem Verbraucher werden nicht markiert.",
+ "mwoauth-consumer-description": "Anwendungsbeschreibung:",
+ "mwoauth-consumer-callbackurl": "OAuth-Callback-URL:",
+ "mwoauth-consumer-callbackisprefix": "Dem Verbraucher die Angabe eines Callbacks in Anfragen ermöglichen und die obige Callback-URL als ein erforderliches Präfix verwenden.",
+ "mwoauth-consumer-granttypes": "Typen der angefragten Berechtigungen:",
+ "mwoauth-consumer-grantsneeded": "Anwendbare Berechtigungen:",
+ "mwoauth-consumer-required-grant": "An Verbraucher anwendbar",
+ "mwoauth-consumer-wiki": "Anwendbares Projekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Aktuelles Projekt ($1)",
+ "mwoauth-consumer-restrictions": "Benutzungsbeschränkungen:",
+ "mwoauth-consumer-restrictions-json": "Benutzungsbeschränkungen (JSON):",
+ "mwoauth-consumer-rsakey": "Öffentlicher RSA-Schlüssel (optional):",
+ "mwoauth-consumer-rsakey-help": "Gib einen öffentlichen Schlüssel an, um die RSA-SHA1-Signaturmethode zu verwenden. Lasse das Feld leer, um HMAC-SHA1 mit einem zufälligen Geheimnis zu verwenden. Falls du dir nicht sicher bist, lasse das Feld leer.",
+ "mwoauth-consumer-secretkey": "Geheimer Verbrauchertoken:",
+ "mwoauth-consumer-accesstoken": "Zugriffstoken:",
+ "mwoauth-consumer-reason": "Grund:",
+ "mwoauth-consumer-developer-agreement": "Durch die Übermittlung dieser Anwendung bestätigst du, dass wir uns das Recht vorbehalten, deine Anwendung zu deaktivieren, entfernen oder den Zugriff deiner Anwendung auf diese Website beschränken können und verfolgen andere Kurse von Aktionen, von denen wir in unserer Beurteilung glauben, dass du oder deine Anwendung eine Richtlinie oder ein führendes Prinzip dieser Website verletzt. Wir können diese Anwendungsrichtlinie jederzeit ohne vorherige Ankündigung nach unserem Ermessen ändern und wie wir es für notwendig erachten. Deine fortgesetzte Verwendung von OAuth stellt eine Akzeptanz dieser Änderungen dar.",
+ "mwoauth-consumer-email-unconfirmed": "Die E-Mail-Adresse deines Benutzerkontos wurde noch nicht bestätigt.",
+ "mwoauth-consumer-email-mismatched": "Die angegebene E-Mail-Adresse muss mit der deines Benutzerkontos übereinstimmen.",
+ "mwoauth-consumer-alreadyexists": "Ein Verbraucher mit dieser Namen-/Versions-/Herausgeberkombination ist bereits vorhanden",
+ "mwoauth-consumer-alreadyexistsversion": "Ein Verbraucher mit dieser Namen-/Herausgeber-Kombination ist bereits mit einer gleichen oder höheren Version vorhanden („$1“)",
+ "mwoauth-consumer-not-accepted": "Die Informationen für einen ausstehenden Verbraucherantrag konnten nicht aktualisiert werden",
+ "mwoauth-consumer-not-proposed": "Der Verbraucher ist derzeit nicht geplant",
+ "mwoauth-consumer-not-disabled": "Der Verbraucher ist derzeit nicht deaktiviert",
+ "mwoauth-consumer-not-approved": "Der Verbraucher ist nicht bestätigt (vielleicht wurde er deaktiviert)",
+ "mwoauth-missing-consumer-key": "Es wurde kein Verbraucherschlüssel angegeben.",
+ "mwoauth-invalid-consumer-key": "Es ist kein Verbraucher mit dem angegebenen Schlüssel vorhanden.",
+ "mwoauth-invalid-access-token": "Es ist kein Zugriffstoken mit dem angegebenen Schlüssel vorhanden.",
+ "mwoauth-invalid-access-wrongwiki": "Der Verbraucher kann nur auf dem Projekt „$1“ verwendet werden.",
+ "mwoauth-consumer-conflict": "Ein anderer hat bereits die Attribute dieses Verbrauchers geändert. Bitte erneut versuchen. Du kannst auch das Änderungs-Logbuch überprüfen.",
+ "mwoauth-consumer-grantshelp": "Jede Berechtigung ermöglicht einen Zugriff auf gelistete Benutzerrechte, die das Benutzerkonto bereits hat. Siehe die [[Special:ListGrants|tabellarische Übersicht]] für mehr Informationen.",
+ "mwoauth-consumer-stage-proposed": "geplant",
+ "mwoauth-consumer-stage-rejected": "abgelehnt",
+ "mwoauth-consumer-stage-expired": "abgelaufen",
+ "mwoauth-consumer-stage-approved": "bestätigt",
+ "mwoauth-consumer-stage-disabled": "deaktiviert",
+ "mwoauth-consumer-stage-suppressed": "unterdrückt",
+ "oauthconsumerregistration": "OAuth-Anwendungsregistrierung",
+ "mwoauthconsumerregistration-navigation": "Navigation:",
+ "mwoauthconsumerregistration-propose": "Neuen Verbraucher planen",
+ "mwoauthconsumerregistration-list": "Meine Verbraucherliste",
+ "mwoauthconsumerregistration-main": "Start",
+ "mwoauthconsumerregistration-propose-text": "Entwickler sollten das unten stehende Formular benutzen, um einen neuen OAuth-Verbraucher zu planen (siehe die [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth Erweiterungsdokumentation] für Einzelheiten). Nach dem Abschicken dieses Formulars erhältst du einen Token, der von deiner Anwendung zur Identifizierung für MediaWiki verwendet wird. Ein OAuth-Administrator muss deine Anwendung bestätigen, bevor sie von anderen Benutzern autorisiert werden kann.\n\nHier einige Empfehlungen und Bemerkungen:\n* Versuche, so wenig Berechtigungen wie möglich zu verwenden. Vermeide Berechtigungen, die in Wirklichkeit nicht benötigt werden.\n* Versionen haben die Form „Hauptversion.Nebenversion.Release“ (die letzten zwei sind optional) und steigen mit der Notwendigkeit von Berechtigungsänderungen an.\n* Bitte gib einen öffentlichen RSA-Schlüssel an (im PEM-Format), falls möglich. Anderenfalls muss ein wenig sicherer Geheimtoken benutzt werden.\n* Du kannst eine Projektkennung verwenden, um den Verbraucher auf ein einzelnes Projekt auf dieser Website zu beschränken (verwende „*“ für alle Projekte).",
+ "mwoauthconsumerregistration-update-text": "Verwende das unten stehende Formular, um Aspekte eines von dir kontrollierten OAuth-Verbrauchers zu aktualisieren.\n\nAlle Werte hier überschreiben alle vorherigen. Hinterlasse keine leeren Felder, außer du beabsichtigst, diese Werte zu löschen.",
+ "mwoauthconsumerregistration-maintext": "Diese Seite dient zum Vorschlagen und Aktualisieren von OAuth-Anwendungen in der Websiteregistrierung durch Entwickler.\n\nDu kannst hier\n* [[Special:OAuthConsumerRegistration/propose|einen Token für eine neue Anwendung anfordern]] oder\n* [[Special:OAuthConsumerRegistration/list|deine vorhandenen Anwendungen verwalten]].\n\nFür mehr Informationen über OAuth, siehe die [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth Erweiterungsdokumentation].",
+ "mwoauthconsumerregistration-propose-legend": "Neue OAuth-Verbraucheranwendung",
+ "mwoauthconsumerregistration-update-legend": "OAuth-Verbraucheranwendung aktualisieren",
+ "mwoauthconsumerregistration-propose-submit": "Verbraucher planen",
+ "mwoauthconsumerregistration-update-submit": "Verbraucher aktualisieren",
+ "mwoauthconsumerregistration-none": "Du kontrollierst keine OAuth-Anwendungen.",
+ "mwoauthconsumerregistration-name": "Verbraucher",
+ "mwoauthconsumerregistration-user": "Herausgeber",
+ "mwoauthconsumerregistration-description": "Beschreibung",
+ "mwoauthconsumerregistration-email": "Kontakt-E-Mail",
+ "mwoauthconsumerregistration-consumerkey": "Verbraucherschlüssel",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Letzte Änderung",
+ "mwoauthconsumerregistration-manage": "verwalten",
+ "mwoauthconsumerregistration-resetsecretkey": "Den geheimen Schlüssel auf einen neuen Wert zurücksetzen",
+ "mwoauthconsumerregistration-proposed": "Wir haben deinen OAuth-Verbraucherantrag erhalten.\n\nDir wurde der Verbrauchertoken '''$1''' und der Geheimtoken '''$2''' zugewiesen. ''Bitte diese für die Zukunft aufbewahren.''",
+ "mwoauthconsumerregistration-created-owner-only": "Dein OAuth-Verbraucher wurde erstellt.\n\nDeine Tokens sind:\n; Verbrauchertoken: $1\n; Verbrauchergeheimnis: $2\n; Zugriffstoken: $3\n; Zugriffsgeheimnis: $4\n<em>Bitte bewahre dies für zukünftige Zwecke auf.</em>",
+ "mwoauthconsumerregistration-updated": "Deine OAuth-Verbraucherregistrierung wurde erfolgreich aktualisiert.",
+ "mwoauthconsumerregistration-secretreset": "Dir wurde der geheime Verbrauchertoken '''$1''' zugeordnet. ''Bitte diesen für die Zukunft aufbewahren.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Deine OAuth-Verbrauchertokens wurden zurückgesetzt. Die neuen Tokens sind:\n; Verbrauchertoken: $1\n; Verbrauchergeheimnis: $2\n; Zugriffstoken: $3\n; Zugriffsgeheimnis: $4\n<em>Bitte bewahre dies für zukünftige Zwecke auf.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Du musst deine E-Mail-Adresse bestätigen, bevor du OAuth-Anwendungen erstellst.\nBitte hinterlege und bestätige deine E-Mail-Adresse in deinen [[Special:Preferences|Benutzereinstellungen]].",
+ "oauthmanageconsumers": "OAuth-Anwendungen verwalten",
+ "mwoauthmanageconsumers-notloggedin": "Du musst angemeldet sein, um auf diese Seite zugreifen zu können.",
+ "mwoauthmanageconsumers-type": "Warteschlangen:",
+ "mwoauthmanageconsumers-showproposed": "Geplante Anträge",
+ "mwoauthmanageconsumers-showrejected": "Abgelehnte Anträge",
+ "mwoauthmanageconsumers-showexpired": "Abgelaufene Anträge",
+ "mwoauthmanageconsumers-linkproposed": "eingereichte Anfragen",
+ "mwoauthmanageconsumers-linkrejected": "abgelehnte Anfragen",
+ "mwoauthmanageconsumers-linkexpired": "abgelaufene Anfragen",
+ "mwoauthmanageconsumers-linkapproved": "genehmigte Anfragen",
+ "mwoauthmanageconsumers-linkdisabled": "deaktivierte Anfragen",
+ "mwoauthmanageconsumers-main": "Start",
+ "mwoauthmanageconsumers-maintext": "Diese Seite ist zur Abwicklung von OAuth-Anwendungsanträgen (siehe http://oauth.net) und zum Verwalten von bestehenden OAuth-Anwendungen gedacht.",
+ "mwoauthmanageconsumers-queues": "Wähle von unten eine Verbraucherbestätigungswarteschlange aus:",
+ "mwoauthmanageconsumers-q-proposed": "Warteschlange geplanter Verbraucheranträge",
+ "mwoauthmanageconsumers-q-rejected": "Warteschlange abgelehnter Verbraucheranträge",
+ "mwoauthmanageconsumers-q-expired": "Warteschlange abgelaufener Verbraucheranträge",
+ "mwoauthmanageconsumers-lists": "Wähle von unten eine Verbraucherstatusliste aus:",
+ "mwoauthmanageconsumers-l-approved": "Liste derzeit bestätigter Verbraucher",
+ "mwoauthmanageconsumers-l-disabled": "Liste derzeit deaktivierter Verbraucher",
+ "mwoauthmanageconsumers-none-proposed": "In dieser Liste gibt es keine geplanten Verbraucher.",
+ "mwoauthmanageconsumers-none-rejected": "In dieser Liste gibt es keine geplanten Verbraucher.",
+ "mwoauthmanageconsumers-none-expired": "In dieser Liste gibt es keine geplanten Verbraucher.",
+ "mwoauthmanageconsumers-none-approved": "Keine Verbraucher entsprechen diesen Kriterien.",
+ "mwoauthmanageconsumers-none-disabled": "Keine Verbraucher entsprechen diesen Kriterien.",
+ "mwoauthmanageconsumers-name": "Verbraucher",
+ "mwoauthmanageconsumers-user": "Herausgeber",
+ "mwoauthmanageconsumers-description": "Beschreibung",
+ "mwoauthmanageconsumers-email": "Kontakt-E-Mail",
+ "mwoauthmanageconsumers-consumerkey": "Verbraucherschlüssel",
+ "mwoauthmanageconsumers-lastchange": "Letzte Änderung",
+ "mwoauthmanageconsumers-review": "überprüfen/verwalten",
+ "mwoauthmanageconsumers-confirm-text": "Benutze dieses Formular, um diesen Verbraucher zu bestätigen, abzulehnen, zu deaktivieren oder zu reaktivieren.",
+ "mwoauthmanageconsumers-confirm-legend": "OAuth-Anwendung verwalten",
+ "mwoauthmanageconsumers-action": "Status ändern:",
+ "mwoauthmanageconsumers-approve": "Bestätigt",
+ "mwoauthmanageconsumers-reject": "Abgelehnt",
+ "mwoauthmanageconsumers-rsuppress": "Abgelehnt und unterdrückt",
+ "mwoauthmanageconsumers-disable": "Deaktiviert",
+ "mwoauthmanageconsumers-dsuppress": "Deaktiviert und unterdrückt",
+ "mwoauthmanageconsumers-reenable": "Bestätigt",
+ "mwoauthmanageconsumers-reason": "Grund:",
+ "mwoauthmanageconsumers-confirm-submit": "Verbraucherstatus aktualisieren",
+ "mwoauthmanageconsumers-success-approved": "Der Antrag wurde bestätigt.",
+ "mwoauthmanageconsumers-success-rejected": "Der Antrag wurde abgelehnt.",
+ "mwoauthmanageconsumers-success-disabled": "Der Verbraucher wurde deaktiviert.",
+ "mwoauthmanageconsumers-success-reanable": "Der Verbraucher wurde reaktiviert.",
+ "mwoauthmanageconsumers-search-name": "Verbraucher mit diesem Namen",
+ "mwoauthmanageconsumers-search-publisher": "Verbraucher von diesem Benutzer",
+ "oauthlistconsumers": "Liste der OAuth-Anwendungen",
+ "mwoauthlistconsumers-legend": "OAuth-Anwendungen durchsuchen",
+ "mwoauthlistconsumers-view": "Einzelheiten",
+ "mwoauthlistconsumers-none": "Es wurden keine Anwendungen gefunden, die diesen Kriterien entsprechen.",
+ "mwoauthlistconsumers-name": "Anwendungsname",
+ "mwoauthlistconsumers-version": "Verbraucherversion",
+ "mwoauthlistconsumers-user": "Herausgeber",
+ "mwoauthlistconsumers-description": "Beschreibung",
+ "mwoauthlistconsumers-wiki": "Anwendbares Projekt",
+ "mwoauthlistconsumers-callbackurl": "OAuth-Callback-URL",
+ "mwoauthlistconsumers-callbackisprefix": "Dem Verbraucher die Angabe eines Callbacks in Anfragen ermöglichen und die obige Callback-URL als ein erforderliches Präfix verwenden.",
+ "mwoauthlistconsumers-grants": "Anwendbare Berechtigungen",
+ "mwoauthlistconsumers-basicgrantsonly": "(nur Basiszugriff)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "alle",
+ "mwoauthlistconsumers-status-proposed": "geplant",
+ "mwoauthlistconsumers-status-approved": "bestätigt",
+ "mwoauthlistconsumers-status-disabled": "deaktiviert",
+ "mwoauthlistconsumers-status-rejected": "abgelehnt",
+ "mwoauthlistconsumers-status-expired": "abgelaufen",
+ "mwoauthlistconsumers-navigation": "Navigation:",
+ "mwoauthlistconsumers-grants-link": "Rechte verwalten",
+ "mwoauthlistconsumers-rclink": "Letzte Änderungen durch diese Anwendung",
+ "oauthmanagemygrants": "Verbundene Anwendungen verwalten",
+ "mwoauthmanagemygrants-text": "Diese Seite listet alle Anwendungen auf, die dein Benutzerkonto verwenden können. Für jede Anwendung ist der Zugriffsbereich durch die von dir gewährten Berechtigungen beschränkt, wenn du die Anwendung zum Handeln auf deinen Namen autorisiert hast. Falls du eine Anwendung separat autorisiert hast, um auf unterschiedliche Schwesterprojekte zuzugreifen, dann wirst du unten separate Konfigurationen für jedes Projekt sehen.\n\nVerbundene Anwendungen greifen auf dein Benutzerkonto durch Verwendung eines OAuth-Protokolls zu. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Mehr über verbundene Anwendungen erfahren])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigation:",
+ "mwoauthmanagemygrants-showlist": "Liste verbundener Anwendungen",
+ "mwoauthmanagemygrants-none": "Es sind keine Anwendungen mit deinem Benutzerkonto verbunden.",
+ "mwoauthmanagemygrants-user": "Herausgeber:",
+ "mwoauthmanagemygrants-description": "Beschreibung",
+ "mwoauthmanagemygrants-wikiallowed": "Erlaubt auf Projekt:",
+ "mwoauthmanagemygrants-grants": "Anwendbare Berechtigungen",
+ "mwoauthmanagemygrants-grantsallowed": "Erlaubte Berechtigungen:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Erlaubte anwendbare Berechtigungen:",
+ "mwoauthmanagemygrants-review": "Zugriff verwalten",
+ "mwoauthmanagemygrants-revoke": "Zugriff entziehen",
+ "mwoauthmanagemygrants-grantaccept": "Gewährt",
+ "mwoauthmanagemygrants-update-text": "Benutze das unten stehende Formular, um die gewährten Berechtigungen für eine Anwendung zu ändern, die in deinem Namen handelt.",
+ "mwoauthmanagemygrants-revoke-text": "Benutze das unten stehende Formular, um den Zugriff für eine Anwendung zu entziehen, die auf deinen Namen handelt.",
+ "mwoauthmanagemygrants-confirm-legend": "Verbundene Anwendung verwalten",
+ "mwoauthmanagemygrants-update": "Berechtigungen aktualisieren",
+ "mwoauthmanagemygrants-renounce": "Deautorisieren",
+ "mwoauthmanagemygrants-action": "Status ändern:",
+ "mwoauthmanagemygrants-confirm-submit": "Zugriffstokenstatus aktualisieren",
+ "mwoauthmanagemygrants-success-update": "Deine Einstellungen für diese Anwendung wurden aktualisiert.",
+ "mwoauthmanagemygrants-success-renounce": "Der Anwendungszugriff auf dein Benutzerkonto wurde widerrufen.",
+ "mwoauthmanagemygrants-basic-tooltip": "Warum kann ich diese Berechtigung nicht aktualisieren? Diese Berechtigung gibt deiner verbundenen Anwendung Basisrechte, die erforderlich sind, um einwandfrei funktionieren zu können. Wenn diese verbundene Anwendung diese Rechte nicht haben soll, solltest du den Zugriff der Anwendung entziehen.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Warum kann ich diese Berechtigung nicht aktualisieren? Falls du nicht möchtest, dass diese verbundene Anwendung dieses Recht hat, solltest du den Anwendungszugriff entziehen.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Deine}} Bearbeitungen durch diese Anwendung",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Deine}} Aktionen durch diese Anwendung",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|plante}} einen OAuth-Verbraucher (Verbraucherschlüssel $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|aktualisierte}} einen OAuth-Verbraucher (Verbraucherschlüssel $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|bestätigte}} einen OAuth-Verbraucher von $3 (Verbraucherschlüssel $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|lehnte}} einen OAuth-Verbraucher von $3 ab (Verbraucherschlüssel $4)",
+ "logentry-mwoauthconsumer-disable": "$1 deaktivierte einen OAuth-Verbraucher von $3 (Verbraucherschlüssel $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|reaktivierte}} einen OAuth-Verbraucher von $3 (Verbraucherschlüssel $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|erstellte}} einen Nur-Besitzer-OAuth-Verbraucher (Verbraucherschlüssel $4)",
+ "log-action-filter-mwoauthconsumer": "Typ der OAuth-Verbraucheraktion:",
+ "log-action-filter-mwoauthconsumer-approve": "OAuth-Verbraucherbestätigung",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "OAuth-Verbrauchererstellung (nur Besitzer)",
+ "log-action-filter-mwoauthconsumer-disable": "OAuth-Verbraucherdeaktivierung",
+ "log-action-filter-mwoauthconsumer-propose": "OAuth-Verbraucherplanung",
+ "log-action-filter-mwoauthconsumer-reenable": "Erneute OAuth-Verbraucheraktivierung",
+ "log-action-filter-mwoauthconsumer-reject": "OAuth-Verbraucherablehnung",
+ "log-action-filter-mwoauthconsumer-update": "OAuth-Verbraucheraktualisierung",
+ "mwoauthconsumer-consumer-logpage": "OAuth-Anwendungs-Logbuch",
+ "mwoauthconsumer-consumer-logpagetext": "Logbuch von Bestätigungen, Ablehnungen und Deaktivierungen registrierter OAuth-Verbraucher.",
+ "mwoauth-bad-request-missing-params": "Leider ist etwas mit der Konfiguration dieser verbundenen Anwendung schief gelaufen. <span class=\"plainlinks\">[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Kontaktiere den Support]</span>, um Hilfe bei der Behebung zu erhalten.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Fehlende Parameter, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Leider ist etwas schief gelaufen. Du wirst den Anwendungsautor kontaktieren müssen, um Hilfe für dieses Problem zu erhalten.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unbekannte URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Leider ist etwas schief gelaufen. Du wirst den Anwendungsautor [$1 kontaktieren] müssen, um Hilfe für dieses Problem zu erhalten.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unbekannte URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Für diesen Autorisierungstoken wurde keine bestätigte Berechtigung gefunden",
+ "mwoauthdatastore-request-token-not-found": "Bei der Verbindung dieser Anwendung ist leider etwas schief gelaufen.\nGehe zurück und versuche, dein Benutzerkonto erneut zu verbinden oder kontaktiere den Anwendungsautor.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-Token nicht gefunden, $1</span>",
+ "mwoauthdatastore-callback-not-found": "OAuth-Callback-URL nicht im Cache gefunden. Dies ist vermutlich ein Fehler, wie die Anwendung Anfragen an den Server stellt.",
+ "mwoauthdatastore-request-token-already-used": "Diese Anfrage wurde bereits abgeschlossen und kann nicht erneut eingereicht werden.\nGehe zurück zur Anwendung und versuche, dein Benutzerkonto neu zu verbinden oder kontaktiere den Autor der Anwendung.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-Token bereits verwendet, $1</span>",
+ "mwoauthdatastore-bad-token": "Es wurde kein Token gefunden, der deiner Anfrage entspricht.",
+ "mwoauthdatastore-bad-source-ip": "Die Anfrage kam von einer ungültigen IP-Adresse.",
+ "mwoauthdatastore-bad-verifier": "Der angegebene Verifikationscode war nicht gültig",
+ "mwoauthdatastore-invalid-token-type": "Der angeforderte Tokentyp ist ungültig",
+ "mwoauthgrants-general-error": "In deiner OAuth-Anfrage gab es einen Fehler",
+ "mwoauthserver-bad-consumer": "„$1“ ist nicht als verbundene Anwendung bestätigt. Um Hilfe zu erhalten, [$2 kontaktiere] den Anwendungsautor.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Verbundene OAuth-Anwendung nicht bestätigt, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Bei der Verbindung dieser Anwendung ist leider etwas schief gelaufen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unbekannter OAuth-Schlüssel, $1</span>",
+ "mwoauthserver-insufficient-rights": "Dein Benutzerkonto ist nicht berechtigt, verbundene Anwendungen zu verwenden. Kontaktiere deinen Websiteadministrator, um den Grund herauszufinden.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nicht ausreichende OAuth-Benutzerrechte, $1</span>",
+ "mwoauthserver-invalid-request-token": "Deine Anfrage enthält einen ungültigen Token",
+ "mwoauthserver-invalid-user": "Um verbundene Anwendungen auf dieser Website zu verwenden, musst du ein Benutzerkonto für alle Projekte besitzen. Falls du ein Benutzerkonto auf allen Projekten besitzt, kannst du versuchen, $1 erneut zu verbinden.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Einheitliche Anmeldung erforderlich, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Leider ist etwas bei der Verbindung dieser Anwendung fehlgeschlagen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Verbraucher hat keinen geheimen Schlüssel, $1</span>",
+ "mwoauthserver-consumer-owner-only": "„$1“ ist eine verbundene Nur-Besitzer-Anwendung. Um den Zugriffstoken abzurufen, siehe [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Verbraucher ist nur für den Besitzer, $3</span>",
+ "mwoauth-invalid-authorization-title": "OAuth-Autorisierungsfehler",
+ "mwoauth-invalid-authorization": "Die Autorisierungsheader in deiner Anfrage sind nicht gültig: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Die Autorisierungsheader in deiner Anfrage sind nicht gültig für $1",
+ "mwoauth-invalid-authorization-invalid-user": "Die Autorisierungsheader in deiner Anfrage sind für einen Benutzer, der hier nicht vorhanden ist.",
+ "mwoauth-invalid-authorization-wrong-user": "Die Autorisierungsheader in deiner Anfrage sind für einen anderen Benutzer",
+ "mwoauth-invalid-authorization-not-approved": "Die Anwendung, die du verbinden willst, scheint nicht korrekt konfiguriert zu sein. Kontaktiere für Hilfe den Autor von $1.",
+ "mwoauth-invalid-authorization-blocked-user": "Die Autorisierungsheader in deiner Anfrage sind für einen Benutzer, der gesperrt ist.",
+ "mwoauth-form-description-allwikis": "Hallo $1,\n\num deine Anfrage abzuschließen, benötigt '''$2''' die Berechtigung, um die folgenden Aktionen in deinem Namen auf allen Projekten dieser Website auszuführen:\n\n$4.",
+ "mwoauth-form-description-onewiki": "Hallo $1,\n\num deine Anfrage abzuschließen, benötigt '''$2''' die Berechtigung, um die folgenden Aktionen in deinem Namen auf ''$4'' auszuführen:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hallo $1,\n\num deine Anfrage abzuschließen, benötigt '''$2''' die Berechtigung zum Zugriff auf Informationen auf allen Projekten dieser Website in deinem Namen. An deinem Benutzerkonto werden keine Änderungen durchgeführt.",
+ "mwoauth-form-description-onewiki-nogrants": "Hallo $1,\n\num deine Anfrage abzuschließen, benötigt '''$2''' die Berechtigung zum Zugriff auf Informationen auf ''$4'' in deinem Namen. An deinem Benutzerkonto werden keine Änderungen durchgeführt.",
+ "mwoauth-form-description-allwikis-privateinfo": "Hallo $1,\n\num deine Anfrage abschließen zu können, benötigt '''$2''' eine Berechtigung, um auf Informationen über dich zuzugreifen, inklusive deines bürgerlichen Namens und deiner E-Mail-Adresse auf allen Projekten dieser Website. Mit deinem Benutzerkonto werden keine Änderungen durchgeführt.",
+ "mwoauth-form-description-onewiki-privateinfo": "Hallo $1,\n\num deine Anfrage abschließen zu können, benötigt '''$2''' eine Berechtigung, um auf Informationen zuzugreifen, inklusive deines bürgerlichen Namens und deiner E-Mail-Adresse auf ''$4''. Mit deinem Benutzerkonto werden keine Änderungen durchgeführt.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Hallo $1,\n\num deine Anfrage abzuschließen, benötigt '''$2''' die Erlaubnis, in allen Projekten auf Informationen über dich zuzugreifen, einschließlich deiner E-Mail-Adresse. Es werden keine Änderungen an deinem Konto vorgenommen.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Hallo $1,\n\num deine Anfrage abzuschließen, benötigt '''$2''' die Erlaubnis, auf ''$4'' auf Informationen über dich zuzugreifen, einschließlich deiner E-Mail-Adresse. Es werden keine Änderungen an deinem Konto vorgenommen.",
+ "mwoauth-form-button-approve": "Erlauben",
+ "mwoauth-form-button-cancel": "Abbrechen",
+ "mwoauth-error": "Anwendungsverbindungsfehler",
+ "mwoauth-grants-heading": "Angeforderte Berechtigungen:",
+ "mwoauth-grants-nogrants": "Die Anwendung hat keine Berechtigungen beantragt.",
+ "mwoauth-acceptance-cancelled": "Du hast dich entschieden, dass $1 nicht auf dein Benutzerkonto zugreifen darf. $1 wird nicht laufen, bis du den Zugriff erlaubt hast. Du kannst zu $1 zurückgehen oder deine verbundenen Anwendungen [[Special:OAuthManageMyGrants|verwalten]].",
+ "mwoauth-granttype-normal": "Autorisierung für spezielle Berechtigungen beantragen.",
+ "grant-mwoauth-authonly": "Nur Verifizierung der Benutzeridentität, keine Möglichkeit zum Lesen von Seiten oder Handeln im Namen des Benutzers.",
+ "grant-mwoauth-authonlyprivate": "Nur Verifizierung der Benutzeridentität mit Zugriff auf den bürgerlichen Namen und die E-Mail-Adresse, keine Möglichkeit zum Lesen von Seiten oder Handeln im Namen des Benutzers.",
+ "mwoauth-listgrants-extra-summary": "== OAuth-spezifische Berechtigungen ==\n\nDiese zusätzlichen Berechtigungen sind auf OAuth-Verbraucher anwendbar.",
+ "mwoauth-oauth-exception": "Im OAuth-Protokoll ist ein Fehler aufgetreten: $1",
+ "mwoauth-callback-not-oob": "oauth_callback muss auf „oob“ festgelegt sein (Groß-/Kleinschreibung beachten)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback muss auf „oob“ festgelegt (Groß-/Kleinschreibung beachten) oder der konfigurierte Callback muss ein Präfix des angegebenen Callbacks sein.",
+ "right-mwoauthproposeconsumer": "Neue OAuth-Anwendungen vorschlagen",
+ "right-mwoauthupdateownconsumer": "Kontrollierte OAuth-Anwendungen aktualisieren",
+ "right-mwoauthmanageconsumer": "OAuth-Anwendungen verwalten",
+ "right-mwoauthsuppress": "OAuth-Anwendungen unterdrücken",
+ "right-mwoauthviewsuppressed": "Unterdrückte OAuth-Anwendungen ansehen",
+ "right-mwoauthviewprivate": "Private OAuth-Daten ansehen",
+ "right-mwoauthmanagemygrants": "OAuth-Berechtigungen verwalten",
+ "action-mwoauthmanageconsumer": "OAuth-Anwendungen zu verwalten",
+ "action-mwoauthsuppress": "OAuth-Anwendungen zu unterdrücken",
+ "action-mwoauthmanagemygrants": "deine OAuth-Berechtigungen zu verwalten",
+ "action-mwoauthproposeconsumer": "neue OAuth-Anwendungen vorzuschlagen",
+ "action-mwoauthupdateownconsumer": "kontrollierte OAuth-Anwendungen zu aktualisieren",
+ "action-mwoauthviewprivate": "private OAuth-Daten anzusehen",
+ "action-mwoauthviewsuppressed": "unterdrückte OAuth-Anwendungen anzusehen",
+ "mwoauth-tag-reserved": "Markierungen, die mit <code>OAuth CID:</code> beginnen, sind für die Verwendung durch OAuth reserviert.",
+ "mwoauth-botpasswords-note": "<strong>Hinweis:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> ist sicherer als Passwörter von Bots und sollte immer bevorzugt werden, wenn der Bot es unterstützt.",
+ "mwoauth-api-module-disabled": "Das Modul „$1“ ist mit OAuth nicht verfügbar.",
+ "echo-category-title-oauth-owner": "OAuth-Entwicklung",
+ "echo-pref-tooltip-oauth-owner": "Benachrichtige mich über Ereignisse bezüglich OAuth-Anwendungen, die ich erstellt habe.",
+ "echo-category-title-oauth-admin": "OAuth-Verwaltung",
+ "echo-pref-tooltip-oauth-admin": "Benachrichtige mich über Ereignisse bezüglich der Überprüfung von OAuth-Anwendungen.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|plante}} die neue OAuth-Anwendung $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|aktualisierte}} die OAuth-Anwendung $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|bestätigte}} {{GENDER:$3|deine}} OAuth-Anwendung ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|lehnte}} {{GENDER:$3|deine}} OAuth-Anwendung ab ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|deaktivierte}} {{GENDER:$3|deine}} OAuth-Anwendung ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reaktivierte}} {{GENDER:$3|deine}} OAuth-Anwendung ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|plante}} eine neue OAuth-Anwendung auf {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|aktualisierte}} eine OAuth-Anwendung auf {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|bestätigte}} {{GENDER:$3|deine}} OAuth-Anwendung auf {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|lehnte}} {{GENDER:$3|deine}} OAuth-Anwendung auf {{SITENAME}} ab",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|deaktivierte}} {{GENDER:$3|deine}} OAuth-Anwendung auf {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reaktivierte}} {{GENDER:$3|deine}} OAuth-Anwendung auf {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Anwendung überprüfen",
+ "notification-oauth-app-update-primary-link": "Anwendung überprüfen",
+ "notification-oauth-app-approve-primary-link": "Anwendung ansehen",
+ "notification-oauth-app-reject-primary-link": "Anwendung ansehen",
+ "notification-oauth-app-disable-primary-link": "Anwendung ansehen",
+ "notification-oauth-app-reenable-primary-link": "Anwendung ansehen",
+ "notification-oauth-app-body": "Grund: $1",
+ "mwoauth-oauth2-granttype-auth-code": "Autorisierungscode"
+}
diff --git a/OAuth/i18n/diq.json b/OAuth/i18n/diq.json
new file mode 100644
index 00000000..0ca63c1d
--- /dev/null
+++ b/OAuth/i18n/diq.json
@@ -0,0 +1,34 @@
+{
+ "@metadata": {
+ "authors": [
+ "1917 Ekim Devrimi",
+ "Marmase",
+ "Mirzali",
+ "Orbot707"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Gıre bıyayey Aplikasyoni",
+ "mwoauth-prefs-managegrantslink": "$1 ya greyın {{PLURAL:$1|dezgi|dezgan}} idare ke",
+ "mwoauth-consumer-user": "Weşanayoğ:",
+ "mwoauth-consumer-stage-disabled": "astengın",
+ "mwoauthconsumerregistration-main": "Ser",
+ "mwoauthconsumerregistration-user": "Weşanayoğ",
+ "mwoauthconsumerregistration-description": "Şınasnayış",
+ "mwoauthconsumerregistration-stage": "Weziyet",
+ "mwoauthmanageconsumers-type": "Rêzi:",
+ "mwoauthmanageconsumers-main": "Ser",
+ "mwoauthmanageconsumers-user": "Weşanayoğ",
+ "mwoauthmanageconsumers-description": "Şınasnayış",
+ "mwoauthmanageconsumers-disable": "Astengın",
+ "oauthlistconsumers": "Listey Dezganê icazetınan",
+ "mwoauthlistconsumers-user": "Weşanayoğ",
+ "mwoauthlistconsumers-description": "Şınasnayış",
+ "mwoauthlistconsumers-status": "Weziyet",
+ "mwoauthlistconsumers-status-disabled": "astengın",
+ "mwoauthmanagemygrants-user": "Weşanayoğ:",
+ "mwoauthmanagemygrants-description": "Şınasnayış",
+ "mwoauth-form-button-cancel": "Bıtexelne",
+ "right-mwoauthviewprivate": "Datay OAutoyê xasi bıvênên",
+ "action-mwoauthviewprivate": "datay OAutoyê xasi bıvênên",
+ "notification-oauth-app-body": "Sebeb: $1"
+}
diff --git a/OAuth/i18n/dty.json b/OAuth/i18n/dty.json
new file mode 100644
index 00000000..dfd2b748
--- /dev/null
+++ b/OAuth/i18n/dty.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "राम प्रसाद जोशी"
+ ]
+ },
+ "mwoauth-consumer-email-unconfirmed": "तमरा खातामी इमेल ठेगाना प्रमाणित भयाको छैन ।"
+}
diff --git a/OAuth/i18n/el.json b/OAuth/i18n/el.json
new file mode 100644
index 00000000..21d58041
--- /dev/null
+++ b/OAuth/i18n/el.json
@@ -0,0 +1,25 @@
+{
+ "@metadata": {
+ "authors": [
+ "Geraki",
+ "Indoril",
+ "Nikosgranturismogt",
+ "Protnet"
+ ]
+ },
+ "mwoauth-nosubpage-explanation": "Ο OAuth είναι ένας μηχανισμός που επιτρέπει σε εξωτερικές εφαρμογές να αναγνωρίζουν έναν χρήστη {{SITENAME}} ή να ενεργούν εξ ονόματός τους, αφού λάβουν άδεια από αυτόν τον χρήστη.\n\nΓια να γίνει κάτι τέτοιο, απαιτούνται περισσότερες παράμετροι. Εάν σας έστειλε εδώ από μια εξωτερική εφαρμογή, αυτό οφείλεται πιθανώς σε ένα σφάλμα στην εφαρμογή εκείνη. Θα πρέπει να επικοινωνήσετε με τον δημιουργό της.",
+ "mwoauth-db-readonly": "Το OAuth βάση δεδομένων είναι προσωρινά κλειδωμένη. Παρακαλούμε δοκιμάστε ξανά σε λίγα λεπτά.",
+ "mwoauth-prefs-managegrants": "Συνδεδεμένες εφαρμογές:",
+ "mwoauth-prefs-managegrantslink": "Διαχείριση $1 {{PLURAL:$1|συνδεδεμένης εφαρμογής|συνδεδεμένων εφαρμογών}}",
+ "oauthlistconsumers": "Κατάλογος των εφαρμογών OAuth",
+ "mwoauthmanagemygrants-text": "Αυτή η σελίδα περιέχει όσες εφαρμογές που μπορούν να χρησιμοποιήσουν το λογαριασμό σας. Για κάθε τέτοια εφαρμογή, το εύρος της πρόσβασής της περιορίζεται από τις άδειες που δώσατε στην εφαρμογή όταν της επιτρέψατε να ενεργεί για λογαριασμό σας. Αν ξεχωριστά επιτρέψατε σε μια εφαρμογή να έχει πρόσβαση σε διαφορετικά αδελφά εγχειρήματα για λογαριασμό σας, τότε θα δείτε ξεχωριστή ρύθμιση για κάθε τέτοιο εγχείρημα παρακάτω.\n\nΟι συνδεδεμένες εφαρμογές έχουν πρόσβαση στο λογαριασμό σας μέσω του πρωτοκόλλου OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Μάθετε περισσότερα για τις συνδεδεμένες εφαρμογές])</span>",
+ "mwoauthdatastore-bad-source-ip": "Η αίτηση ήρθε από μια μη έγκυρη διεύθυνση IP.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Γειά σου $2,\n\nπροκειμένου να ολοκληρώσετε το αίτημά σας, το '''$2''' χρειάζεται άδεια να προσπελάσει πληροφορίες για εσάς, περιλαμβανομένης της διεύθυνσης email σας, σε όλα τα εγχειρήματα αυτού του ιστότοπου. Δεν θα γίνουν αλλαγές με τον λογαριασμό σας.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Γειά σου $2,\n\nπροκειμένου να ολοκληρώσετε το αίτημά σας, το '''$2''' χρειάζεται άδεια να προσπελάσει πληροφορίες, περιλαμβανομένης της διεύθυνσης email σας, στο ''$4''. Δεν θα γίνουν αλλαγές με τον λογαριασμό σας.",
+ "grant-mwoauth-authonly": "Επαλήθευσης ταυτότητας χρήστη μόνο, καμία δυνατότητα ανάγνωσης σελίδων ή να ενεργήσει για λογαριασμό άλλου.",
+ "grant-mwoauth-authonlyprivate": "Επαλήθευσης ταυτότητας χρήστη μόνο με πρόσβαση στο πραγματικό όνομα και τη διεύθυνση ηλεκτρονικού ταχυδρομείου, καμία δυνατότητα ανάγνωσης σελίδων ή να ενεργήσει για λογαριασμό άλλου.",
+ "mwoauth-botpasswords-note": "<strong>Σημείωση:</strong> Το <span class=\"plainlinks\">[$1 OAuth]</span> είναι πιο ασφαλές από τα συνθηματικά για ρομπότ και θα πρέπει να προτιμάται όποτε το ρομπότ το υποστηρίζει.",
+ "mwoauth-login-required-reason": "Θα πρέπει να συνδεθείτε στον λογαριασμό σας {{SITENAME}} για να εξουσιοδοτήσετε εφαρμογές να έχουν πρόσβαση σε αυτόν.",
+ "mwoauthconsumer-consumer-view": "Εμφάνιση αυτού το καταναλωτή",
+ "mwoauthconsumer-application-view": "Εμφάνιση αυτής της εφαρμογής"
+}
diff --git a/OAuth/i18n/en-gb.json b/OAuth/i18n/en-gb.json
new file mode 100644
index 00000000..f77a3d6d
--- /dev/null
+++ b/OAuth/i18n/en-gb.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Reedy",
+ "Samwilson",
+ "Shirayuki"
+ ]
+ },
+ "mwoauth-desc": "Allows usage of OAuth 1.0a and OAuth 2.0 for API authorisation",
+ "mwoauthconsumerregistration-propose-text": "Developers should use the form below to propose a new OAuth consumer (see the [//www.mediawiki.org/wiki/Extension:OAuth extension documentation] for more details). After submitting this form, you will receive a token that your application will use to identify itself to MediaWiki. An OAuth administrator will need to approve your application before it can be authorised by other users.\n\nA few recommendations and remarks:\n* Try to use as few grants as possible. Avoid grants that are not actually needed now.\n* Versions are of the form \"major.minor.release\" (the last two being optional) and increase as grant changes are needed.\n* Please provide a public RSA key (in PEM format) if possible; otherwise a (less secure) secret token will have to be used.\n* You can use a project ID to restrict the consumer to a single project on this site (use \"*\" for all projects).",
+ "mwoauthmanagemygrants-text": "This page lists any applications that can use your account. For any such application, the scope of its access is limited by the permissions that you granted to the application when you authorised it to act on your behalf. If you separately authorised an application to access different \"sister\" projects on your behalf, then you will see separate configuration for each such project below.\n\nConnected applications access your account by using the OAuth protocol. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Learn more about connected applications])</span>",
+ "mwoauthmanagemygrants-renounce": "Deauthorise",
+ "mwoauthdatastore-access-token-not-found": "No approved grant was found for that authorisation token",
+ "mwoauth-invalid-authorization-title": "OAuth authorisation error",
+ "mwoauth-invalid-authorization": "The authorisation headers in your request are not valid: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "The authorisation headers in your request are not valid for $1",
+ "mwoauth-invalid-authorization-invalid-user": "The authorisation headers in your request are for a user that does not exist here",
+ "mwoauth-invalid-authorization-wrong-user": "The authorisation headers in your request are for a different user",
+ "mwoauth-invalid-authorization-not-approved": "The app that you are trying to connect seems to be set up incorrectly. Contact the author of \"$1\" for help.",
+ "mwoauth-invalid-authorization-blocked-user": "The authorisation headers in your request are for a user who is blocked",
+ "mwoauth-acceptance-cancelled": "You have chosen not to allow \"$1\" to access your account. \"$1\" will not work unless you allow it access. You can go back to \"$1\" or [[Special:OAuthManageMyGrants|manage]] your connected apps."
+}
diff --git a/OAuth/i18n/en.json b/OAuth/i18n/en.json
new file mode 100644
index 00000000..a591ea01
--- /dev/null
+++ b/OAuth/i18n/en.json
@@ -0,0 +1,330 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "oauth": "OAuth",
+ "mwoauth-desc": "Allows usage of OAuth 1.0a and OAuth 2.0 for API authorization",
+ "mwoauth-nosubpage-explanation": "OAuth is a mechanism which allows external applications to identify a {{SITENAME}} user or act on their behalf, after receiving permission from that user.\n\nFor this page to do something, more parameters are required. If you were sent here from an external application, that was probably due to an error in that application; you should contact the author.",
+ "mwoauth-verified": "The application is now allowed to access MediaWiki on your behalf.\n\nTo complete the process, provide this verification value to the application: '''$1'''",
+ "mwoauth-db-readonly": "The OAuth database is temporarily locked. Please try again in a few minutes.",
+ "mwoauth-missing-field": "Missing value for \"$1\" field",
+ "mwoauth-invalid-field": "Invalid value provided for \"$1\" field",
+ "mwoauth-invalid-field-generic": "Invalid value provided",
+ "mwoauth-field-hidden": "(this information is hidden)",
+ "mwoauth-field-private": "(this information is private)",
+ "mwoauth-prefs-managegrants": "Connected apps:",
+ "mwoauth-prefs-managegrantslink": "Manage {{PLURAL:$1|$1 connected application|$1 connected applications|0=connected applications}}",
+ "mwoauth-consumer-allwikis": "All projects on this site",
+ "mwoauth-consumer-key": "Consumer key:",
+ "mwoauth-consumer-name": "Application name:",
+ "mwoauth-consumer-version": "Consumer version:",
+ "mwoauth-consumer-user": "Publisher:",
+ "mwoauth-consumer-stage": "Current status:",
+ "mwoauth-consumer-email": "Contact email address:",
+ "mwoauth-consumer-email-help": "Only visible to those who are approving new consumers",
+ "mwoauth-consumer-owner-only-label": "Owner-only:",
+ "mwoauth-consumer-owner-only": "This consumer is for use only by $1.",
+ "mwoauth-consumer-owner-only-help": "Selecting this option will cause the consumer to be automatically approved and accepted for use by $1. It will not be usable by any other user, and the usual authorization flow will not function. Actions taken using this consumer will not be tagged.",
+ "mwoauth-consumer-description": "Application description:",
+ "mwoauth-consumer-callbackurl": "OAuth \"callback\" URL:",
+ "mwoauth-consumer-callbackisprefix": "Allow consumer to specify a callback in requests and use \"callback\" URL above as a required prefix.",
+ "mwoauth-consumer-granttypes": "Types of grants being requested:",
+ "mwoauth-consumer-grantsneeded": "Applicable grants:",
+ "mwoauth-consumer-required-grant": "Applicable to consumer",
+ "mwoauth-consumer-wiki": "Applicable project:",
+ "mwoauth-consumer-wiki-thiswiki": "Current project ($1)",
+ "mwoauth-consumer-restrictions": "Usage restrictions:",
+ "mwoauth-consumer-restrictions-json": "Usage restrictions (JSON):",
+ "mwoauth-consumer-rsakey": "Public RSA key (optional):",
+ "mwoauth-consumer-rsakey-help": "Enter a public key to use the RSA-SHA1 signature method. Leave empty to use HMAC-SHA1 with a random secret. If you are not sure which, leave it empty.",
+ "mwoauth-consumer-secretkey": "Consumer secret token:",
+ "mwoauth-consumer-accesstoken": "Access token:",
+ "mwoauth-consumer-reason": "Reason:",
+ "mwoauth-consumer-developer-agreement": "By submitting this application, you acknowledge that we reserve the right to disable your application, remove or restrict you or your application's access to this site, and pursue any other course of action we deem appropriate if we believe, in our sole judgment, that you or your application are violating any policy, guideline, and guiding principle of the this site. We can change this Application Policy at any time without prior notice, at our sole discretion and as we deem necessary. Your continued use of OAuth constitutes acceptance of those changes.",
+ "mwoauth-consumer-email-unconfirmed": "Your account email address has not yet been confirmed.",
+ "mwoauth-consumer-email-mismatched": "Provided email address must match that of your account.",
+ "mwoauth-consumer-alreadyexists": "A consumer with this name/version/publisher combination already exists",
+ "mwoauth-consumer-alreadyexistsversion": "A consumer with this name/publisher combination already exists with an equal or higher version (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Cannot update information for a pending consumer request",
+ "mwoauth-consumer-not-proposed": "The consumer is not currently proposed",
+ "mwoauth-consumer-not-disabled": "The consumer is not currently disabled",
+ "mwoauth-consumer-not-approved": "The consumer is not approved (it may have been disabled)",
+ "mwoauth-missing-consumer-key": "No consumer key was provided.",
+ "mwoauth-invalid-consumer-key": "No consumer exists with the given key.",
+ "mwoauth-invalid-access-token": "No access token exists with the given key.",
+ "mwoauth-invalid-access-wrongwiki": "The consumer can only be used on project \"$1\".",
+ "mwoauth-consumer-conflict": "Someone changed the attributes of this consumer as you viewed it. Please try again. You may want to check the change log.",
+ "mwoauth-consumer-grantshelp": "Each grant gives access to listed user rights that a user account already has. See the [[Special:ListGrants|table of grants]] for more information.",
+ "mwoauth-consumer-stage-proposed": "proposed",
+ "mwoauth-consumer-stage-rejected": "rejected",
+ "mwoauth-consumer-stage-expired": "expired",
+ "mwoauth-consumer-stage-approved": "approved",
+ "mwoauth-consumer-stage-disabled": "disabled",
+ "mwoauth-consumer-stage-suppressed": "suppressed",
+ "oauthconsumerregistration": "OAuth consumer registration",
+ "mwoauthconsumerregistration-navigation": "Navigation:",
+ "mwoauthconsumerregistration-propose": "Propose new consumer",
+ "mwoauthconsumerregistration-list": "My consumer list",
+ "mwoauthconsumerregistration-main": "Main",
+ "mwoauthconsumerregistration-propose-text": "Developers should use the form below to propose a new OAuth consumer (see the [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth extension documentation] for more details). After submitting this form, you will receive a token that your application will use to identify itself to MediaWiki. An OAuth administrator will need to approve your application before it can be authorized by other users.\n\nA few recommendations and remarks:\n* Try to use as few grants as possible. Avoid grants that are not actually needed now.\n* Versions are of the form \"major.minor.release\" (the last two being optional) and increase as grant changes are needed.\n* Please provide a public RSA key (in PEM format) if possible; otherwise a (less secure) secret token will have to be used.\n* You can use a project ID to restrict the consumer to a single project on this site (use \"*\" for all projects).",
+ "mwoauthconsumerregistration-update-text": "Use the form below to update aspects of an OAuth consumer you control.\n\nAll values here will overwrite any previous ones. Do not leave blank fields unless you intend to clear those values.",
+ "mwoauthconsumerregistration-maintext": "This page is for letting developers propose and update OAuth consumer applications in this site's registry.\n\nFrom here, you can:\n* [[Special:OAuthConsumerRegistration/propose|Request a token for a new consumer]].\n* [[Special:OAuthConsumerRegistration/list|Manage your existing consumers]].\n\nFor more information about OAuth, please see the [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth extension documentation].",
+ "mwoauthconsumerregistration-propose-legend": "New OAuth consumer application",
+ "mwoauthconsumerregistration-update-legend": "Update OAuth consumer application",
+ "mwoauthconsumerregistration-propose-submit": "Propose consumer",
+ "mwoauthconsumerregistration-update-submit": "Update consumer",
+ "mwoauthconsumerregistration-none": "You do not control any OAuth consumers.",
+ "mwoauthconsumerregistration-name": "Consumer",
+ "mwoauthconsumerregistration-user": "Publisher",
+ "mwoauthconsumerregistration-description": "Description",
+ "mwoauthconsumerregistration-email": "Contact email",
+ "mwoauthconsumerregistration-consumerkey": "Consumer key",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Last change",
+ "mwoauthconsumerregistration-manage": "manage",
+ "mwoauthconsumerregistration-resetsecretkey": "Reset the secret key to a new value",
+ "mwoauthconsumerregistration-proposed": "Your OAuth consumer request has been received.\n\nYou have been assigned a consumer token of '''$1''' and a secret token of '''$2'''. ''Please record these for future reference.''",
+ "mwoauthconsumerregistration-created-owner-only": "Your OAuth consumer has been created.\n\nYour tokens are:\n; Consumer token: $1\n; Consumer secret: $2\n; Access token: $3\n; Access secret: $4\n<em>Please record these for future reference.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "Your OAuth 2.0 client has been created.\n\nYour tokens are:\n; Client application key: $1\n; Client application secret: $2\n; Access token: $3\n;<em>Please record these for future reference.</em>",
+ "mwoauthconsumerregistration-updated": "Your OAuth consumer registry was updated.",
+ "mwoauthconsumerregistration-secretreset": "You have been assigned a consumer secret token of '''$1'''. ''Please record this for future reference.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Your OAuth consumer tokens have been reset. The new tokens are:\n; Consumer token: $1\n; Consumer secret: $2\n; Access token: $3\n; Access secret: $4\n<em>Please record these for future reference.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Your OAuth 2.0 consumer tokens have been reset. The new tokens are:\n; Consumer token: $1\n; Consumer secret: $2\n; Access token: $3\n<em>Please record these for future reference.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "You must confirm your email address before creating OAuth applications.\nPlease set and validate your email address through your [[Special:Preferences|user preferences]].",
+ "oauthmanageconsumers": "Manage OAuth consumers",
+ "mwoauthmanageconsumers-notloggedin": "You have to be logged in to access this page.",
+ "mwoauthmanageconsumers-type": "Queues:",
+ "mwoauthmanageconsumers-showproposed": "Proposed requests",
+ "mwoauthmanageconsumers-showrejected": "Rejected requests",
+ "mwoauthmanageconsumers-showexpired": "Expired requests",
+ "mwoauthmanageconsumers-linkproposed": "proposed requests",
+ "mwoauthmanageconsumers-linkrejected": "rejected requests",
+ "mwoauthmanageconsumers-linkexpired": "expired requests",
+ "mwoauthmanageconsumers-linkapproved": "approved requests",
+ "mwoauthmanageconsumers-linkdisabled": "disabled requests",
+ "mwoauthmanageconsumers-main": "Main",
+ "mwoauthmanageconsumers-maintext": "This page is meant for handling OAuth (see http://oauth.net) consumer application requests and managing established OAuth consumers.",
+ "mwoauthmanageconsumers-queues": "Select a consumer confirmation queue from below:",
+ "mwoauthmanageconsumers-q-proposed": "Queue of proposed consumer requests",
+ "mwoauthmanageconsumers-q-rejected": "Queue of rejected consumer requests",
+ "mwoauthmanageconsumers-q-expired": "Queue of expired consumer requests",
+ "mwoauthmanageconsumers-lists": "Select a consumer status list from below:",
+ "mwoauthmanageconsumers-l-approved": "List of currently approved consumers",
+ "mwoauthmanageconsumers-l-disabled": "List of currently disabled consumers",
+ "mwoauthmanageconsumers-none-proposed": "No proposed consumers in this list.",
+ "mwoauthmanageconsumers-none-rejected": "No proposed consumers in this list.",
+ "mwoauthmanageconsumers-none-expired": "No proposed consumers in this list.",
+ "mwoauthmanageconsumers-none-approved": "No consumers meet this criteria.",
+ "mwoauthmanageconsumers-none-disabled": "No consumers meet this criteria.",
+ "mwoauthmanageconsumers-name": "Consumer",
+ "mwoauthmanageconsumers-user": "Publisher",
+ "mwoauthmanageconsumers-description": "Description",
+ "mwoauthmanageconsumers-email": "Contact email",
+ "mwoauthmanageconsumers-consumerkey": "Consumer key",
+ "mwoauthmanageconsumers-lastchange": "Last change",
+ "mwoauthmanageconsumers-review": "review/manage",
+ "mwoauthmanageconsumers-confirm-text": "Use this form to approve, reject, disable, or re-enable this consumer.",
+ "mwoauthmanageconsumers-confirm-legend": "Manage OAuth consumer",
+ "mwoauthmanageconsumers-action": "Change status:",
+ "mwoauthmanageconsumers-approve": "Approved",
+ "mwoauthmanageconsumers-reject": "Rejected",
+ "mwoauthmanageconsumers-rsuppress": "Rejected and suppressed",
+ "mwoauthmanageconsumers-disable": "Disabled",
+ "mwoauthmanageconsumers-dsuppress": "Disabled and suppressed",
+ "mwoauthmanageconsumers-reenable": "Approved",
+ "mwoauthmanageconsumers-reason": "Reason:",
+ "mwoauthmanageconsumers-confirm-submit": "Update consumer status",
+ "mwoauthmanageconsumers-success-approved": "Request has been approved.",
+ "mwoauthmanageconsumers-success-rejected": "Request has been rejected.",
+ "mwoauthmanageconsumers-success-disabled": "Consumer has been disabled.",
+ "mwoauthmanageconsumers-success-reanable": "Consumer has been re-enabled.",
+ "mwoauthmanageconsumers-search-name": "consumers with this name",
+ "mwoauthmanageconsumers-search-publisher": "consumers by this user",
+ "oauthlistconsumers": "List OAuth applications",
+ "mwoauthlistconsumers-legend": "Browse OAuth applications",
+ "mwoauthlistconsumers-view": "details",
+ "mwoauthlistconsumers-none": "No applications found meeting this criteria.",
+ "mwoauthlistconsumers-name": "Application name",
+ "mwoauthlistconsumers-version": "Consumer version",
+ "mwoauthlistconsumers-user": "Publisher",
+ "mwoauthlistconsumers-description": "Description",
+ "mwoauthlistconsumers-wiki": "Applicable project",
+ "mwoauthlistconsumers-callbackurl": "OAuth \"callback URL\"",
+ "mwoauthlistconsumers-callbackisprefix": "Allow consumer to specify a callback in requests and use \"callback\" URL above as a required prefix.",
+ "mwoauthlistconsumers-grants": "Applicable grants",
+ "mwoauthlistconsumers-basicgrantsonly": "(basic access only)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "any",
+ "mwoauthlistconsumers-status-proposed": "proposed",
+ "mwoauthlistconsumers-status-approved": "approved",
+ "mwoauthlistconsumers-status-disabled": "disabled",
+ "mwoauthlistconsumers-status-rejected": "rejected",
+ "mwoauthlistconsumers-status-expired": "expired",
+ "mwoauthlistconsumers-navigation": "Navigation:",
+ "mwoauthlistconsumers-update-link": "Update consumer",
+ "mwoauthlistconsumers-manage-link": "Manage consumer",
+ "mwoauthlistconsumers-grants-link": "Manage grants",
+ "mwoauthlistconsumers-rclink": "Recent changes by this application",
+ "oauthmanagemygrants": "Manage connected applications",
+ "mwoauthmanagemygrants-text": "This page lists any applications that can use your account. For any such application, the scope of its access is limited by the permissions that you granted to the application when you authorized it to act on your behalf. If you separately authorized an application to access different sister projects on your behalf, then you will see separate configuration for each such project below.\n\nConnected applications access your account by using the OAuth protocol. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Learn more about connected applications])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigation:",
+ "mwoauthmanagemygrants-showlist": "Connected application list",
+ "mwoauthmanagemygrants-none": "There are no applications connected to your account.",
+ "mwoauthmanagemygrants-user": "Publisher:",
+ "mwoauthmanagemygrants-description": "Description",
+ "mwoauthmanagemygrants-wikiallowed": "Allowed on project:",
+ "mwoauthmanagemygrants-grants": "Applicable grants",
+ "mwoauthmanagemygrants-grantsallowed": "Grants allowed",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Applicable grants allowed:",
+ "mwoauthmanagemygrants-review": "manage access",
+ "mwoauthmanagemygrants-revoke": "revoke access",
+ "mwoauthmanagemygrants-grantaccept": "Granted",
+ "mwoauthmanagemygrants-update-text": "Use the form below to modify the permissions granted to an application to act on your behalf.",
+ "mwoauthmanagemygrants-revoke-text": "Use the form below to revoke access for an application to act on your behalf.",
+ "mwoauthmanagemygrants-confirm-legend": "Manage connected application",
+ "mwoauthmanagemygrants-update": "Update grants",
+ "mwoauthmanagemygrants-renounce": "Deauthorize",
+ "mwoauthmanagemygrants-action": "Change status:",
+ "mwoauthmanagemygrants-confirm-submit": "Update access token status",
+ "mwoauthmanagemygrants-success-update": "Your preferences for this application have been updated.",
+ "mwoauthmanagemygrants-success-renounce": "The application's access to your account has been revoked.",
+ "mwoauthmanagemygrants-basic-tooltip": "Why can't I update this grant? This grant gives your connected application basic permissions which it requires to function properly. If you don't want this connected application to have these rights, you should revoke the application's access.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Why can't I update this grant? If you don't want this connected application to have this right, you should revoke the application's access.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Your}} edits by this application",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Your}} actions by this application",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|proposed}} an OAuth consumer (consumer key $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|updated}} an OAuth consumer (consumer key $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|approved}} an OAuth consumer by $3 (consumer key $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|rejected}} an OAuth consumer by $3 (consumer key $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|disabled}} an OAuth consumer by $3 (consumer key $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|re-enabled}} an OAuth consumer by $3 (consumer key $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|created}} an owner-only OAuth consumer (consumer key $4)",
+ "log-action-filter-mwoauthconsumer": "Type of OAuth consumer action:",
+ "log-action-filter-mwoauthconsumer-approve": "OAuth consumer approval",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Owner-only OAuth consumer creation",
+ "log-action-filter-mwoauthconsumer-disable": "OAuth consumer disablement",
+ "log-action-filter-mwoauthconsumer-propose": "OAuth consumer proposal",
+ "log-action-filter-mwoauthconsumer-reenable": "OAuth consumer reenablement",
+ "log-action-filter-mwoauthconsumer-reject": "OAuth consumer rejection",
+ "log-action-filter-mwoauthconsumer-update": "OAuth consumer update",
+ "mwoauthconsumer-consumer-logpage": "OAuth consumer log",
+ "mwoauthconsumer-consumer-logpagetext": "Log of approvals, rejections, and disabling of registered OAuth consumers.",
+ "mwoauth-bad-request-missing-params": "Sorry, something went wrong configuring this connected application. Contact the application's developer.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth missing parameters, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Sorry, something went wrong, you'll need to contact the application author for help with this.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unknown URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Sorry, something went wrong. You'll need to [$1 contact] the application author for help with this.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unknown URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "No approved grant was found for that authorization token.",
+ "mwoauthdatastore-request-token-not-found": "Sorry, something went wrong connecting this application.\nGo back and try to connect your account again, or contact the application author.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth token not found, $1</span>",
+ "mwoauthdatastore-callback-not-found": "OAuth callback URL not found in cache. This is probably an error in how the application makes requests to the server.",
+ "mwoauthdatastore-request-token-already-used": "This request has already been completed and cannot be resubmitted.\nGo back to the application and try to connect your account again, or contact the application author.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth token already used, $1</span>",
+ "mwoauthdatastore-bad-token": "No token was found matching your request.",
+ "mwoauthdatastore-bad-source-ip": "The request came from an invalid IP address.",
+ "mwoauthdatastore-bad-verifier": "The verification code provided was not valid.",
+ "mwoauthdatastore-invalid-token-type": "The requested token type is invalid.",
+ "mwoauthgrants-general-error": "There was an error in your OAuth request.",
+ "mwoauthserver-bad-consumer": "\"$1\" is not approved as a Connected App. [$2 Contact] the application author for help.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Connected OAuth app not approved, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Sorry, something went wrong connecting this application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unknown OAuth key, $1</span>",
+ "mwoauthserver-insufficient-rights": "Your account is not allowed to use Connected Apps, contact your site administrator to find out why.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Insufficient OAuth user rights, $1</span>",
+ "mwoauthserver-invalid-request-token": "Invalid token in your request.",
+ "mwoauthserver-invalid-user": "To use Connected Apps on this site, you must have an account across all projects. When you have an account on all projects, you can try to connect \"$1\" again.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unified login needed, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Sorry, something went wrong connecting this application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Consumer has no secret key, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" is an owner-only Connected App. To fetch the access token, see [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Consumer is owner-only, $3</span>",
+ "mwoauth-invalid-authorization-title": "OAuth authorization error",
+ "mwoauth-invalid-authorization": "The authorization headers in your request are not valid: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "The authorization headers in your request are not valid for $1",
+ "mwoauth-invalid-authorization-invalid-user": "The authorization headers in your request are for a user that does not exist here",
+ "mwoauth-invalid-authorization-wrong-user": "The authorization headers in your request are for a different user",
+ "mwoauth-invalid-authorization-not-approved": "The app that you are trying to connect seems to be set up incorrectly. Contact the author of \"$1\" for help.",
+ "mwoauth-invalid-authorization-blocked-user": "The authorization headers in your request are for a user who is blocked",
+ "mwoauth-form-description-allwikis": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to perform the following actions on your behalf on all projects of this site:\n\n$4",
+ "mwoauth-form-description-onewiki": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to perform the following actions on your behalf on ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to access information on all projects of this site on your behalf. No changes will be made with your account.",
+ "mwoauth-form-description-onewiki-nogrants": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to access information on ''$4'' on your behalf. No changes will be made with your account.",
+ "mwoauth-form-description-allwikis-privateinfo": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to access information about you, including your real name and email address, on all projects of this site. No changes will be made with your account.",
+ "mwoauth-form-description-onewiki-privateinfo": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to access information, including your real name and email address, on ''$4''. No changes will be made with your account.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to access information about you, including your email address, on all projects of this site. No changes will be made with your account.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Hi $1,\n\nIn order to complete your request, '''$2''' needs permission to access information, including your email address, on ''$4''. No changes will be made with your account.",
+ "mwoauth-form-legal": "",
+ "mwoauth-form-button-approve": "Allow",
+ "mwoauth-form-button-cancel": "Cancel",
+ "mwoauth-error": "Application Connection Error",
+ "mwoauth-grants-heading": "Requested permissions:",
+ "mwoauth-grants-nogrants": "The application has not requested any permissions.",
+ "mwoauth-acceptance-cancelled": "You have chosen not to allow \"$1\" to access your account. \"$1\" will not work unless you allow it access. You can go back to \"$1\" or [[Special:OAuthManageMyGrants|manage]] your connected apps.",
+ "mwoauth-granttype-normal": "Request authorization for specific permissions.",
+ "grant-mwoauth-authonly": "User identity verification only, no ability to read pages or act on a user's behalf.",
+ "grant-mwoauth-authonlyprivate": "User identity verification only with access to real name and email address, no ability to read pages or act on a user's behalf.",
+ "mwoauth-listgrants-extra-summary": "== OAuth-specific grants ==\n\nThese additional grants are applicable to OAuth consumers.",
+ "mwoauth-oauth-exception": "An error occurred in the OAuth protocol: $1",
+ "mwoauth-callback-not-oob": "oauth_callback must be set, and must be set to \"oob\" (case-sensitive)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback must be set, and must be set to \"oob\" (case-sensitive), or the configured callback must be a prefix of the supplied callback.",
+ "right-mwoauthproposeconsumer": "Propose new OAuth consumers",
+ "right-mwoauthupdateownconsumer": "Update OAuth consumers you control",
+ "right-mwoauthmanageconsumer": "Manage OAuth consumers",
+ "right-mwoauthsuppress": "Suppress OAuth consumers",
+ "right-mwoauthviewsuppressed": "View suppressed OAuth consumers",
+ "right-mwoauthviewprivate": "View private OAuth data",
+ "right-mwoauthmanagemygrants": "Manage OAuth grants",
+ "action-mwoauthmanageconsumer": "manage OAuth consumers",
+ "action-mwoauthsuppress": "suppress OAuth consumers",
+ "action-mwoauthmanagemygrants": "manage your OAuth grants",
+ "action-mwoauthproposeconsumer": "propose new OAuth consumers",
+ "action-mwoauthupdateownconsumer": "update OAuth consumers you control",
+ "action-mwoauthviewprivate": "view private OAuth data",
+ "action-mwoauthviewsuppressed": "view suppressed OAuth consumers",
+ "mwoauth-tag-reserved": "Tags beginning with <code>OAuth CID:</code> are reserved for use by OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Note:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> is more secure than bot passwords and should be preferred whenever the bot supports it.",
+ "mwoauth-api-module-disabled": "The \"$1\" module is not available with OAuth.",
+ "echo-category-title-oauth-owner": "OAuth development",
+ "echo-pref-tooltip-oauth-owner": "Notify me about events related to OAuth applications I have created.",
+ "echo-category-title-oauth-admin": "OAuth admin",
+ "echo-pref-tooltip-oauth-admin": "Notify me about events related to reviewing OAuth applications.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|proposed}} a new OAuth app: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|updated}} the OAuth app $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|approved}} {{GENDER:$3|your}} OAuth app ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|rejected}} {{GENDER:$3|your}} OAuth app ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|disabled}} {{GENDER:$3|your}} OAuth app ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reenabled}} {{GENDER:$3|your}} OAuth app ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|proposed}} a new OAuth app on {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|updated}} an OAuth app on {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|approved}} {{GENDER:$3|your}} OAuth app on {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|rejected}} {{GENDER:$3|your}} OAuth app on {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|disabled}} {{GENDER:$3|your}} OAuth app on {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reenabled}} {{GENDER:$3|your}} OAuth app on {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Review app",
+ "notification-oauth-app-update-primary-link": "Review app",
+ "notification-oauth-app-approve-primary-link": "View app",
+ "notification-oauth-app-reject-primary-link": "View app",
+ "notification-oauth-app-disable-primary-link": "View app",
+ "notification-oauth-app-reenable-primary-link": "View app",
+ "notification-oauth-app-body": "Reason: $1",
+ "mwoauth-oauth-version": "OAuth protocol version",
+ "mwoauth-oauth-version-1": "OAuth 1.0a",
+ "mwoauth-oauth-version-2": "OAuth 2.0",
+ "mwoauth-oauth2-is-confidential": "Client is confidential",
+ "mwoauth-oauth2-is-confidential-help": "A confidential client is an application that is capable of keeping a client password confidential to the world. Non-confidential clients are less secure",
+ "mwoauth-oauth2-granttypes": "Allowed OAuth2 grant types",
+ "mwoauth-oauth2-granttype-auth-code": "Authorization code",
+ "mwoauth-oauth2-granttype-refresh-token": "Refresh token",
+ "mwoauth-oauth2-granttype-client-credentials": "Client credentials",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Cannot create access token, user did not approve issuing this access token",
+ "mwoauth-oauth2-error-user-approval-deny": "User has rejected the request from client application",
+ "mwoauth-oauth-unsupported-version": "This endpoint is not allowed for OAuth version $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "Scope \"$1\" is not allowed for this application",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Owner-only clients must be allowed to use client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Could not retrieve access token: $1",
+ "mwoauth-oauth2-error": "$1\n\n$2\n\n$3",
+ "mwoauth-oauth2-error-server-error": "The authorization server encountered an unexpected condition that prevented it from fulfilling the request.",
+ "mwoauth-oauth2-error-invalid-request": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.",
+ "mwoauth-oauth2-error-unauthorized-client": "The client is not authorized to request an authorization code using this method.",
+ "mwoauth-oauth2-error-access-denied": "The resource owner or authorization server denied the request.",
+ "mwoauth-oauth2-error-unsupported-response-type": "The authorization server does not support obtaining an authorization code using this method.",
+ "mwoauth-oauth2-error-invalid-scope": "The requested scope is invalid, unknown, or malformed.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.",
+ "mwoauth-oauth2-error-invalid-client": "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)",
+ "mwoauth-oauth2-error-request-not-verified": "Attempting to retrieve verified property before verifying the request",
+ "mwoauth-oauth2-invalid-access-token": "Invalid access token",
+ "mwoauth-consumer-access-no-user": "Consumer approval must be bound to a valid user, user with ID 0 given",
+ "mwoauth-login-required-reason": "You need to log into your {{SITENAME}} account to authorize applications to access it.",
+ "mwoauthconsumer-consumer-view": "View this consumer",
+ "mwoauthconsumer-application-view": "View this application"
+}
diff --git a/OAuth/i18n/eo.json b/OAuth/i18n/eo.json
new file mode 100644
index 00000000..0384f1c2
--- /dev/null
+++ b/OAuth/i18n/eo.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gamliel Fishkin",
+ "Robin van der Vliet"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Konektitaj aplikaĵoj:",
+ "mwoauth-prefs-managegrantslink": "Administri $1 {{PLURAL:$1|konektitan aplikaĵon|konektitajn aplikaĵojn}}",
+ "mwoauth-form-button-cancel": "Nuligi",
+ "notification-oauth-app-approve-primary-link": "Montri aplikaĵon",
+ "notification-oauth-app-reject-primary-link": "Montri aplikaĵon",
+ "notification-oauth-app-disable-primary-link": "Montri aplikaĵon",
+ "notification-oauth-app-reenable-primary-link": "Montri aplikaĵon",
+ "notification-oauth-app-body": "Kialo: $1"
+}
diff --git a/OAuth/i18n/es.json b/OAuth/i18n/es.json
new file mode 100644
index 00000000..16ebce29
--- /dev/null
+++ b/OAuth/i18n/es.json
@@ -0,0 +1,255 @@
+{
+ "@metadata": {
+ "authors": [
+ "AlvaroMolina",
+ "Amitie 10g",
+ "Csbotero",
+ "DannyS712",
+ "Dgstranz",
+ "Fitoschido",
+ "Ihojose",
+ "Jelou",
+ "Lemondoge",
+ "Macofe",
+ "MarcoAurelio",
+ "Matiia",
+ "Ovruni",
+ "Ryo567",
+ "Tiberius1701"
+ ]
+ },
+ "mwoauth-desc": "Permite el uso de OAuth 1.0a y OAuth 2.0 para la autorización de la API",
+ "mwoauth-verified": "La aplicación ahora puede acceder a MediaWiki en tu nombre.\n\nPara completar el proceso, proporciona este valor de comprobación a la aplcación: '''$1'''",
+ "mwoauth-db-readonly": "La base de datos OAuth está bloqueada temporalmente. Inténtalo de nuevo en unos minutos.",
+ "mwoauth-missing-field": "Falta el valor del campo «$1»",
+ "mwoauth-invalid-field": "El valor del campo «$1» no es válido",
+ "mwoauth-invalid-field-generic": "Se ha proporcionado un valor no válido",
+ "mwoauth-field-hidden": "(esta información está oculta)",
+ "mwoauth-field-private": "(esta información es privada)",
+ "mwoauth-prefs-managegrants": "Aplicaciones conectadas:",
+ "mwoauth-prefs-managegrantslink": "Gestionar $1 {{PLURAL:$1|aplicación conectada|aplicaciones conectadas|0=aplicaciones conectadas}}",
+ "mwoauth-consumer-allwikis": "Todos los proyectos en este sitio",
+ "mwoauth-consumer-key": "Clave de consumidor:",
+ "mwoauth-consumer-name": "Nombre de la aplicación:",
+ "mwoauth-consumer-version": "Versión de consumidor:",
+ "mwoauth-consumer-user": "Editor:",
+ "mwoauth-consumer-stage": "Estado actual:",
+ "mwoauth-consumer-email": "Correo electrónico de contacto:",
+ "mwoauth-consumer-email-help": "Visible solamente para aquellos que pueden aprobar nuevos consumidores",
+ "mwoauth-consumer-owner-only": "Este consumidor es exclusivo para $1.",
+ "mwoauth-consumer-description": "Descripción de la aplicación:",
+ "mwoauth-consumer-callbackurl": "URL de devolución de llamada de OAuth:",
+ "mwoauth-consumer-granttypes": "Tipo de subvenciones que se solicitan:",
+ "mwoauth-consumer-wiki-thiswiki": "Proyecto actual ($1)",
+ "mwoauth-consumer-restrictions": "Restricciones de uso:",
+ "mwoauth-consumer-restrictions-json": "Restricciones de uso (JSON):",
+ "mwoauth-consumer-rsakey": "Clave RSA pública (opcional):",
+ "mwoauth-consumer-accesstoken": "Ficha de acceso:",
+ "mwoauth-consumer-reason": "Motivo:",
+ "mwoauth-consumer-email-unconfirmed": "Todavía no se ha confirmado la dirección de correo de tu cuenta.",
+ "mwoauth-consumer-email-mismatched": "La dirección de correo electrónico proporcionada debe coincidir con la de tu cuenta.",
+ "mwoauth-consumer-alreadyexists": "Ya existe un consumidor con esta combinación de nombre/versión/editor",
+ "mwoauth-consumer-alreadyexistsversion": "Ya existe un consumidor con esta combinación de nombre/editor con una versión igual o mayor (\"$1\")",
+ "mwoauth-consumer-not-accepted": "No se puede actualizar la información de una solicitud de consumidor pendiente",
+ "mwoauth-consumer-not-proposed": "El consumidor no está propuesto actualmente.",
+ "mwoauth-consumer-not-disabled": "El consumidor no está desactivado actualmente.",
+ "mwoauth-consumer-not-approved": "El consumidor no está aprobado (puede haber sido deshabilitado)",
+ "mwoauth-missing-consumer-key": "No hay clave de consumidor.",
+ "mwoauth-invalid-access-wrongwiki": "El consumidor solo puede usarse en el proyecto \"$1\".",
+ "mwoauth-consumer-stage-proposed": "propuesto",
+ "mwoauth-consumer-stage-rejected": "rechazado",
+ "mwoauth-consumer-stage-expired": "expirado",
+ "mwoauth-consumer-stage-approved": "aprobado",
+ "mwoauth-consumer-stage-disabled": "desactivado",
+ "mwoauth-consumer-stage-suppressed": "suprimido",
+ "mwoauthconsumerregistration-navigation": "Navegación:",
+ "mwoauthconsumerregistration-propose": "Proponer nuevo consumidor",
+ "mwoauthconsumerregistration-list": "Mi lista de consumidores",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-submit": "Proponer consumidor",
+ "mwoauthconsumerregistration-update-submit": "Actualizar consumidor",
+ "mwoauthconsumerregistration-name": "Consumidor",
+ "mwoauthconsumerregistration-user": "Editor",
+ "mwoauthconsumerregistration-description": "Descripción",
+ "mwoauthconsumerregistration-email": "Correo electrónico de contacto",
+ "mwoauthconsumerregistration-consumerkey": "Clave de consumidor",
+ "mwoauthconsumerregistration-stage": "Estado",
+ "mwoauthconsumerregistration-lastchange": "Último cambio",
+ "mwoauthconsumerregistration-manage": "gestionar",
+ "mwoauthconsumerregistration-resetsecretkey": "Restablecer clave secreta a un valor nuevo",
+ "mwoauthmanageconsumers-notloggedin": "Debes iniciar sesión para acceder a esta página.",
+ "mwoauthmanageconsumers-type": "Colas:",
+ "mwoauthmanageconsumers-showproposed": "Solicitudes propuestas",
+ "mwoauthmanageconsumers-showrejected": "Solicitudes rechazadas",
+ "mwoauthmanageconsumers-showexpired": "Solicitudes caducadas",
+ "mwoauthmanageconsumers-linkproposed": "solicitudes propuestas",
+ "mwoauthmanageconsumers-linkrejected": "solicitudes rechazadas",
+ "mwoauthmanageconsumers-linkexpired": "solicitudes expiradas",
+ "mwoauthmanageconsumers-linkapproved": "solicitudes aprobadas",
+ "mwoauthmanageconsumers-linkdisabled": "solicitudes desactivadas",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-l-approved": "Lista de consumidores aprobados actualmente",
+ "mwoauthmanageconsumers-l-disabled": "Lista de consumidores desactivados actualmente",
+ "mwoauthmanageconsumers-none-proposed": "No hay consumidores propuestos en esta lista.",
+ "mwoauthmanageconsumers-none-rejected": "No hay consumidores propuestos en esta lista.",
+ "mwoauthmanageconsumers-none-expired": "No hay consumidores propuestos en esta lista.",
+ "mwoauthmanageconsumers-none-approved": "Ningún consumidor cumple con este criterio.",
+ "mwoauthmanageconsumers-none-disabled": "Ningún consumidor cumple con este criterio.",
+ "mwoauthmanageconsumers-name": "Consumidor",
+ "mwoauthmanageconsumers-user": "Editor",
+ "mwoauthmanageconsumers-description": "Descripción",
+ "mwoauthmanageconsumers-email": "Correo electrónico de contacto",
+ "mwoauthmanageconsumers-consumerkey": "Clave de consumidor",
+ "mwoauthmanageconsumers-lastchange": "Último cambio",
+ "mwoauthmanageconsumers-review": "revisar/gestionar",
+ "mwoauthmanageconsumers-confirm-legend": "Gestionar consumidor de OAuth",
+ "mwoauthmanageconsumers-action": "Cambiar el estado:",
+ "mwoauthmanageconsumers-approve": "Aprobada",
+ "mwoauthmanageconsumers-reject": "Rechazada",
+ "mwoauthmanageconsumers-rsuppress": "Rechazada y suprimida",
+ "mwoauthmanageconsumers-disable": "Desactivada",
+ "mwoauthmanageconsumers-dsuppress": "Desactivada y suprimida",
+ "mwoauthmanageconsumers-reenable": "Aprobada",
+ "mwoauthmanageconsumers-reason": "Motivo:",
+ "mwoauthmanageconsumers-confirm-submit": "Actualizar el estado del consumidor",
+ "mwoauthmanageconsumers-success-approved": "Se aprobó la solicitud.",
+ "mwoauthmanageconsumers-success-rejected": "Se rechazó la solicitud.",
+ "mwoauthmanageconsumers-success-disabled": "El consumidor se ha desactivado.",
+ "mwoauthmanageconsumers-search-name": "consumidores con este nombre",
+ "mwoauthmanageconsumers-search-publisher": "consumidores de este usuario",
+ "oauthlistconsumers": "Lista de aplicaciones OAuth",
+ "mwoauthlistconsumers-legend": "Explorar aplicaciones OAuth",
+ "mwoauthlistconsumers-view": "detalles",
+ "mwoauthlistconsumers-none": "No se encontraron aplicaciones en base a estos criterios.",
+ "mwoauthlistconsumers-name": "Nombre de la aplicación",
+ "mwoauthlistconsumers-version": "Versión de consumidor",
+ "mwoauthlistconsumers-user": "Editor",
+ "mwoauthlistconsumers-description": "Descripción",
+ "mwoauthlistconsumers-wiki": "Proyecto aplicable",
+ "mwoauthlistconsumers-callbackurl": "«URL de devolución de llamada» de OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Permitir al consumidor especificar una devolución de llamadas en las solicitudes y utilizar el URL anterior como prefijo obligatorio.",
+ "mwoauthlistconsumers-grants": "Permisos aplicables",
+ "mwoauthlistconsumers-basicgrantsonly": "(solo acceso básico)",
+ "mwoauthlistconsumers-status": "Estado",
+ "mwoauth-consumer-stage-any": "cualquiera",
+ "mwoauthlistconsumers-status-proposed": "propuesto",
+ "mwoauthlistconsumers-status-approved": "aprobado",
+ "mwoauthlistconsumers-status-disabled": "desactivado",
+ "mwoauthlistconsumers-status-rejected": "rechazado",
+ "mwoauthlistconsumers-status-expired": "caducado",
+ "mwoauthlistconsumers-rclink": "Cambios recientes por esta aplicación",
+ "oauthmanagemygrants": "Gestionar aplicaciones conectadas",
+ "mwoauthmanagemygrants-text": "Esta página muestra las aplicaciones que pueden utilizar tu cuenta. Para cualquier aplicación, el alcance de su acceso está limitado por los permisos que se le otorgaron al momento de autorizarla. Si has autorizado una aplicación para que acceda a varios proyectos en tu nombre, verás ajustes separados a continuación por cada uno de los proyectos.\n\nLas aplicaciones conectadas acceden a tu cuenta mediante el protocolo OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Más información sobre las aplicaciones conectadas])</span>",
+ "mwoauthmanagemygrants-navigation": "Navegación:",
+ "mwoauthmanagemygrants-showlist": "Lista de aplicaciones conectadas",
+ "mwoauthmanagemygrants-none": "No hay aplicaciones conectadas con tu cuenta.",
+ "mwoauthmanagemygrants-user": "Editor:",
+ "mwoauthmanagemygrants-description": "Descripción",
+ "mwoauthmanagemygrants-wikiallowed": "Permitida en el proyecto:",
+ "mwoauthmanagemygrants-grantsallowed": "Concesiones permitidas",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Permisos correspondientes permitidos:",
+ "mwoauthmanagemygrants-review": "gestionar el acceso",
+ "mwoauthmanagemygrants-revoke": "revocar el acceso",
+ "mwoauthmanagemygrants-grantaccept": "Concedido",
+ "mwoauthmanagemygrants-update-text": "Utiliza el formulario siguiente para modificar los permisos concedidos a una aplicación para que actúe en tu nombre.",
+ "mwoauthmanagemygrants-confirm-legend": "Gestionar aplicación conectada",
+ "mwoauthmanagemygrants-update": "Actualizar permisos",
+ "mwoauthmanagemygrants-renounce": "Desautorizar",
+ "mwoauthmanagemygrants-action": "Cambiar el estado:",
+ "mwoauthmanagemygrants-success-update": "Se han actualizado tus preferencias para esta aplicación.",
+ "mwoauthmanagemygrants-success-renounce": "Se ha revocado el acceso de esta aplicación a tu cuenta.",
+ "mwoauthmanagemygrants-basic-tooltip": "¿Por qué no puedo actualizar este permiso? Este permiso otorga a tu aplicación conectada unos permisos básicos que necesita para funcionar adecuadamente. Si no quieres que esta aplicación conectada tenga estos permisos, deberías revocar el acceso de esta aplicación.",
+ "mwoauthmanagemygrants-authonly-tooltip": "¿Por qué no puedo actualizar esta subvención? Si no quieres que esta aplicación conectada tenga este permiso, debes revocar el acceso de la aplicación.",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|propuso}} un consumidor OAuth (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|actualizó}} un consumidor OAuth (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|aprobó}} un consumidor OAuth por $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|rechazó}} un consumidor OAuth por $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|desactivó}} un consumidor OAuth por $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|reactivó}} un consumidor OAuth por $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|creó}} un consumidor OAuth con un solo propietario (clave de consumidor $4)",
+ "mwoauthconsumer-consumer-logpage": "Registro de consumidores OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Registro de aprobaciones, rechazos, y desactivaciones de consumidores OAuth registrados.",
+ "mwoauth-bad-request-missing-params": "Se produjo un problema al configurar esta aplicación conectada. Ponte en contacto con los desarrolladores de la aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Faltan parámetros de OAuth, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Algo salió mal. Tendrás que ponerte en contacto con el autor de la aplicación para obtener ayuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconocida, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Algo salió mal. Tendrás que [$1 ponerte en contacto] con el autor de la aplicación para obtener ayuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconocida, $2</span>",
+ "mwoauthdatastore-request-token-not-found": "Se produjo un error al conectar con esta aplicación. Sentimos las molestias.\nVuelve atrás e intenta conectar tu cuenta de nuevo, o ponte en contacto con el autor de la aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">No se encontró la ficha OAuth, $1</span>",
+ "mwoauthdatastore-request-token-already-used": "Esta solicitud ya se ha completado y no puede volver a enviarse.\nVuelve a la aplicación y prueba a conectar tu cuenta otra vez, o bien ponte en contacto con los autores de la aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ya se utilizó la ficha de OAuth; $1</span>",
+ "mwoauthdatastore-bad-source-ip": "La solicitud proviene de una dirección IP inválida.",
+ "mwoauthdatastore-bad-verifier": "El código de verificación proporcionado no es válido.",
+ "mwoauthdatastore-invalid-token-type": "El tipo de ficha solicitado no es válido.",
+ "mwoauthgrants-general-error": "Hay un error en la solicitud de OAuth.",
+ "mwoauthserver-bad-consumer": "«$1» no se ha aprobado como aplicación conectada. [$2 Ponte en contacto] con el autor de la aplicación para obtener ayuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Aplicación conectada por OAuth no aprobada, $3</span>",
+ "mwoauthserver-insufficient-rights": "Tu cuenta no tiene permitido utilizar aplicaciones conectadas. Ponte en contacto con un administrador del sitio para saber por qué.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Privilegios de usuario OAuth insuficientes, $1</span>",
+ "mwoauthserver-consumer-no-secret": "Se produjo un error al conectar con esta aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">El consumidor no tiene ninguna clave secreta; $1</span>",
+ "mwoauthserver-consumer-owner-only": "«$1» es una aplicación conectada reservada al propietario. Para recuperar el pase de acceso, consulta [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">El consumidor es exclusivo del propietario, $3</span>",
+ "mwoauth-invalid-authorization-title": "Error de autorización de OAuth",
+ "mwoauth-invalid-authorization": "Las cabeceras de autorización de tu petición no son válidas: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Las cabeceras de autorización de tu petición no son válidas para $1",
+ "mwoauth-invalid-authorization-invalid-user": "Las cabeceras de autorización de tu petición son para un usuario que no existe aquí",
+ "mwoauth-invalid-authorization-wrong-user": "Las cabeceras de autorización de tu petición son para otro usuario",
+ "mwoauth-invalid-authorization-not-approved": "La aplicación a la que estás intentando conectarte parece estar configurada de forma incorrecta. Contacta con el autor de \"$1\" para obtener ayuda.",
+ "mwoauth-invalid-authorization-blocked-user": "Las cabeceras de autorización en tu petición son para un usuario que está bloqueado",
+ "mwoauth-form-description-allwikis": "Hola, $1:\n\nPara completar tu solicitud, '''$2''' necesita permiso para realizar las acciones siguientes en tu nombre en todos los proyectos de este sitio:\n\n$4",
+ "mwoauth-form-description-onewiki": "Hola, $1:\n\nPara completar tu pedido, '''$2''' necesita permiso para realizar las acciones siguientes en tu nombre en ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hola, $1:\n\nPara completar tu solicitud, '''$2''' necesita permiso para acceder en tu nombre a información de todos los proyectos de este sitio. No se harán cambios con tu cuenta.",
+ "mwoauth-form-description-onewiki-nogrants": "Hola, $1:\n\nPara completar tu solicitud, '''$2''' necesita permiso para acceder en tu nombre a información de ''$4''. No se harán cambios con tu cuenta.",
+ "mwoauth-form-description-allwikis-privateinfo": "Hola, $1:\n\nPara completar la solicitud, '''$2''' necesita permiso para acceder a datos sobre ti, como tu nombre verdadero y tu dirección de correo electrónico, en todos los proyectos de este sitio. No se realizará ninguna modificación a través de tu cuenta.",
+ "mwoauth-form-description-onewiki-privateinfo": "Hola, $1:\n\nPara completar la solicitud, '''$2''' necesita permiso para acceder a datos como tu nombre verdadero y tu dirección de correo electrónico, en ''$4''. No se realizará ninguna modificación a través de tu cuenta.",
+ "mwoauth-form-button-approve": "Permitir",
+ "mwoauth-form-button-cancel": "Cancelar",
+ "mwoauth-error": "Error de conexión de la aplicación",
+ "mwoauth-grants-heading": "Permisos solicitados:",
+ "mwoauth-grants-nogrants": "La aplicación no ha solicitado ningún permiso.",
+ "mwoauth-granttype-normal": "Solicita autorización para permisos específicos.",
+ "grant-mwoauth-authonly": "Solamente para la verificación de la identidad del usuario, sin capacidad de leer páginas ni de actuar en nombre de un usuario.",
+ "grant-mwoauth-authonlyprivate": "Para la verificación de la identidad del usuario con acceso al nombre real y a la dirección de correo electrónico, sin capacidad de leer páginas ni de actuar en nombre de un usuario.",
+ "mwoauth-listgrants-extra-summary": "== Concesiones específicas de OAuth ==\nEstas concesiones adicionales se aplican a usuarios de OAuth.",
+ "mwoauth-oauth-exception": "Ha ocurrido un error en el protocolo OAuth: $1",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback debe ser conjunto y se debe establecerse en \"oob\" (sensible a mayúsculas) o la configuración de devolución de llamada debe ser un prefijo de devolución de llamada.",
+ "right-mwoauthproposeconsumer": "Proponer consumidores OAuth nuevos",
+ "right-mwoauthupdateownconsumer": "Actualizar consumidores OAuth que controles",
+ "right-mwoauthmanageconsumer": "Gestionar consumidores OAuth",
+ "right-mwoauthsuppress": "Eliminar consumidores OAuth",
+ "right-mwoauthviewsuppressed": "Ver consumidores OAuth eliminados",
+ "right-mwoauthviewprivate": "Ver datos OAuth privados",
+ "right-mwoauthmanagemygrants": "Administrar concesiones OAuth",
+ "action-mwoauthmanageconsumer": "gestionar consumidores OAuth",
+ "action-mwoauthsuppress": "suprimir consumidores OAuth",
+ "action-mwoauthmanagemygrants": "administrar tus concesiones OAuth",
+ "action-mwoauthproposeconsumer": "proponer consumidores OAuth nuevos",
+ "action-mwoauthupdateownconsumer": "actualizar consumidores OAuth que controles",
+ "action-mwoauthviewprivate": "ver datos OAuth privados",
+ "action-mwoauthviewsuppressed": "ver consumidores OAuth eliminados",
+ "mwoauth-tag-reserved": "Las etiquetas que comienzan con <code>OAuth CID:</code> están reservadas para que las use OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Nota:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> es más seguro que las contraseñas de bots, y debería ser la opción prioritaria en caso de que el bot lo permitiese.",
+ "mwoauth-api-module-disabled": "El módulo «$1» no está disponible con OAuth.",
+ "echo-category-title-oauth-owner": "Desarrollo de OAuth",
+ "echo-category-title-oauth-admin": "Administrador de OAuth",
+ "echo-pref-tooltip-oauth-admin": "Notificarme sobre eventos relacionados con la revisión de aplicaciones OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|propuso}} una aplicación OAuth nueva: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|actualizó}} la aplicación OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|aprobó}} {{GENDER:$3|tu}} aplicación OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|rechazó}} {{GENDER:$3|tu}} aplicación OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|desactivó}} {{GENDER:$3|tu}} aplicación OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reactivó}} {{GENDER:$3|tu}} aplicación OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|propuso}} una aplicación OAuth nueva en {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|actualizó}} una aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|aprobó}} {{GENDER:$3|tu}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|rechazó}} {{GENDER:$3|tu}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|desactivó}} {{GENDER:$3|tu}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reactivó}} {{GENDER:$3|tu}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Revisión de la aplicación",
+ "notification-oauth-app-update-primary-link": "Revisión de la aplicación",
+ "notification-oauth-app-approve-primary-link": "Ver aplicación",
+ "notification-oauth-app-reject-primary-link": "Ver aplicación",
+ "notification-oauth-app-disable-primary-link": "Ver aplicación",
+ "notification-oauth-app-reenable-primary-link": "Ver aplicación",
+ "notification-oauth-app-body": "Motivo: $1",
+ "mwoauth-oauth2-is-confidential": "El cliente es confidencial",
+ "mwoauth-oauth2-granttype-auth-code": "Código de autorización",
+ "mwoauth-oauth2-granttype-refresh-token": "Refrescar token",
+ "mwoauth-oauth2-granttype-client-credentials": "Credenciales del cliente",
+ "mwoauth-oauth2-error-unauthorized-scope": "Esta aplicación no tiene permitido utilizar el ámbito «$1»",
+ "mwoauth-oauth2-invalid-access-token": "Token de acceso inválido",
+ "mwoauthconsumer-application-view": "Ver esta aplicación"
+}
diff --git a/OAuth/i18n/et.json b/OAuth/i18n/et.json
new file mode 100644
index 00000000..09c24ff1
--- /dev/null
+++ b/OAuth/i18n/et.json
@@ -0,0 +1,168 @@
+{
+ "@metadata": {
+ "authors": [
+ "Boxmein",
+ "Pikne"
+ ]
+ },
+ "mwoauth-desc": "Võimaldab kasutada OAuthi versioone 1.0a ja 2.0 API-volitamiseks.",
+ "mwoauth-prefs-managegrants": "Ühendatud rakendused:",
+ "mwoauth-prefs-managegrantslink": "Halda {{PLURAL:$1|üht ühendatud rakendust|$1 ühendatud rakendust|0=ühendatud rakendusi}}",
+ "mwoauth-consumer-allwikis": "Kõigis selle võrgukoha projektides",
+ "mwoauth-consumer-key": "Tarvituse võti:",
+ "mwoauth-consumer-name": "Rakenduse nimi:",
+ "mwoauth-consumer-version": "Tarvituse versioon:",
+ "mwoauth-consumer-user": "Väljaandja:",
+ "mwoauth-consumer-stage": "Praegune olek:",
+ "mwoauth-consumer-email": "E-posti kontaktaadress:",
+ "mwoauth-consumer-email-help": "Nähtav ainult neile, kes tarvitusi heaks kiidavad.",
+ "mwoauth-consumer-owner-only": "Seda tarvitust kasutab ainult $1.",
+ "mwoauth-consumer-owner-only-help": "Selle valiku korral kiidetakse tarvitus automaatselt heaks ja selle kasutamine lubatakse kasutajale $1. Teised kasutajad seda kasutada ei saa ja harilik volitamisvoog ei toimi. Selle tarvitusega sooritatud toiminguid ei märgistata.",
+ "mwoauth-consumer-description": "Rakenduse kirjeldus:",
+ "mwoauth-consumer-callbackurl": "OAuthi tagasisuunamis-URL:",
+ "mwoauth-consumer-callbackisprefix": "Luba määrata tarvituses tagasihelistamine ja kasuta nõutava eesliitena ülaltoodud tagasihelistamis-URL-i.",
+ "mwoauth-consumer-granttypes": "Taotlen järgmist tüüpi volitusi:",
+ "mwoauth-consumer-grantsneeded": "Rakendatavad volitused:",
+ "mwoauth-consumer-required-grant": "Tarvituse suhtes rakendatav",
+ "mwoauth-consumer-wiki": "Rakendamise projekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Selles projektis ($1)",
+ "mwoauth-consumer-restrictions": "Kasutuspiirangud:",
+ "mwoauth-consumer-restrictions-json": "Kasutuspiirangud (JSON):",
+ "mwoauth-consumer-rsakey": "Avalik RSA-võti (valikuline):",
+ "mwoauth-consumer-rsakey-help": "Sisesta avalik võti, et kasutada allkirjastamismeetodit RSA-SHA1. Jäta tühjaks, et kasutada meetodit HMAC-SHA1 juhusliku salajase tõendiga. Kui sa pole kindel, siis jäta väli tühjaks.",
+ "mwoauth-consumer-reason": "Põhjus:",
+ "mwoauth-consumer-developer-agreement": "Selle rakenduse esitamisel oled teadlik, et meil on õigus keelata sinu rakendus, õigus selle juurdepääsu sellele saidile piirata või juurdepääs eemaldada ning õigus toimida mis tahes muul meie meelest sobival moel, kui meile tundub, et sina või sinu rakendus rikuvad mis tahes reegleid, juhtnööre või selle saidi juhtpõhimõtteid. Me võime selle rakenduse staatust igal ajal oma äranägemise järgi ja ette teatamata muuta. Kui jätkad OAuthi kasutamist, siis tuleb sul neid muudatusi tunnistada.",
+ "mwoauth-invalid-access-token": "Antud võtmega juurdepääsutõend puudub.",
+ "mwoauth-consumer-grantshelp": "Iga volitus võimaldab juurdepääsu loetletud õigustele, mis on kasutajakontol juba olemas. Lisateavet leiad [[Special:ListGrants|volituste tabelist]].",
+ "mwoauth-consumer-stage-proposed": "ettepanek",
+ "mwoauth-consumer-stage-rejected": "tagasilükatud",
+ "mwoauth-consumer-stage-expired": "iganenud",
+ "mwoauth-consumer-stage-approved": "kinnitatud",
+ "mwoauth-consumer-stage-disabled": "keelatud",
+ "oauthconsumerregistration": "OAuthi tarvituse registreerimine",
+ "mwoauthconsumerregistration-navigation": "Navigeerimine:",
+ "mwoauthconsumerregistration-propose": "uue tarvituse ettepanek",
+ "mwoauthconsumerregistration-list": "minu tarvituste loend",
+ "mwoauthconsumerregistration-main": "pealeht",
+ "mwoauthconsumerregistration-propose-text": "Uue OAuthi tarvituse kohta ettepaneku tegemiseks peaks arendajad kasutama allpool olevat vormi (loe üksikasju [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth tarkvaralisa dokumentatsioonist]). Pärast selle vormi esitamist saad tõendi, mida sinu rakendus kasutab MediaWiki tarkvaras identimiseks. Enne kui teised kasutajad saavad sinu rakendust volitamiseks kasutada, peab OAuthi administraator selle heaks kiitma.\n\nMõned soovitused ja märkused:\n* Proovi kasutada nii vähe volitusi kui võimalik. Väldi volitusi, mida pole tegelikult vaja.\n* Versioonid on kujul \"peaversioon.kõrvalversioon.väljaanne\" (viimased kaks on valikulised). Need kasvavad, kui volitusi on vaja muuta.\n* Palun too ära avalik RSA-võti (PEM-vormingus), kui võimalik. Vastasel juhul tuleb kasutada salajast tõendit, mis on vähem turvaline.\n* Saad kasutada projekti identifikaatorit, et piirata tarvitus selle saidi ühele projektile (kõigi projektide jaoks kirjuta \"*\").",
+ "mwoauthconsumerregistration-maintext": "See lehekülg võimaldab arendajatel teha ettepanekuid OAuthi tarvitavate rakenduste registreerimiseks siin saidil. Samuti saavad arendajad siin leheküljel registreeritud rakendusi uuendada.\n\nSaad siin\n* [[Special:OAuthConsumerRegistration/propose|taotleda luba uuele tarvitusele]] ja\n* [[Special:OAuthConsumerRegistration/list|hallata enda olemasolevaid tarvitusi]].\n\nLisateavet OAuthi kohta leiad [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth tarkvaralisa dokumentatsioonist].",
+ "mwoauthconsumerregistration-propose-legend": "Uus OAuthi tarvitav rakendus",
+ "mwoauthconsumerregistration-update-legend": "OAuthi tarvitava rakenduse uuendamine",
+ "mwoauthconsumerregistration-propose-submit": "Tee tarvituse ettepanek",
+ "mwoauthconsumerregistration-update-submit": "Uuenda tarvitust",
+ "mwoauthconsumerregistration-none": "Sa ei halda ühtegi OAuthi tarvitust.",
+ "oauthmanageconsumers": "OAuthi tarvituste haldamine",
+ "mwoauthmanageconsumers-type": "Järjekorrad:",
+ "mwoauthmanageconsumers-showproposed": "ettepaneku staatuses taotlused",
+ "mwoauthmanageconsumers-showrejected": "tagasi lükatud taotlused",
+ "mwoauthmanageconsumers-showexpired": "iganenud taotlused",
+ "mwoauthmanageconsumers-main": "pealeht",
+ "mwoauthmanageconsumers-confirm-legend": "OAuthi tarvituse haldamine",
+ "mwoauthmanageconsumers-action": "Staatuse muutmine:",
+ "mwoauthmanageconsumers-approve": "Heaks kiidetud",
+ "mwoauthmanageconsumers-reject": "Tagasi lükatud",
+ "mwoauthmanageconsumers-confirm-submit": "Uuenda tarvituse staatust",
+ "oauthlistconsumers": "OAuthi-rakenduste loend",
+ "mwoauthlistconsumers-legend": "OAuthi-rakenduste sirvimine",
+ "mwoauthlistconsumers-view": "üksikasjad",
+ "mwoauthlistconsumers-name": "Rakenduse nimi",
+ "mwoauthlistconsumers-version": "Tarvituse versioon",
+ "mwoauthlistconsumers-user": "Väljaandja",
+ "mwoauthlistconsumers-description": "Kirjeldus",
+ "mwoauthlistconsumers-wiki": "Rakendamise projekt",
+ "mwoauthlistconsumers-callbackurl": "Rakenduse internetiaadress",
+ "mwoauthlistconsumers-callbackisprefix": "Luba määrata tarvituses tagasihelistamine ja kasuta nõutava eesliitena ülaltoodud tagasihelistamis-URL-i.",
+ "mwoauthlistconsumers-grants": "Rakendatavad volitused",
+ "mwoauthlistconsumers-basicgrantsonly": "(ainult põhijuurdepääs)",
+ "mwoauthlistconsumers-status": "Olek",
+ "mwoauth-consumer-stage-any": "ükskõik",
+ "mwoauthlistconsumers-status-proposed": "ettepanek",
+ "mwoauthlistconsumers-status-approved": "kinnitatud",
+ "mwoauthlistconsumers-status-disabled": "keelatud",
+ "mwoauthlistconsumers-status-rejected": "tagasilükatud",
+ "mwoauthlistconsumers-status-expired": "iganenud",
+ "mwoauthlistconsumers-rclink": "Selle rakendusega tehtud viimased muudatused",
+ "oauthmanagemygrants": "Ühendatud rakenduste haldamine",
+ "mwoauthmanagemygrants-text": "Siin leheküljel on loetletud kõik rakendused, mida ühenduses sinu kontoga saab kasutada. Kõigi nende rakendamise ulatus on piiratud volitustega, mille rakendusega sidusid, kui nõustusid seda enda nimel kasutama. Kui volitasid rakenduse enda nimel kasutuse eri sõsarprojektides eraldi, näed allpool iga projekti jaoks eraldi häälestust.\n\nRakendused kasutavad ühenduseks sinu kontoga OAuthi protokolli. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Lisateave ühendatud rakenduste kohta])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigeerimine:",
+ "mwoauthmanagemygrants-showlist": "ühendatud rakenduste loend",
+ "mwoauthmanagemygrants-none": "Puuduvad sinu kontoga ühendatud rakendused.",
+ "mwoauthmanagemygrants-user": "Väljaandja:",
+ "mwoauthmanagemygrants-description": "Kirjeldus",
+ "mwoauthmanagemygrants-wikiallowed": "Lubatud järgmises projektis:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Rakenduse lubatud volitused:",
+ "mwoauthmanagemygrants-review": "halda juurdepääsu",
+ "mwoauthmanagemygrants-revoke": "tühista juurdepääs",
+ "mwoauthmanagemygrants-grantaccept": "Volitatud",
+ "mwoauthmanagemygrants-update-text": "Kasuta seda vormi, et muuta sinu nimel toimivate rakenduste volitusi.",
+ "mwoauthmanagemygrants-revoke-text": "Kasuta seda vormi, et tühistada sinu nimel toimivate rakenduste volitused.",
+ "mwoauthmanagemygrants-confirm-legend": "Ühendatud rakenduse haldamine",
+ "mwoauthmanagemygrants-update": "Uuenda volitusi",
+ "mwoauthmanagemygrants-renounce": "Tühista volitused",
+ "mwoauthmanagemygrants-success-update": "Sinu eelistused selle rakenduse jaoks on uuendatud.",
+ "mwoauthmanagemygrants-success-renounce": "Selle rakenduse juurdepääs sinu kontole on tühistatud.",
+ "mwoauthmanagemygrants-basic-tooltip": "Miks ma ei saa seda volitust uuendada? Sellega on ühendatud rakendusele antud põhilised õigused, mida on vaja, et rakendus õigesti toimiks. Kui sa ei taha, et ühendatud rakendusel oleks need õigused, siis peaksid tühistama selle rakenduse juurdepääsu.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Sinu}} muudatused selle rakendusega",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Sinu}} toimingud selle rakendusega",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|tegi ettepaneku}} OAuthi tarvituse kohta (tarvituse võti $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|uuendas}} OAuthi tarvitust (tarvituse võti $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|kiitis heaks}} kasutaja $3 OAuthi tarvituse (tarvituse võti $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|lükkas tagasi}} kasutaja $3 OAuthi tarvituse (tarvituse võti $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|keelas}} kasutaja $3 OAuthi tarvituse (tarvituse võti $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|lubas uuesti}} kasutaja $3 OAuthi tarvituse (tarvituse võti $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|koostas}} ainult omanikule mõeldud OAuthi tarvituse (tarvituse võti $4)",
+ "mwoauthconsumer-consumer-logpage": "OAuthi tarvituste logi",
+ "mwoauthconsumer-consumer-logpagetext": "Registreeritud OAuthi tarvituste heakskiitmise, tagasilükkamise ja keelamise logi.",
+ "mwoauthdatastore-request-token-not-found": "Selle rakendusega ühendumise juures läks kahjuks midagi valesti.\nMine tagasi ja ürita uuesti oma kontot ühendada või võta ühendust rakenduse autoriga.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuthi tõendit ei leitud, $1</span>",
+ "mwoauthdatastore-bad-source-ip": "Taotlus saadeti ebakorrektselt IP-aadressilt.",
+ "mwoauthserver-bad-consumer-key": "Selle rakendusega ühendumise juures läks kahjuks midagi valesti.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Tundmatu OAuthi-võti, $1</span>",
+ "mwoauth-form-description-allwikis": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' luba teha sinu nimel kõigis selle võrgukoha projektides järgmisi toiminguid:\n\n$4",
+ "mwoauth-form-description-onewiki": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' luba teha sinu nimel võrgukohas ''$4'' järgmisi toiminguid:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' luba juurdepääsuks teabele sinu nimel kõigis selle võrgukoha projektides. Sinu kontot ei kasutata muudatuste tegemiseks.",
+ "mwoauth-form-description-onewiki-nogrants": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' luba juurdepääsuks teabele sinu nimel võrgukohas $4. Sinu kontot ei kasutata muudatuste tegemiseks.",
+ "mwoauth-form-description-allwikis-privateinfo": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' kõigis selle võrgukoha projektides luba juurdepääsuks teabele sinu kohta, sealhulgas juurdepääsuks sinu pärisnimele ja e-posti aadressile. Sinu kontot ei kasutata muudatuste tegemiseks.",
+ "mwoauth-form-description-onewiki-privateinfo": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' võrgukohas ''$4'' luba juurdepääsuks teabele, sealhulgas juurdepääsuks sinu pärisnimele ja e-posti aadressile. Sinu kontot ei kasutata muudatuste tegemiseks.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' kõigis selle võrgukoha projektides luba juurdepääsuks teabele sinu kohta, sealhulgas juurdepääsuks sinu e-posti aadressile. Sinu kontot ei kasutata muudatuste tegemiseks.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Tere, $1.\n\nSelleks et sinu päringule vastata, vajab '''$2''' võrgukohas ''$4'' luba juurdepääsuks teabele, sealhulgas juurdepääsuks sinu e-posti aadressile. Sinu kontot ei kasutata muudatuste tegemiseks.",
+ "mwoauth-form-button-approve": "Luba",
+ "mwoauth-form-button-cancel": "Loobu",
+ "mwoauth-acceptance-cancelled": "Sa pole lubanud rakendusel \"$1\" juurdepääsu oma kontole. \"$1\" ei tööta, kui sa ei luba juurdepääsu. Saad minna tagasi rakenduse \"$1\" juurde või [[Special:OAuthManageMyGrants|hallata]] ühendatud rakendusi.",
+ "mwoauth-granttype-normal": "Taotlus kindlate lubade volitamiseks.",
+ "grant-mwoauth-authonly": "Ainult kasutaja identsuskontroll, ei saa lehekülgi lugeda ega kasutaja nimel toimida.",
+ "grant-mwoauth-authonlyprivate": "Ainult kasutaja identsuskontroll juurdepääsuga pärisnimele ja e-posti aadressile, ei saa lehekülgi lugeda ega kasutaja nimel toimida.",
+ "mwoauth-listgrants-extra-summary": "== OAuthile eriomased volitused ==\n\nNeed täiendavad volitused on rakendatavad OAuthi tarvituste juures.",
+ "right-mwoauthproposeconsumer": "Esitada uusi OAuthi-rakendusi",
+ "right-mwoauthupdateownconsumer": "Uuendada OAuthi-rakendusi, mida valdad",
+ "right-mwoauthmanageconsumer": "Hallata OAuthi-rakendusi",
+ "right-mwoauthmanagemygrants": "Hallata OAuthi-volitusi",
+ "action-mwoauthmanageconsumer": "OAuthi-rakendusi hallata",
+ "action-mwoauthmanagemygrants": "oma OAuthi-volitusi hallata",
+ "action-mwoauthproposeconsumer": "esitada uusi OAuthi-rakendusi",
+ "action-mwoauthupdateownconsumer": "uuendada OAuthi-rakendusi, mida valdad",
+ "mwoauth-botpasswords-note": "<strong>Märkus:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> on robotiparoolidest turvalisem ja seda tuleks eelistada alati, kui robot seda toetab.",
+ "echo-category-title-oauth-owner": "OAuthi arendus",
+ "echo-pref-tooltip-oauth-owner": "Teavita mind sündmustest, mis on seotud OAuthi rakendustega, mille olen koostanud.",
+ "echo-category-title-oauth-admin": "OAuthi haldus",
+ "echo-pref-tooltip-oauth-admin": "Teavita mind sündmustest, mis on seotud OAuthi rakenduste ülevaatusega.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|tegi ettepaneku}} uue OAuthi rakenduse kohta: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|uuendas}} OAuthi rakendust $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|kiitis heaks}} {{GENDER:$3|sinu}} OAuthi rakenduse ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|lükkas tagasi}} {{GENDER:$3|sinu}} OAuthi rakenduse ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|keelas}} {{GENDER:$3|sinu}} OAuthi rakenduse ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|lubas uuesti}} {{GENDER:$3|sinu}} OAuthi rakenduse ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|tegi ettepaneku}} uue OAuthi rakenduse kohta saidil {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|uuendas}} OAuthi rakendust saidil {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|kiitis heaks}} {{GENDER:$3|sinu}} OAuthi rakenduse saidil {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|lükkas tagasi}} {{GENDER:$3|sinu}} OAuthi rakenduse saidil {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|keelas}} {{GENDER:$3|sinu}} OAuthi rakenduse saidil {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|lubas uuesti}} {{GENDER:$3|sinu}} OAuthi rakenduse saidil {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Vaata rakendus üle",
+ "notification-oauth-app-update-primary-link": "Vaata rakendus üle",
+ "notification-oauth-app-approve-primary-link": "Vaata rakendust",
+ "notification-oauth-app-reject-primary-link": "Vaata rakendust",
+ "notification-oauth-app-disable-primary-link": "Vaata rakendust",
+ "notification-oauth-app-reenable-primary-link": "Vaata rakendust",
+ "notification-oauth-app-body": "Põhjus: $1",
+ "mwoauth-oauth-version": "OAuthi protokolli versioon:",
+ "mwoauthconsumer-application-view": "vaata seda rakendust"
+}
diff --git a/OAuth/i18n/eu.json b/OAuth/i18n/eu.json
new file mode 100644
index 00000000..e97e4b62
--- /dev/null
+++ b/OAuth/i18n/eu.json
@@ -0,0 +1,49 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sator",
+ "Subi"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Lotutako aplikazioak:",
+ "mwoauth-prefs-managegrantslink": "Kudeatu lotutako {{PLURAL:$1|aplikazioa|$1 aplikazioak}}",
+ "mwoauth-consumer-name": "Aplikazioaren izena:",
+ "mwoauth-consumer-reason": "Arrazoia:",
+ "mwoauth-consumer-stage-proposed": "proposatuta",
+ "mwoauth-consumer-stage-rejected": "ukatuta",
+ "mwoauth-consumer-stage-expired": "iraungita",
+ "mwoauth-consumer-stage-approved": "onartuta",
+ "mwoauth-consumer-stage-disabled": "ezgaituta",
+ "mwoauth-consumer-stage-suppressed": "ezabatuta",
+ "mwoauthconsumerregistration-navigation": "Nabigazioa:",
+ "mwoauthconsumerregistration-main": "Nagusia",
+ "mwoauthconsumerregistration-description": "Deskribapena",
+ "mwoauthconsumerregistration-email": "Harremanetarako e-posta",
+ "mwoauthconsumerregistration-stage": "Egoera",
+ "mwoauthconsumerregistration-lastchange": "Azken aldaketa",
+ "mwoauthconsumerregistration-manage": "kudeatu",
+ "mwoauthmanageconsumers-type": "Ilarak:",
+ "mwoauthmanageconsumers-main": "Nagusia",
+ "mwoauthmanageconsumers-description": "Deskribapena",
+ "mwoauthmanageconsumers-lastchange": "Azken aldaketa",
+ "mwoauthmanageconsumers-reason": "Arrazoia:",
+ "oauthlistconsumers": "Zerrendatu OAuth aplikazioak",
+ "mwoauthlistconsumers-view": "xehetasunak",
+ "mwoauthlistconsumers-name": "Aplikazioaren izena",
+ "mwoauthlistconsumers-description": "Deskribapena",
+ "mwoauthlistconsumers-status": "Egoera",
+ "mwoauth-consumer-stage-any": "edozein",
+ "mwoauthlistconsumers-status-proposed": "proposatuta",
+ "mwoauthlistconsumers-status-approved": "onartuta",
+ "mwoauthlistconsumers-status-disabled": "ezgaituta",
+ "mwoauthlistconsumers-status-rejected": "ukatuta",
+ "mwoauthlistconsumers-status-expired": "iraungita",
+ "oauthmanagemygrants": "Kudeatu lotutako aplikazioak",
+ "mwoauthmanagemygrants-description": "Deskribapena",
+ "mwoauth-form-description-allwikis": "Kaixo $1,\n\nZure eskaera osatze aldera, '''$2'''(e)k baimena behar du zure izenean ondorengo ekintzak egin ahal izateko gune honetako proiektu guzti hauetan:\n\n$4",
+ "mwoauth-form-description-onewiki": "Kaixo $1,\n\nZure eskaera osatze aldera, '''$2''' aplikazioak baimena behar du hurrengo ekintzak zure izenean egin ahal izateko ''$4'' proiektuan:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Kaixo $1,\n\nZure eskaera osatze aldera, '''$2'''(e)k baimena behar du zure izenean gune honetako proiektu guztien informazioa eskuratzeko. Zure kontuan ez da aldaketarik egingo.",
+ "mwoauth-form-button-approve": "Baimendu",
+ "mwoauth-form-button-cancel": "Utzi",
+ "notification-oauth-app-body": "Arrazoia: $1"
+}
diff --git a/OAuth/i18n/fa.json b/OAuth/i18n/fa.json
new file mode 100644
index 00000000..fdf54256
--- /dev/null
+++ b/OAuth/i18n/fa.json
@@ -0,0 +1,255 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ahmad252",
+ "Alirezaaa",
+ "Armin1392",
+ "Calak",
+ "Dalba",
+ "Ebraminio",
+ "FarsiNevis",
+ "Hosseinblue",
+ "Huji",
+ "Ladsgroup",
+ "Mardetanha",
+ "Mjbmr",
+ "Omid.koli",
+ "Physicsch",
+ "Reza1615",
+ "Taha"
+ ]
+ },
+ "oauth": "OAuth",
+ "mwoauth-desc": "اجازهٔ استفاده از رابط برنامه‌نویسی هویت‌سنجی OAuth ۱.۰ را می‌دهد",
+ "mwoauth-verified": "این برنامه اجازهٔ دسترسی به مدیاویکی از طرف شما را دارد.\n\nبرای تکمیل این فرایند، مقدار تأیید را برای برنامه فراهم کنید: '''$1'''",
+ "mwoauth-db-readonly": "دیتابیس OAuth موقتا قفل شده‌است. لطفا چند دقیقه دیگر تلاش کنید.",
+ "mwoauth-missing-field": "مقدار ناموجود برای مورد \"$1\"",
+ "mwoauth-invalid-field": "مقدار نامعتبر برای مورد \"$1\"",
+ "mwoauth-invalid-field-generic": "مقدار نامعتبر ارائه شده",
+ "mwoauth-field-hidden": "(این اطلاعات پنهان است)",
+ "mwoauth-field-private": "(این اطلاعات خصوصی است)",
+ "mwoauth-prefs-managegrants": "برنامه‌های متصل:",
+ "mwoauth-prefs-managegrantslink": "مدیریت {{PLURAL:$1|برنامهٔ وصل‌شده}}",
+ "mwoauth-consumer-allwikis": "همه پروژه‌ها در این وب‌گاه",
+ "mwoauth-consumer-key": "کلید مصرف‌کننده:",
+ "mwoauth-consumer-name": "نام برنامه:",
+ "mwoauth-consumer-version": "نسخهٔ مصرف‌کنندگان:",
+ "mwoauth-consumer-user": "ناشر:",
+ "mwoauth-consumer-stage": "وضعیت فعلی:",
+ "mwoauth-consumer-email": "نشانی ایمیل تماس:",
+ "mwoauth-consumer-owner-only-label": "فقط-مالکان:",
+ "mwoauth-consumer-description": "توضیحات برنامه:",
+ "mwoauth-consumer-callbackurl": "نشانی وب فراخوانی‌شده توسط OAuth:",
+ "mwoauth-consumer-grantsneeded": "اعطاهای مربوط:",
+ "mwoauth-consumer-required-grant": "قابل استفاده برای مصرف‌کننده",
+ "mwoauth-consumer-wiki": "پروژهٔ مربوط:",
+ "mwoauth-consumer-wiki-thiswiki": "پروژه فعلی ($1)",
+ "mwoauth-consumer-restrictions": "محدودیت‌ها در استفاده:",
+ "mwoauth-consumer-restrictions-json": "محدودیت استفاده از (JSON):",
+ "mwoauth-consumer-rsakey": "کلید عمومی RSA (اختیاری):",
+ "mwoauth-consumer-secretkey": " نشانه‌های مخفی مصرف‌کننده:",
+ "mwoauth-consumer-accesstoken": "رمز دسترسی:",
+ "mwoauth-consumer-reason": "دلیل:",
+ "mwoauth-consumer-email-unconfirmed": "نشانی ایمیل شما هنوز تأیید نشده است.",
+ "mwoauth-consumer-email-mismatched": "آدرس ایمیل ارائه شده باید با حساب کاربریتان مطابقت داشته باشد.",
+ "mwoauth-consumer-alreadyexists": "مصرف‌کنندگان با این ترکیب ناشر/نام/نسخه در حال حاضر وجود دارد",
+ "mwoauth-consumer-alreadyexistsversion": "مصرف‌کننده‌ای با این ترکیب نام/منتشرکننده با نسخه‌ای برابر یا بالاتر در حال حاضر موجود است («$1»)",
+ "mwoauth-consumer-not-accepted": "اطلاعات یک درخواست معلق مصرف‌کننده را نمی‌توان روزآمد کرد",
+ "mwoauth-consumer-not-proposed": "مصرف‌کننده در حال حاضر پیشنهاد نشده‌است",
+ "mwoauth-consumer-not-disabled": "مصرف‌کننده در حال حاضر غیرفعال شده‌است",
+ "mwoauth-consumer-not-approved": "مصرف‌کننده تأییدنشده (ممکن است غیرفعال شده باشد)",
+ "mwoauth-missing-consumer-key": "کلید مصرف‌کننده‌ای فراهم نشده‌است.",
+ "mwoauth-invalid-consumer-key": "مصرف‌کننده‌ای با کلید داده‌شده موجود نیست.",
+ "mwoauth-invalid-access-token": "نشان عدم دسترسی با کلید داده موجود است.",
+ "mwoauth-invalid-access-wrongwiki": "مصرف‌کننده فقط می‌تواند در پروژه «$1» استفاده شود.",
+ "mwoauth-consumer-conflict": "یک نفر خصوصیات این مصرف‌کننده را از زمانی که شما دیدید، تغییر داده است. لطفاً دوباره تلاش کنید. شاید لازم باشد سیاهه تغییرات را مشاهده کنید.",
+ "mwoauth-consumer-grantshelp": "هر اعطا امکان دسترسی به فهرست دسترسی‌های کاربران را می دهد. برای اطلاعات بیشتر [[Special:ListGrants|جدول اعطاها]] را مشاهده کنید.",
+ "mwoauth-consumer-stage-proposed": "پیشنهاد شده",
+ "mwoauth-consumer-stage-rejected": "رد شده",
+ "mwoauth-consumer-stage-expired": "منقضی شده",
+ "mwoauth-consumer-stage-approved": "تأییدشده",
+ "mwoauth-consumer-stage-disabled": "غیرفعال شد",
+ "mwoauth-consumer-stage-suppressed": "توقیف شد",
+ "oauthconsumerregistration": "ثبت نام OAuth مصرف‌کننده",
+ "mwoauthconsumerregistration-navigation": "ناوبری:",
+ "mwoauthconsumerregistration-propose": "پیشنهاد مصرف کننده تازه",
+ "mwoauthconsumerregistration-list": "فهرست مصرف‌کننده من",
+ "mwoauthconsumerregistration-main": "اصلی",
+ "mwoauthconsumerregistration-propose-text": "توسعه‌دهندگان باید از فرم زیر برای پاسخ به یک مصرف‌کنندهٔ جدید اُآت استفاده کنند (مشاهدهٔ [//www.mediawiki.org/wiki/Extension:OAuth extension documentation] برای جزئیات بیشتر). پس از ارائهٔ این فرم، شما نشانه‌ای دریافت خواهید کرد که درخواست شما برای شناسایی خود به مدیاویکی مورد استفاده قرار خواهد گرفت. سرپرست اُآت نیاز به تأیید درخواست شما دارد قبل از اینکه بتواند توسط کاربران دیگر تصویب شود.\nچند توصیه و اظهار:\n*سعی به استفاده ممکن به اندازهٔ کمک‌های مالی اندک. اجتناب از کمک‌هایی که اکنون در حقیقت مورد نیاز نیستند.\n*نسخه‌ها فرم \"اصلی،فرعی،آزاد\" هستند (آخرین دو اختیاری بودن) و افزایش به اندازهٔ تغییرات کمک مورد نیاز هستند.\n*لطفاً یک کلید عمومی آر‌اس‌ای (در فرمت پی‌ای‌ام) اگر ممکن است ارائه کنید؛ در غیر این‌ صورت یک نشانهٔ سری (امنیت کمتر) باید مورد استفاده قرار گیرد.\n*شما می‌توانید از یک شناسهٔ پروژه برای محدود کردن مصرف‌کننده به یک پروژه‌ٔ تک در این سایت استفاده کنید (برای همهٔ پروژه‌ها از \"*\" استفاده کنید).",
+ "mwoauthconsumerregistration-update-text": "فرم زیر را برای بروزرسانی جوانب مصرف‌کنندگان OAuth که شما کنترل می‌کنید، استفاده کنید.\n\nهمه مقادیر برروی مقادیر گذشته ذخیره می‌شوند. هیچ مقداری را خالی نگذارید مگر اینکه قصد پاک کردن آن مقادیر را داشته باشید.",
+ "mwoauthconsumerregistration-maintext": "این صفحه برای اجازه‌دادن به اهداف توسعه‌دهندگان است و به‌روز رسانی درخواست‌های مصرف‌کنندهٔ OAuth در ثبت این وب‌گاه است.\nاز اینجا، شما می‌توانید:\n* [[Special:OAuthConsumerRegistration/propose|درخوایت یک نشانهٔ برای مصرف‌کنندهٔ جدید]].\n* [[Special:OAuthConsumerRegistration/list|مدیریت مصرف‌کنندگان موجود خود]].\nبرای اطلاعات بیشتر دربارهٔ‌ OAuth، لطفاً [//www.mediawiki.org/wiki/Extension:OAuth extension documentation] را مشاهده کنید.",
+ "mwoauthconsumerregistration-propose-legend": "برنامهٔ مصرف‌کننده تازه OAuth",
+ "mwoauthconsumerregistration-update-legend": "روزآمدسازی برنامهٔ مصرف‌کنندهٔ OAuth",
+ "mwoauthconsumerregistration-propose-submit": "پشیناهد مصرف‌کننده",
+ "mwoauthconsumerregistration-update-submit": "روزآمدسازی مصرف‌کننده",
+ "mwoauthconsumerregistration-none": "شما هیچ مصرف‌کنندهٔ OAuth را کنترل نمی‌کنید.",
+ "mwoauthconsumerregistration-name": "مصرف‌کننده",
+ "mwoauthconsumerregistration-user": "ناشر",
+ "mwoauthconsumerregistration-description": "توضیحات",
+ "mwoauthconsumerregistration-email": "ایمیل تماس",
+ "mwoauthconsumerregistration-consumerkey": "کلید مصرف‌کننده",
+ "mwoauthconsumerregistration-stage": "وضعیت",
+ "mwoauthconsumerregistration-lastchange": "آخرین تغییر",
+ "mwoauthconsumerregistration-manage": "مدیریت",
+ "mwoauthconsumerregistration-resetsecretkey": "بازنشانی کلید رمز به مقداری جدید",
+ "mwoauthconsumerregistration-proposed": "درخواست مصرف کننده OAuth شما دریافت شده‌است.\nشما نشانهٔ مصرف کنندهٔ '''$1''' و یک نشانهٔ سری '''$2''' را تعیین کرده‌اید.\n''لطفاً این‌ها را برای منابع آینده ضبط کنید.''",
+ "mwoauthconsumerregistration-updated": "ثبت مصرف‌کنندهٔ OAuth شما روزآمدسازی شد.",
+ "mwoauthconsumerregistration-secretreset": "شما یک نشانهٔ سری مصرف‌کنندهٔ '''$1''' را تعیین کرده‌اید. ''لطفاً این را برای منبع آینده ضبط کنید.''",
+ "oauthmanageconsumers": "مدیریت مصرف‌کننده‌های OAuth",
+ "mwoauthmanageconsumers-notloggedin": "شما برای دسترسی به این صفحه باید وارد شده باشید.",
+ "mwoauthmanageconsumers-type": "صف:",
+ "mwoauthmanageconsumers-showproposed": "درخواست‌های پیشنهادی",
+ "mwoauthmanageconsumers-showrejected": "درخواست‌های رد شده",
+ "mwoauthmanageconsumers-showexpired": "درخواست‌های منقضی‌شده",
+ "mwoauthmanageconsumers-linkproposed": "درخواست‌های پیشنهادی",
+ "mwoauthmanageconsumers-linkrejected": "درخواست‌های رد شده",
+ "mwoauthmanageconsumers-linkexpired": "درخواست‌های منقضی‌شده",
+ "mwoauthmanageconsumers-linkapproved": "درخواست‌های تاییدشده",
+ "mwoauthmanageconsumers-linkdisabled": "درخواست‌های غیرفعال‌شده",
+ "mwoauthmanageconsumers-main": "اصلی",
+ "mwoauthmanageconsumers-maintext": "این صفحه برای کنترل درخواست‌های برنامهٔ مصرف‌کننده (مشاهدهٔ http://oauth.net) و مدیریت تآٔسیس مصرف‌کنند‌گان OAuth است.",
+ "mwoauthmanageconsumers-queues": "یک صف تأیید مصرف‌کننده از زیر انتخاب کنید:",
+ "mwoauthmanageconsumers-q-proposed": "به صف‌کردن درخواست پینشهاد مصرف‌کننده",
+ "mwoauthmanageconsumers-q-rejected": "صف درخواست‌های رده شده مصرف‌کننده",
+ "mwoauthmanageconsumers-q-expired": "صف درخواست‌های منقضی‌شده مصرف‌کننده",
+ "mwoauthmanageconsumers-lists": "انتخاب فهرست وضعیت مصرف‌کننده از پائین:",
+ "mwoauthmanageconsumers-l-approved": "فهرست مصرف‌کنندگان در حال حاضر تأییدشده",
+ "mwoauthmanageconsumers-l-disabled": "فهرست مصرف‌کنندگان در حال حاضر غیرفعال",
+ "mwoauthmanageconsumers-none-proposed": "در این فهرست مصرف‌کنندگان وجود ندارد.",
+ "mwoauthmanageconsumers-none-rejected": "در این فهرست مصرف‌کنندگان وجود ندارد.",
+ "mwoauthmanageconsumers-none-expired": "در این فهرست مصرف‌کنندگان وجود ندارد.",
+ "mwoauthmanageconsumers-none-approved": "هیچ مصرف‌کننده‌ای شامل این محدوده نمی‌شود.",
+ "mwoauthmanageconsumers-none-disabled": "هیچ مصرف‌کننده‌ای این بخش را ندیده‌است.",
+ "mwoauthmanageconsumers-name": "مصرف‌کننده",
+ "mwoauthmanageconsumers-user": "ناشر",
+ "mwoauthmanageconsumers-description": "توضیحات",
+ "mwoauthmanageconsumers-email": "ایمیل تماس",
+ "mwoauthmanageconsumers-consumerkey": "کلید مصرف‌کننده",
+ "mwoauthmanageconsumers-lastchange": "آخرین تغییر",
+ "mwoauthmanageconsumers-review": "بررسی/مدیریت",
+ "mwoauthmanageconsumers-confirm-text": "از این فرم برای تأیید، رد، غیرفعال یا دوباره فعال کردن این مصرف‌کننده استفاده کنید.",
+ "mwoauthmanageconsumers-confirm-legend": "مدیریت مصرف OAuth",
+ "mwoauthmanageconsumers-action": "تغییر وضعیت:",
+ "mwoauthmanageconsumers-approve": "تأییدشده",
+ "mwoauthmanageconsumers-reject": "رد شده",
+ "mwoauthmanageconsumers-rsuppress": "رد و توقیف شد",
+ "mwoauthmanageconsumers-disable": "غیرفعال شد",
+ "mwoauthmanageconsumers-dsuppress": "غیرفعال و فرونشانی شد",
+ "mwoauthmanageconsumers-reenable": "تأییدشده",
+ "mwoauthmanageconsumers-reason": "دلیل:",
+ "mwoauthmanageconsumers-confirm-submit": "روزآمدسازی وضعیت مصرف‌کننده",
+ "mwoauthmanageconsumers-success-approved": "درخواست تأیید شد.",
+ "mwoauthmanageconsumers-success-rejected": "درخواست رد شده است.",
+ "mwoauthmanageconsumers-success-disabled": "مصرف‌کننده غیرفعال شده است.",
+ "mwoauthmanageconsumers-success-reanable": "مصرف‌کننده دوباره فعال شده است.",
+ "mwoauthmanageconsumers-search-name": "مصرف‌کنندگان با این نام",
+ "mwoauthmanageconsumers-search-publisher": "مصرف‌کنندگان توسط این کاربر",
+ "oauthlistconsumers": "فهرست برنامه‌های کاربردی OAuth",
+ "mwoauthlistconsumers-legend": "مرور برنامه‌های کاربردی OAuth",
+ "mwoauthlistconsumers-view": "جزئیات",
+ "mwoauthlistconsumers-none": "هیچ برنامه‌ای از این محدوده استفاده نمی‌کند.",
+ "mwoauthlistconsumers-name": "نام برنامه کاربردی",
+ "mwoauthlistconsumers-version": "نسخهٔ مصرف‌کننده",
+ "mwoauthlistconsumers-user": "ناشر",
+ "mwoauthlistconsumers-description": "توضیحات",
+ "mwoauthlistconsumers-wiki": "پروژه‌های قابل اجرا",
+ "mwoauthlistconsumers-callbackurl": "نشانی پاسخ OAuth",
+ "mwoauthlistconsumers-grants": "اعطاهای اجرا شدنی",
+ "mwoauthlistconsumers-basicgrantsonly": "(فقط دسترسی پایه)",
+ "mwoauthlistconsumers-status": "وضعیت",
+ "mwoauth-consumer-stage-any": "همه",
+ "mwoauthlistconsumers-status-proposed": "پیشنهاد شده",
+ "mwoauthlistconsumers-status-approved": "تأییدشده",
+ "mwoauthlistconsumers-status-disabled": "غیرفعال شد",
+ "mwoauthlistconsumers-status-rejected": "رد شده",
+ "mwoauthlistconsumers-status-expired": "منقضی شده",
+ "oauthmanagemygrants": "مدیریت برنامه‌های کاربردی متصل",
+ "mwoauthmanagemygrants-text": "در این صفحه فهرست همه برنامه‌های کاربردیی که از کاربری شما می‌تواند برای ویرایش استفاده کند فهرست شده‌است. هدف از اجازه‌ای که به برنامه داده‌اید که به جای شما ویرایش کند، محدودسازی دامنه عملکرد آن برنامه به جای شما است. اگر به صورت جداگانه برای پروژه‌های خواهر دسترسی به برنامه‌های کاربردی داده‌اید، تنظیمات دسترسی‌ها را به صورت مجزا در پائین مشاهده خواهید کرد.\n\n\nارتباط با کاربری شما بر پایه پروتکل OAuth است.<span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth یادگیری بیشتر در مورد برنامه های کاربردی متصل])</span>",
+ "mwoauthmanagemygrants-navigation": "ناوبری:",
+ "mwoauthmanagemygrants-showlist": "فهرست برنامه‌های متصل",
+ "mwoauthmanagemygrants-none": "هیچ برنامه‌ای به حسابتان متصل نیست.",
+ "mwoauthmanagemygrants-user": "ناشر:",
+ "mwoauthmanagemygrants-description": "توضیحات",
+ "mwoauthmanagemygrants-wikiallowed": "مجاز در پروژه:",
+ "mwoauthmanagemygrants-grants": "اعطاهای اجرا شدنی",
+ "mwoauthmanagemygrants-grantsallowed": "اعطاهای مجاز",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "کمک‌های مالی مجاز:",
+ "mwoauthmanagemygrants-review": "مدیریت دسترسی",
+ "mwoauthmanagemygrants-revoke": "لغو دسترسی",
+ "mwoauthmanagemygrants-grantaccept": "اعطا",
+ "mwoauthmanagemygrants-update-text": "استفاده از فرم زیر برای تغییر مجوز‌های اعطا شده به برنامه برای عمل از طرف شما.",
+ "mwoauthmanagemygrants-revoke-text": "استفاده از فرم زیر برای لغو دسترسی برای درخواست به عمل از طرف شما.",
+ "mwoauthmanagemygrants-confirm-legend": "مدیریت برنامه‌های متصل",
+ "mwoauthmanagemygrants-update": "اعطای بارگذاری",
+ "mwoauthmanagemygrants-renounce": "برداشتن ثبت",
+ "mwoauthmanagemygrants-action": "تغییر وضعیت:",
+ "mwoauthmanagemygrants-confirm-submit": "به‌روزرسانی وضعیت رمز دسترسی",
+ "mwoauthmanagemygrants-success-update": "ترجیحات شما در خصوص این برنامهٔ کاربردی، روزآمد شد.",
+ "mwoauthmanagemygrants-success-renounce": "رمز دسترسی برای این مصرف‌کننده حذف شد.",
+ "mwoauthmanagemygrants-basic-tooltip": "چرا من نمی‌توانم این کمک را به روز کنم؟ این کمک به درخواست متصل شما مجوزهای اساسی می‌دهد که نیاز به عملکرد مناسب می‌دهد. اگز شما این درخواست متصل را برای داشتن این حقوق نمی‌خواهید، شما باید دسترسی برنامه را لغو کنید.",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|پیشنهاد }} یک مصرف‌کننده OAuth داد (کد مصرف‌کننده $4)",
+ "logentry-mwoauthconsumer-update": "$1 یک مصرف‌کنندهٔ OAuth (با کد مصرف‌کنندهٔ $4) را روزآمد {{GENDER:$2|کرد}}",
+ "logentry-mwoauthconsumer-approve": "$1 یک مصرف‌کننده OAuth برای $3 {{GENDER:$2|تأیید کرد}} (کد مصرف‌کننده $4)",
+ "logentry-mwoauthconsumer-reject": "$1 یک مصرف‌کننده OAuth برای $3 {{GENDER:$2|ردکرد}} (کد مصرف‌کننده $4)",
+ "logentry-mwoauthconsumer-disable": "$1 یک مصرف‌کننده OAuth برای $3 {{GENDER:$2|غیرفعال کرد}} (کد مصرف‌کننده $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 یک مصرف‌کننده OAuth برای $3 {{GENDER:$2|دوباره فعال کرد}} (کد مصرف‌کننده $4)",
+ "mwoauthconsumer-consumer-logpage": "سیاهه مصرف OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "سیاههٔ تائیدها، ردها و غیرفعال‌سازی مصرف‌کنندگان رد شده OAuth.",
+ "mwoauth-bad-request-missing-params": "با عرض پوزش،‌ چیزی در پیکربندی این برنامهٔ متصل‌شده اشتباه شده‌است. <span class=\"plainlinks\">[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth نشانی پشتیبانی]</span> برای کمک به حل آن.\n\n<span class=\"plainlinks mw-mwoautherror-details\">پارامترهای گم‌شدهٔ OAuth،&rlm; [https://www.mediawiki.org/wiki/Help:OAuth/Errors#E001 E001]</span>",
+ "mwoauth-bad-request-invalid-action": "پوزش، خطایی رخ داد. برای تماس با برنامه‌نویس برای کمک به آدرس زیر مراجعه کنید.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unknown URL, [https://www.mediawiki.org/wiki/Help:OAuth/Errors#E002 E002]</span>",
+ "mwoauth-bad-request-invalid-action-contact": "پوزش، خطایی رخ داد. برای [$1 تماس] با برنامه‌نویس برای کمک به آدرس زیر مراجعه کنید.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Unknown URL, [https://www.mediawiki.org/wiki/Help:OAuth/Errors#E003 E003]</span>",
+ "mwoauthdatastore-access-token-not-found": "هیچ اعطای تأیید برای رمز اختیار یافت نشد.",
+ "mwoauthdatastore-request-token-not-found": "با عرض پوزش، چیزی در اتصال به این برنامه اشتباه شده‌است.\nبازگردید و برای اتصال حسابتان دوباره امتحان کنید یا با نویسندهٔ برنامه تماس حاصل کنید.\n\n<span class=\"plainlinks mw-mwoautherror-details\"> نشانهٔ OAuth یافت نشد، [https://www.mediawiki.org/wiki/Help:OAuth/Errors#E004 E004]</span>",
+ "mwoauthdatastore-bad-token": "هیچ نشانه مطابق درخواست شما یافت نشد.",
+ "mwoauthdatastore-bad-source-ip": "این درخواست از یک نشانی آی‌پی نامعتبر آمده‌است.",
+ "mwoauthdatastore-bad-verifier": "کد تأیید صحت ارائه‌شده معتبر نیست.",
+ "mwoauthdatastore-invalid-token-type": "نوع رمز درخواستی نامعتبر است.",
+ "mwoauthgrants-general-error": "خطایی در درخواست OAuth شما وجود داشت.",
+ "mwoauthserver-bad-consumer": "«$1» دیگر یک برنامه متصل‌شده پذیرفته‌شده نیست، برای کمک با نویسندهٔ برنامه [$2 اتصال حاصل کنید].\n\n<span class=\"plainlinks mw-mwoautherror-details\">نرم‌افزار متصل‌شدهٔ OAuth مورد تأیید نیست، [https://www.mediawiki.org/wiki/Help:OAuth/Errors#E005 E005]</span>",
+ "mwoauthserver-bad-consumer-key": "با عرض پوزش، مشکلی در طی اتصال به این برنامه پیش آمد.\n\n<span class=\"plainlinks mw-mwoautherror-details\">کلید ناشناخته OAuth، [https://www.mediawiki.org/wiki/Help:OAuth/Errors#E006 E006]</span>",
+ "mwoauthserver-insufficient-rights": "حساب شما اجازهٔ استفاده از برنامه‌های متصل را ندارد، با سرپرست سایت خود برای فهمیدن چرا، در تماس باشید.\n<span class=\"plainlinks mw-mwoautherror-details\"> حقوق ناکافی کاربر اُآت،[https://www.mediawiki.org/wiki/Help:OAuth/Errors#E007 E007]</span>",
+ "mwoauthserver-invalid-request-token": "علامت نامعتبر در درخواست شما.",
+ "mwoauthserver-invalid-user": "برای استفاده از برنامه‌های متصل در این سایت، شما باید یک حساب کاربری در سراسر پروژه‌ها داشته باشید. هنگامی که شما یک حساب در همهٔ پروژه‌ها داشته‌باشید، می‌توانید برای اتصال «$1» مجدد امتحان کنید.\n<span class=\"plainlinks mw-mwoautherror-details\">ورود به سامانه یکپارچهٔ مورد نیاز،[https://www.mediawiki.org/wiki/Help:OAuth/Errors#E008 E008]</span>",
+ "mwoauth-invalid-authorization-title": "خطای مجوز OAuth",
+ "mwoauth-invalid-authorization": "سرصفحه‌های اختیار در درخواست شما، معتبر نیستند: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "سرصفحه‌های اختیار در درخواست شما، برای $1 معتبر نیستند",
+ "mwoauth-invalid-authorization-invalid-user": "سرصفحه‌های اختیار در درخواست شما، برای کاربری ناموجود است.",
+ "mwoauth-invalid-authorization-wrong-user": "سرصفحه‌های اختیار در درخواست شما، برای کاربر متفاوت است.",
+ "mwoauth-invalid-authorization-not-approved": "برنامه‌ای شما سعی به اتصال به آن را دارید، به نظر می‌رسد به‌درستی تنظیم نشده‌است. برای کمک با نویسندهٔ \"$1\" در تماس باشید.",
+ "mwoauth-invalid-authorization-blocked-user": "سرایندهای اصالت‌سنجیِ درخواست شما متعلق به کاربری بسته‌شده است",
+ "mwoauth-form-description-allwikis": "درود $1،\n\n'''$2''' خواهان انجام این اعمال از طرف شما در همهٔ پروژه‌های این وبگاه است:\n\n$4",
+ "mwoauth-form-description-onewiki": "درود $1،\n\n'''$2''' خواهان انجام این اعمال از طرف شما در ''$4'' است:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "درود $1،\n\n'''$2''' خواهان اخذ دسترسی اولیه‌ای از طرف شما در همهٔ پروژه‌های این وبگاه است.",
+ "mwoauth-form-description-onewiki-nogrants": "درود $1،\n\n'''$2''' خواهان اخذ دسترسی اولیه‌ای از طرف شما در ''$4'' است.",
+ "mwoauth-form-button-approve": "پذیرفتن",
+ "mwoauth-form-button-cancel": "انصراف",
+ "mwoauth-error": "خطای اتصال برنامه کاربردی",
+ "mwoauth-grants-heading": "دسترسی‌های درخواست شده:",
+ "mwoauth-grants-nogrants": "برنامه نیازمند هیچ دسترسی نخواسته است.",
+ "mwoauth-acceptance-cancelled": "شما انتخاب کرده‌اید که به \"$1\" برای دسترسی به حساب شما اجازه ندهید. \"$1\" کار نخواهد کرد مگر اینکه اجازهٔ دسترسی دهید. شما می‌توانید به \"$1\" بازگردید یا [[Special:OAuthManageMyGrants|مدیریت]] برنامه‌های متصل خود.",
+ "mwoauth-oauth-exception": "خطایی در پروتکل OAuth رخ داد :$1",
+ "mwoauth-callback-not-oob": "oauth_callback باید تنظیم شود، و باید به \"اوب\" تنظیم شود (حساس به مورد)",
+ "right-mwoauthproposeconsumer": "پیشنهاد مصرف‌کننده جدید OAuth",
+ "right-mwoauthupdateownconsumer": "روزآمدسازی مصرف‌کننده‌های OAuth که شما کنترل می‌کنید",
+ "right-mwoauthmanageconsumer": "مدیریت مصرف‌کننده‌های OAuth",
+ "right-mwoauthsuppress": "توقیف مصرف‌کنندهٔ OAuth",
+ "right-mwoauthviewsuppressed": "مشاهده مصرف‌کننده‌های OAuth توقیف‌شده",
+ "right-mwoauthviewprivate": "مشاهدهٔ داده‌های شخصی OAuth",
+ "right-mwoauthmanagemygrants": "مدیریت OAuthهای اعطاشده",
+ "action-mwoauthmanageconsumer": "مدیریت مصرف‌کننده‌های OAuth",
+ "action-mwoauthmanagemygrants": "مدیریت اعطاشده‌های OAuth شما",
+ "action-mwoauthproposeconsumer": "پیشنهاد مصرف‌کنندهٔ جدید OAuth",
+ "action-mwoauthupdateownconsumer": "روزآمدسازی مصرف‌کننده‌های OAuth که شما کنترل می‌کنید",
+ "action-mwoauthviewsuppressed": "نمایش مصرف‌کننده‌های OAuth توقیف‌شده",
+ "mwoauth-botpasswords-note": "<strong>نکته:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> نسبت به پسورد ربات امنیت بیشتری دارد و زمانی که ربات از آن پشتیبانی کند، از OAuth به جای پسورد ربات استفاده کنید.",
+ "mwoauth-api-module-disabled": "پودمان «$1» با OAuth قابل انجام نیست.",
+ "echo-category-title-oauth-owner": "توسعه OAuth",
+ "echo-category-title-oauth-admin": "مدیر OAuth",
+ "notification-oauth-app-propose-primary-link": "بازبینی برنامه",
+ "notification-oauth-app-update-primary-link": "بازبینی برنامه",
+ "notification-oauth-app-approve-primary-link": "نمایش برنامه",
+ "notification-oauth-app-reject-primary-link": "نمایش برنامه",
+ "notification-oauth-app-disable-primary-link": "نمایش برنامه",
+ "notification-oauth-app-reenable-primary-link": "نمایش برنامه",
+ "notification-oauth-app-body": "دلیل: $1"
+}
diff --git a/OAuth/i18n/fi.json b/OAuth/i18n/fi.json
new file mode 100644
index 00000000..3ee5888a
--- /dev/null
+++ b/OAuth/i18n/fi.json
@@ -0,0 +1,80 @@
+{
+ "@metadata": {
+ "authors": [
+ "01miki10",
+ "Nike",
+ "Pxos",
+ "Pyscowicz",
+ "Stryn"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Liitetyt sovellukset:",
+ "mwoauth-prefs-managegrantslink": "Hallinnoi {{PLURAL:$1|$1 yhdistettyä sovellusta|$1 yhdistettyä sovellusta|0=yhdistettyä sovellusta}}",
+ "mwoauth-consumer-allwikis": "Kaikki projektit tällä sivustolla",
+ "mwoauth-consumer-key": "Asiakkaan avain:",
+ "mwoauth-consumer-name": "Sovelluksen nimi:",
+ "mwoauth-consumer-version": "Asiakkaan versio:",
+ "mwoauth-consumer-user": "Julkaisija:",
+ "mwoauth-consumer-stage": "Nykyinen tila:",
+ "mwoauth-consumer-email": "Sähköpostiosoite:",
+ "mwoauth-consumer-description": "Sovelluksen kuvaus:",
+ "mwoauth-consumer-wiki": "Sovellettava projekti:",
+ "mwoauth-consumer-wiki-thiswiki": "Nykyinen projekti ($1)",
+ "mwoauth-consumer-accesstoken": "Käyttöoikeuspoletti:",
+ "mwoauth-consumer-reason": "Syy:",
+ "mwoauth-consumer-stage-proposed": "ehdotettu",
+ "mwoauth-consumer-stage-rejected": "hylätty",
+ "mwoauth-consumer-stage-expired": "vanhentunut",
+ "mwoauth-consumer-stage-approved": "hyväksytty",
+ "mwoauth-consumer-stage-disabled": "poistettu käytöstä",
+ "mwoauth-consumer-stage-suppressed": "häivytetty",
+ "oauthconsumerregistration": "OAuth asiakasrekisteröinti",
+ "mwoauthconsumerregistration-navigation": "Valikko:",
+ "mwoauthconsumerregistration-propose": "Ehdota uutta asiakasta",
+ "mwoauthconsumerregistration-list": "Asiakaslistani",
+ "mwoauthconsumerregistration-update-submit": "Päivitä asiakas",
+ "mwoauthconsumerregistration-user": "Julkaisija",
+ "mwoauthconsumerregistration-description": "Kuvaus",
+ "mwoauthconsumerregistration-consumerkey": "Asiakkaan avain",
+ "mwoauthconsumerregistration-stage": "Tila",
+ "mwoauthmanageconsumers-user": "Julkaisija",
+ "mwoauthmanageconsumers-description": "Kuvaus",
+ "mwoauthmanageconsumers-consumerkey": "Asiakkaan avain",
+ "mwoauthmanageconsumers-lastchange": "Viimeisin muutos",
+ "mwoauthmanageconsumers-action": "Muuta tilaa:",
+ "mwoauthmanageconsumers-reason": "Syy:",
+ "oauthlistconsumers": "Luettele OAuth-sovellukset",
+ "mwoauthlistconsumers-legend": "Selaa OAuth-sovelluksia",
+ "mwoauthlistconsumers-view": "tiedot",
+ "mwoauthlistconsumers-name": "Sovelluksen nimi",
+ "mwoauthlistconsumers-version": "Asiakkaan versio",
+ "mwoauthlistconsumers-user": "Julkaisija",
+ "mwoauthlistconsumers-description": "Kuvaus",
+ "mwoauthlistconsumers-wiki": "Sovellettava projekti",
+ "mwoauthlistconsumers-status": "Tila",
+ "mwoauth-consumer-stage-any": "mikä tahansa",
+ "mwoauthlistconsumers-status-proposed": "ehdotettu",
+ "mwoauthlistconsumers-status-approved": "hyväksytty",
+ "mwoauthlistconsumers-status-disabled": "poistettu käytöstä",
+ "mwoauthlistconsumers-status-rejected": "hylätty",
+ "mwoauthlistconsumers-status-expired": "vanhentunut",
+ "oauthmanagemygrants": "Liitettyjen sovellusten hallinnointi",
+ "mwoauthmanagemygrants-navigation": "Valikko:",
+ "mwoauthmanagemygrants-showlist": "Liitettyjen sovellusten luettelo",
+ "mwoauthmanagemygrants-none": "Yhtään sovellusta ei ole liitetty tunnukseesi.",
+ "mwoauthmanagemygrants-user": "Julkaisija:",
+ "mwoauthmanagemygrants-description": "Kuvaus",
+ "mwoauthmanagemygrants-wikiallowed": "Sallittu projektissa:",
+ "mwoauthmanagemygrants-action": "Muuta tilaa:",
+ "mwoauthmanagemygrants-confirm-submit": "Päivitä käyttöoikeuspoletin tila",
+ "mwoauth-form-description-allwikis": "Hei $1,\n\n'''$2''' haluaisi tehdä seuraavat toiminnot puolestasi kaikissa tämän sivuston projekteissa:\n\n$4",
+ "mwoauth-form-description-onewiki": "Hei $1,\n\n'''$2''' haluaisi tehdä seuraavat toiminnot puolestasi sivustolla ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hei $1,\n\nPyynnön suorittamiseksi, '''$2''' tarvitsee luvan päästäkseen puolestasi kaikkien tämän sivuston projektien tietoihin. Tililläsi ei tehdä muutoksia.",
+ "mwoauth-form-button-approve": "Salli",
+ "mwoauth-form-button-cancel": "Peruuta",
+ "mwoauth-error": "Sovelluksen yhteysvirhe",
+ "mwoauth-grants-heading": "Pyydetyt oikeudet:",
+ "right-mwoauthviewprivate": "Nähdä yksityisiä OAuth-tietoja",
+ "echo-category-title-oauth-owner": "OAuth-kehitys",
+ "notification-oauth-app-body": "Syy: $1"
+}
diff --git a/OAuth/i18n/fit.json b/OAuth/i18n/fit.json
new file mode 100644
index 00000000..edcaf595
--- /dev/null
+++ b/OAuth/i18n/fit.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Pyscowicz"
+ ]
+ },
+ "mwoauthconsumerregistration-navigation": "Navikeerinki:",
+ "mwoauthmanageconsumers-lastchange": "Viimisin muutos",
+ "mwoauthlistconsumers-navigation": "Navikeerinki:",
+ "mwoauthmanagemygrants-navigation": "Navikeerinki:"
+}
diff --git a/OAuth/i18n/fr.json b/OAuth/i18n/fr.json
new file mode 100644
index 00000000..8239445a
--- /dev/null
+++ b/OAuth/i18n/fr.json
@@ -0,0 +1,345 @@
+{
+ "@metadata": {
+ "authors": [
+ "Crochet.david",
+ "Dr Brains",
+ "Eneelk",
+ "Fitoschido",
+ "Gomoko",
+ "Jean-Frédéric",
+ "Linedwell",
+ "Louperivois",
+ "Ltrlg",
+ "McDutchie",
+ "Od1n",
+ "Orlodrim",
+ "Pols12",
+ "Thibaut120094",
+ "Urhixidur",
+ "VIGNERON",
+ "Verdy p",
+ "Wladek92",
+ "Wyz"
+ ]
+ },
+ "mwoauth-desc": "Autorise l’utilisation de OAuth 1.0a et 2.0 pour l’authentification de l’API",
+ "mwoauth-nosubpage-explanation": "OAuth est un mécanisme qui permet aux applications externes d’identifier un utilisateur de {{SITENAME}} ou d’agir en son nom après avoir reçu la permission de cet utilisateur.\n\nPour que cette page fasse quelque chose, d’autres paramètres sont nécessaires. Si vous avez été redirigé ici par une application externe, il s’agit probablement d’une erreur de cette application; veuillez en contacter l’auteur.",
+ "mwoauth-verified": "L’application peut maintenant accéder à MediaWiki en votre nom.\n\nPour terminer le processus, veuillez fournir cette valeur de vérification à l’application : '''$1'''",
+ "mwoauth-db-readonly": "La base de données OAuth est temporairement verrouillée. Veuillez réessayer dans quelques minutes.",
+ "mwoauth-missing-field": "Valeur manquante pour le champ « $1 »",
+ "mwoauth-invalid-field": "Valeur invalide fournie pour le champ « $1 »",
+ "mwoauth-invalid-field-generic": "Valeur non valide fournie",
+ "mwoauth-field-hidden": "(cette information est masquée)",
+ "mwoauth-field-private": "(cette information est privée)",
+ "mwoauth-prefs-managegrants": "Applications connectées :",
+ "mwoauth-prefs-managegrantslink": "Gérer {{PLURAL:$1|$1 application connectée|$1 applications connectées|0=les applications connectées}}",
+ "mwoauth-consumer-allwikis": "Tous les projets sur ce site",
+ "mwoauth-consumer-key": "Clé du consommateur :",
+ "mwoauth-consumer-name": "Nom de l'application :",
+ "mwoauth-consumer-version": "Version du consommateur :",
+ "mwoauth-consumer-user": "Éditeur :",
+ "mwoauth-consumer-stage": "Statut actuel :",
+ "mwoauth-consumer-email": "Adresse de courriel de contact :",
+ "mwoauth-consumer-email-help": "Visible uniquement par les personnes qui approuvent les nouveaux consommateurs",
+ "mwoauth-consumer-owner-only-label": "Réservé au propriétaire :",
+ "mwoauth-consumer-owner-only": "Réserver l’utilisation de ce consommateur à $1.",
+ "mwoauth-consumer-owner-only-help": "Si cette option est sélectionnée, le consommateur sera automatiquement approuvé et son utilisation par $1 sera acceptée. Il ne sera utilisable par aucun autre utilisateur et le processus d’autorisation habituel ne fonctionnera pas. Les actions effectuées en utilisant ce consommateur ne seront pas balisées.",
+ "mwoauth-consumer-description": "Description de l'application :",
+ "mwoauth-consumer-callbackurl": "URL de « rappel » pour OAuth :",
+ "mwoauth-consumer-callbackisprefix": "Permettre au consommateur de spécifier un rappel dans les requêtes et utiliser l’URL de « rappel » ci-dessus comme préfixe obligatoire.",
+ "mwoauth-consumer-granttypes": "Types d’autorisation demandés :",
+ "mwoauth-consumer-grantsneeded": "Autorisations possibles :",
+ "mwoauth-consumer-required-grant": "Applicable au consommateur",
+ "mwoauth-consumer-wiki": "Projet applicable :",
+ "mwoauth-consumer-wiki-thiswiki": "Projet actuel ($1)",
+ "mwoauth-consumer-restrictions": "Limitations d’utilisation :",
+ "mwoauth-consumer-restrictions-json": "Limitations d’utilisation (JSON) :",
+ "mwoauth-consumer-rsakey": "Clé RSA publique (facultatif) :",
+ "mwoauth-consumer-rsakey-help": "Saisir une clé publique pour utiliser la méthode de signature RSA-SHA1. Laisser vide pour utiliser HMAC-SHA1 avec un motif secret aléatoire. Si vous n’êtes pas sûr du choix, laisser vide.",
+ "mwoauth-consumer-secretkey": "Jeton secret du consommateur :",
+ "mwoauth-consumer-accesstoken": "Jeton d’accès :",
+ "mwoauth-consumer-reason": "Motif :",
+ "mwoauth-consumer-developer-agreement": "En soumettant cette application, vous acceptez que nous nous réservons le droit de la désactiver, de supprimer ou restreindre votre accès ou celui de votre application à ce site, et d’entreprendre toute autre action que nous estimons appropriée si nous estimons, à notre seule discrétion, que vous ou votre application violez une politique, une ligne directrice ou un principe de ce site. Nous pouvons modifier cette Politique des applications n’importe quand sans avis préalable, à notre seule discrétion et comme nous le jugeons nécessaire. Votre poursuite de l’utilisation de OAuth constitue une acceptation de ces modifications.",
+ "mwoauth-consumer-email-unconfirmed": "Votre adresse de courriel du compte n’a pas encore été confirmée.",
+ "mwoauth-consumer-email-mismatched": "L’adresse de courriel fournie doit correspondre à celle de votre compte.",
+ "mwoauth-consumer-alreadyexists": "Un consommateur avec cette combinaison de nom/version/éditeur existe déjà",
+ "mwoauth-consumer-alreadyexistsversion": "Un consommateur avec cette combinaison de nom/éditeur existe déjà avec une version égale ou supérieure (« $1 »)",
+ "mwoauth-consumer-not-accepted": "Impossible de mettre à jour les informations pour une demande de consommateur en cours",
+ "mwoauth-consumer-not-proposed": "Le consommateur n’est actuellement pas proposé",
+ "mwoauth-consumer-not-disabled": "Le consommateur n’est pas désactivé pour le moment",
+ "mwoauth-consumer-not-approved": "Le consommateur n’est pas approuvé (il peut avoir été désactivé)",
+ "mwoauth-missing-consumer-key": "Aucune clé de consommateur n’a été fournie.",
+ "mwoauth-invalid-consumer-key": "Aucun consommateur n’existe avec la clé fournie.",
+ "mwoauth-invalid-access-token": "Aucun jeton d’accès n’existe pour la clé fournie",
+ "mwoauth-invalid-access-wrongwiki": "Le consommateur ne peut être utilisé que sur le projet « $1 ».",
+ "mwoauth-consumer-conflict": "Quelqu’un a modifié les attributs de ce consommateur pendant que vous le consultiez. Veuillez réessayer. Vous pouvez aussi vérifier le journal des modifications.",
+ "mwoauth-consumer-grantshelp": "Chaque droit accorde l’accès aux droits d’utilisateur listés qu’un compte utilisateur possède déjà. Voyez le [[Special:ListGrants|tableau des droits]] pour plus d’informations.",
+ "mwoauth-consumer-stage-proposed": "proposé",
+ "mwoauth-consumer-stage-rejected": "rejeté",
+ "mwoauth-consumer-stage-expired": "expiré",
+ "mwoauth-consumer-stage-approved": "approuvé",
+ "mwoauth-consumer-stage-disabled": "désactivé",
+ "mwoauth-consumer-stage-suppressed": "supprimé",
+ "oauthconsumerregistration": "Inscription de consommateur OAuth",
+ "mwoauthconsumerregistration-navigation": "Navigation :",
+ "mwoauthconsumerregistration-propose": "Proposer un nouveau consommateur",
+ "mwoauthconsumerregistration-list": "Ma liste de consommateurs",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-text": "Les développeurs devraient utiliser le formulaire ci-dessous pour proposer un nouveau consommateur OAuth (voir la [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth documentation de l’extension] pour plus de détails). Après avoir validé ce formulaire, vous recevrez un jeton que votre application utilisera pour s’identifier auprès de MediaWiki. Un administrateur OAuth devra approuver votre application avant qu’elle ne puisse être approuvée par les autres utilisateurs.\n\nQuelques recommandations et remarques :\n* Essayez d’utiliser le moins de droits possibles. Évitez les droits qui ne sont pas vraiment nécessaires pour le moment.\n* Les versions sont de la forme « majeure.mineure.révision » (les deux derniers champs étant facultatifs) et croissent quand des changements dans les permissions sont nécessaires.\n* Veuillez fournir une clé publique RSA (au format PEM) si possible ; sinon, un jeton secret (moins sécurisé) vous sera assigné.\n* Vous pouvez utiliser un ID de projet pour limiter ce consommateur à un unique projet de ce site (utilisez « * » pour tous les projets).",
+ "mwoauthconsumerregistration-update-text": "Utilisez le formulaire ci-dessous pour mettre à jour les aspects d’un consommateur OAuth que vous contrôlez.\n\nToutes les valeurs ici écraseront les précédentes. Ne laissez aucun champ blanc sauf si vous désirez vraiment effacer ces valeurs.",
+ "mwoauthconsumerregistration-maintext": "Cette page sert à laisser les développeurs proposer et mettre à jour des applications consommatrices OAuth dans le registre de ce site.\n\nDepuis ici, vous pouvez :\n* [[Special:OAuthConsumerRegistration/propose|Demander un jeton pour un nouveau consommateur]].\n* [[Special:OAuthConsumerRegistration/list|Gérer vos consommateurs existants]].\n\nPour plus d’information sur OAuth, voyez la [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth documentation de l’extension].",
+ "mwoauthconsumerregistration-propose-legend": "Nouvelle application consommatrice OAuth",
+ "mwoauthconsumerregistration-update-legend": "Mettre à jour l’application consommatrice OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Proposer un consommateur",
+ "mwoauthconsumerregistration-update-submit": "Mettre à jour un consommateur",
+ "mwoauthconsumerregistration-none": "Vous ne contrôlez aucun consommateur OAuth.",
+ "mwoauthconsumerregistration-name": "Consommateur",
+ "mwoauthconsumerregistration-user": "Éditeur",
+ "mwoauthconsumerregistration-description": "Description",
+ "mwoauthconsumerregistration-email": "Courriel de contact",
+ "mwoauthconsumerregistration-consumerkey": "Clé du consommateur",
+ "mwoauthconsumerregistration-stage": "État",
+ "mwoauthconsumerregistration-lastchange": "Dernière modification",
+ "mwoauthconsumerregistration-manage": "gérer",
+ "mwoauthconsumerregistration-resetsecretkey": "Réinitialiser la clé secrète avec une nouvelle valeur",
+ "mwoauthconsumerregistration-proposed": "Votre demande de consommateur OAuth a été reçue.\n\nIl vous a été assigné un jeton de consommateur '''$1''' et un jeton secret '''$2'''. ''Veuillez les conserver pour des besoins ultérieurs.''",
+ "mwoauthconsumerregistration-created-owner-only": "Votre consommateur OAuth a été créé.\n\nVos jetons sont :\n; Jeton du consommateur : $1\n; Secret du consommateur : $2\n; Jeton d’accès : $3\n; Secret d’accès : $4\n<em>Veuillez les conserver pour pouvoir vous y référer ultérieurement.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "Votre client OAuth 2.0 a été créé.\n\nVos jetons sont :\n; Clé d’application client : $1\n; Secret de l’application client : $2\n; Jeton d’accès : $3\n;<em>Veuillez les enregistrer pour de futures références.</em>",
+ "mwoauthconsumerregistration-updated": "Votre registre de consommateur OAuth a été mis à jour.",
+ "mwoauthconsumerregistration-secretreset": "Un jeton secret de consommateur de '''$1''' vous a été assigné. ''Veuillez le conserver pour tout besoin ultérieur.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Vos jetons de consommateur OAuth ont été réinitialisés. Les nouveaux jetons sont :\n; Jeton du consommateur : $1\n; Secret du consommateur : $2\n; Jeton d’accès : $3\n; Secret d’accès : $4\n<em>Veuillez les conserver pour pouvoir vous y référer ultérieurement.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Vos jetons de consommateur OAuth 2.0 ont été réinitialisés. Les nouveaux jetons sont :\n; Jeton du consommateur : $1\n; Secret du consommateur : $2\n; Jeton d’accès : $3\n<em>Veuillez les enregistrer pour de futures références.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Vous devez confirmer votre adresse de messagerie avant de créer des applications OAuth.\nVeuillez fournir et valider votre adresse de messagerie via vos [[Special:Preferences|préférences utilisateur]].",
+ "oauthmanageconsumers": "Gérer les consommateurs OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Vous devez être connecté(e) pour accéder à cette page.",
+ "mwoauthmanageconsumers-type": "Files :",
+ "mwoauthmanageconsumers-showproposed": "Requêtes proposées",
+ "mwoauthmanageconsumers-showrejected": "Requêtes rejetées",
+ "mwoauthmanageconsumers-showexpired": "Requêtes expirées",
+ "mwoauthmanageconsumers-linkproposed": "requêtes proposées",
+ "mwoauthmanageconsumers-linkrejected": "requêtes rejetées",
+ "mwoauthmanageconsumers-linkexpired": "requêtes expirées",
+ "mwoauthmanageconsumers-linkapproved": "requêtes approuvées",
+ "mwoauthmanageconsumers-linkdisabled": "requêtes désactivées",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-maintext": "Cette page est conçue pour traiter les demandes d’applications consommatrices OAuth (voir http://oauth.net) et gérer les consommateurs OAuth existants.",
+ "mwoauthmanageconsumers-queues": "Sélectionner une file de confirmation de consommateur ci-dessous :",
+ "mwoauthmanageconsumers-q-proposed": "File des requêtes de consommateur proposés",
+ "mwoauthmanageconsumers-q-rejected": "File des requêtes de consommateur rejetées",
+ "mwoauthmanageconsumers-q-expired": "File des requêtes de consommateur expirées",
+ "mwoauthmanageconsumers-lists": "Sélectionner une liste d’états de consommateur ci-dessous :",
+ "mwoauthmanageconsumers-l-approved": "Liste des consommateurs actuellement approuvés",
+ "mwoauthmanageconsumers-l-disabled": "Liste des consommateurs actuellement désactivés",
+ "mwoauthmanageconsumers-none-proposed": "Aucun consommateur proposé dans cette liste.",
+ "mwoauthmanageconsumers-none-rejected": "Aucun consommateur proposé dans cette liste.",
+ "mwoauthmanageconsumers-none-expired": "Aucun consommateur proposé dans cette liste.",
+ "mwoauthmanageconsumers-none-approved": "Aucun consommateur ne répond à ce critère.",
+ "mwoauthmanageconsumers-none-disabled": "Aucun consommateur ne correspond à ce critère.",
+ "mwoauthmanageconsumers-name": "Consommateur",
+ "mwoauthmanageconsumers-user": "Éditeur",
+ "mwoauthmanageconsumers-description": "Description",
+ "mwoauthmanageconsumers-email": "Courriel de contact",
+ "mwoauthmanageconsumers-consumerkey": "Clé de consommateur",
+ "mwoauthmanageconsumers-lastchange": "Dernière modification",
+ "mwoauthmanageconsumers-review": "revoir/gérer",
+ "mwoauthmanageconsumers-confirm-text": "Utilisez ce formulaire pour approuver, rejeter, désactiver ou réactiver ce consommateur.",
+ "mwoauthmanageconsumers-confirm-legend": "Gérer le consommateur OAuth",
+ "mwoauthmanageconsumers-action": "Modifier l’état :",
+ "mwoauthmanageconsumers-approve": "Approuvé",
+ "mwoauthmanageconsumers-reject": "Rejeté",
+ "mwoauthmanageconsumers-rsuppress": "Rejeté et supprimé",
+ "mwoauthmanageconsumers-disable": "Désactivé",
+ "mwoauthmanageconsumers-dsuppress": "Désactivé et supprimé",
+ "mwoauthmanageconsumers-reenable": "Approuvé",
+ "mwoauthmanageconsumers-reason": "Motif :",
+ "mwoauthmanageconsumers-confirm-submit": "Mettre à jour l’état du consommateur",
+ "mwoauthmanageconsumers-success-approved": "La requête a été approuvée.",
+ "mwoauthmanageconsumers-success-rejected": "La requête a été rejetée.",
+ "mwoauthmanageconsumers-success-disabled": "Le consommateur a été désactivé.",
+ "mwoauthmanageconsumers-success-reanable": "Le consommateur a été réactivé.",
+ "mwoauthmanageconsumers-search-name": "consommateurs avec ce nom",
+ "mwoauthmanageconsumers-search-publisher": "consommateurs liés à cet utilisateur",
+ "oauthlistconsumers": "Lister les applications OAuth",
+ "mwoauthlistconsumers-legend": "Naviguer dans les applications OAuth",
+ "mwoauthlistconsumers-view": "détails",
+ "mwoauthlistconsumers-none": "Aucune application correspondant à ces critères n’a été trouvée.",
+ "mwoauthlistconsumers-name": "Nom de l’application",
+ "mwoauthlistconsumers-version": "Version du consommateur",
+ "mwoauthlistconsumers-user": "Éditeur",
+ "mwoauthlistconsumers-description": "Description",
+ "mwoauthlistconsumers-wiki": "Projet applicable",
+ "mwoauthlistconsumers-callbackurl": "« URL de rappel » de OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Permettre au consommateur de spécifier un rappel dans les requêtes et utiliser l’URL de « rappel » ci-dessus comme préfixe obligatoire.",
+ "mwoauthlistconsumers-grants": "Droits applicables",
+ "mwoauthlistconsumers-basicgrantsonly": "(accès de base uniquement)",
+ "mwoauthlistconsumers-status": "État",
+ "mwoauth-consumer-stage-any": "tous",
+ "mwoauthlistconsumers-status-proposed": "proposé",
+ "mwoauthlistconsumers-status-approved": "approuvé",
+ "mwoauthlistconsumers-status-disabled": "désactivé",
+ "mwoauthlistconsumers-status-rejected": "rejeté",
+ "mwoauthlistconsumers-status-expired": "expiré",
+ "mwoauthlistconsumers-navigation": "Navigation :",
+ "mwoauthlistconsumers-update-link": "Mettre à jour le consommateur",
+ "mwoauthlistconsumers-manage-link": "Gérer le consommateur",
+ "mwoauthlistconsumers-grants-link": "Gérer les droits",
+ "mwoauthlistconsumers-rclink": "Modifications récentes par cette application",
+ "oauthmanagemygrants": "Gérer les applications connectées",
+ "mwoauthmanagemygrants-text": "Cette page liste toutes les applications qui peuvent utiliser votre compte. Pour chacune de ces applications, son périmètre d’accès est limité par les droits que vous lui avez accordés quand vous l’avez autorisée à agir en votre nom. Si vous avez autorisé une application de façon séparée à accéder à différents projets « frères » en votre nom, alors vous verrez une configuration distincte pour chacun de ces projets ci-dessous.\n\nLes applications connectées accèdent à votre compte en utilisant le protocole OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth En savoir plus sur les applications connectées])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigation :",
+ "mwoauthmanagemygrants-showlist": "Liste des applications connectées",
+ "mwoauthmanagemygrants-none": "Il n’y a aucune application connectée à votre compte.",
+ "mwoauthmanagemygrants-user": "Éditeur :",
+ "mwoauthmanagemygrants-description": "Description",
+ "mwoauthmanagemygrants-wikiallowed": "Autorisé sur le projet :",
+ "mwoauthmanagemygrants-grants": "Droits applicables",
+ "mwoauthmanagemygrants-grantsallowed": "Droits accordés :",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Droits applicables accordés :",
+ "mwoauthmanagemygrants-review": "gérer l’accès",
+ "mwoauthmanagemygrants-revoke": "révoquer l’accès",
+ "mwoauthmanagemygrants-grantaccept": "Accordé",
+ "mwoauthmanagemygrants-update-text": "Utiliser le formulaire ci-dessous pour modifier les droits accordés à une application pour agir en votre nom.",
+ "mwoauthmanagemygrants-revoke-text": "Utiliser le formulaire ci-dessous pour révoquer le droit, pour une application, d’agir en votre nom.",
+ "mwoauthmanagemygrants-confirm-legend": "Gérer les applications connectées",
+ "mwoauthmanagemygrants-update": "Mettre à jour les droits",
+ "mwoauthmanagemygrants-renounce": "Ne plus autoriser",
+ "mwoauthmanagemygrants-action": "Modifier l’état :",
+ "mwoauthmanagemygrants-confirm-submit": "Mettre à jour l’état du jeton d’accès",
+ "mwoauthmanagemygrants-success-update": "Vos préférences pour cette application ont été mises à jour.",
+ "mwoauthmanagemygrants-success-renounce": "L'accès de l'application à votre compte a été supprimé.",
+ "mwoauthmanagemygrants-basic-tooltip": "Pourquoi ne puis-je pas mettre à jour cette autorisation ? Celle-ci donne à votre application connectée des droits de base dont elle a besoin pour fonctionner correctement. Si vous ne voulez pas que cette application connectée ait ces droits, vous devriez révoquer son accès.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Pourquoi ne puis-je pas mettre à jour ce droit ? Si vous ne voulez pas que cette application connectée ait ce droit, vous devriez révoquer son accès.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Vos}} modifications par cette application",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Vos}} actions par cette application",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|a proposé}} un consommateur OAuth (clé du consommateur $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|a mis à jour}} un consommateur OAuth (clé du consommateur $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|a approuvé}} un consommateur OAuth proposé par $3 (clé du consommateur $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|a rejeté}} un consommateur OAuth proposé par $3 (clé du consommateur $4)",
+ "logentry-mwoauthconsumer-disable": "$1 a désactivé un consommateur OAuth proposé par $3 (clé du consommateur $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|a réactivé}} un consommateur OAuth proposé par $3 (clé du consommateur $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|a créé}} un consommateur OAuth réservé au propriétaire (clé de consommateur $4)",
+ "log-action-filter-mwoauthconsumer": "Type d'action de consommateur OAuth :",
+ "log-action-filter-mwoauthconsumer-approve": "Approbation de consommateur OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Création d’un consommateur OAuth pour le propriétaire seul",
+ "log-action-filter-mwoauthconsumer-disable": "Désactivation de consommateur OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Proposition de consommateur OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Réactivation de consommateur OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Rejet de consommateur OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Mise à jour de consommateur OAuth",
+ "mwoauthconsumer-consumer-logpage": "Journal du consommateur OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Journal des approbations, rejets et désactivations de consommateurs OAuth enregistrés.",
+ "mwoauth-bad-request-missing-params": "Désolé, quelque chose s’est mal passé lors de la configuration de cette application connectée. Contactez le développeur de l’application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Paramètres OAuth manquants, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Désolé, quelque chose s’est mal passé, vous devez contacter l’auteur de l’application pour vous aider.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL inconnue, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Désolé, quelque chose s’est mal passé. Vous devez [$1 contacter] l’auteur de l’application pour obtenir de l’aide.\n\n<span class=\"plainlinks mw-mwoautherror-détail\">URL inconnue, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Aucun droit approuvé n’a été trouvé pour ce jeton d’autorisation.",
+ "mwoauthdatastore-request-token-not-found": "Désolé, quelque chose s’est mal passé lors de la connexion de cette application.\nRevenez en arrière et essayez de reconnecter votre compte, ou contactez l’auteur de l’application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Jeton OAuth introuvable, $1</span>",
+ "mwoauthdatastore-callback-not-found": "URL de rappel de OAuth non trouvée dans le cache. C’est probablement une erreur de l’application (mauvaise façon de faire la requête au serveur).",
+ "mwoauthdatastore-request-token-already-used": "Cette requête a déjà été effectuée et ne peut pas être soumise à nouveau.\nRetournez à l’application et essayez de connecter de nouveau votre compte, ou contactez l’auteur de l’application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Jeton OAuth déjà utilisé, $1</span>",
+ "mwoauthdatastore-bad-token": "Aucun jeton correspondant à votre demande n’a été trouvé",
+ "mwoauthdatastore-bad-source-ip": "La requête provient d’une adresse IP non valide.",
+ "mwoauthdatastore-bad-verifier": "Le code de vérification fourni n’était pas valide",
+ "mwoauthdatastore-invalid-token-type": "Le type de jeton demandé n’est pas valide",
+ "mwoauthgrants-general-error": "Il y a eu une erreur dans votre demande OAuth",
+ "mwoauthserver-bad-consumer": "« $1 » n’est pas approuvé comme App Connectée, [$2 contacter] l’auteur de l’application pour l’aide.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Application connectée OAuth non approuvée, $3 </span>",
+ "mwoauthserver-bad-consumer-key": "Désolé, quelque chose s’est mal passé lors de la connexion de cette application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Clé de OAuth inconnue, $1</span>",
+ "mwoauthserver-insufficient-rights": "Votre compte n’est pas autorisé à utiliser les Applications connectées, contactez l’administrateur de votre site pour savoir pourquoi.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Droits utilisateur OAuth insuffisants, $1</span>",
+ "mwoauthserver-invalid-request-token": "Jeton non valide dans votre demande",
+ "mwoauthserver-invalid-user": "Pour utiliser les Applications connectées sur ce site, vous devez avoir un compte transverse à tous les projets. Quand vous aurez un compte sur tous les projets, vous pouvez essayer de vous reconnecter à « $1 ».\n\n<span class=\"plainlinks mw-mwoautherror-details\">Connexion unifiée nécessaire, $2 </span>",
+ "mwoauthserver-consumer-no-secret": "Désolé, quelque chose s’est mal passé lors de la connexion à cette application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Le consommateur n’a pas de clé secrète, $1</span>",
+ "mwoauthserver-consumer-owner-only": "« $1 » est une Application connectée réservée au propriétaire. Pour obtenir le jeton d’accès, voyez [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Le consommateur est limité au propriétaire, $3</span>",
+ "mwoauth-invalid-authorization-title": "Erreur d’autorisation OAuth",
+ "mwoauth-invalid-authorization": "Les entêtes d’autorisation dans votre requête ne sont pas valides : $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Les entêtes d’autorisation dans votre requête ne sont pas valides pour $1",
+ "mwoauth-invalid-authorization-invalid-user": "Les entêtes d’autorisation dans votre requête concernent un utilisateur qui n’existe pas ici",
+ "mwoauth-invalid-authorization-wrong-user": "Les entêtes d’autorisation dans votre requête concernent un autre utilisateur",
+ "mwoauth-invalid-authorization-not-approved": "L’application à laquelle vous essayez de vous connecter semble mal paramétrée, contactez l’auteur de $1 pour de l’aide.",
+ "mwoauth-invalid-authorization-blocked-user": "Les entêtes d’autorisation dans votre requête concernent un utilisateur qui est bloqué",
+ "mwoauth-form-description-allwikis": "Bonjour $1,\n\nAfin de finaliser votre demande, '''$2''' a besoin de l'autorisation pour faire les actions suivantes en votre nom sur tous les projets de ce site :\n\n\n$4",
+ "mwoauth-form-description-onewiki": "Bonjour $1,\n\nAfin de finaliser votre demande, '''$2''' a besoin de votre permission pour faire les actions suivantes en votre nom sur ''$4'':\n\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Bonjour $1,\n\nAfin de finaliser votre demande, '''$2''' a besoin de votre permission pour accéder en votre nom aux informations concernant tous les projets de ce site. Aucune modification ne sera faite avec votre compte.",
+ "mwoauth-form-description-onewiki-nogrants": "Bonjour $1,\n\nAfin de finaliser votre demande, '''$2''' a besoin de votre permission pour accéder aux informations sur ''$4''. Aucune modification ne sera faite avec votre compte.",
+ "mwoauth-form-description-allwikis-privateinfo": "Bonjour $1,\n\nAfin de finaliser votre demande, <strong>$2</strong> a besoin de droits pour accéder à des informations vous concernant, y compris votre vrai nom et votre adresse de messagerie, sur tous les projets de ce site. Aucune modification ne sera effectuée avec votre compte.",
+ "mwoauth-form-description-onewiki-privateinfo": "Bonjour $1,\n\nAfin de finaliser votre demande, <strong>$2</strong> a besoin de droits pour accéder à des informations, comme votre vrai nom et votre adresse de messagerie, sur ''$4''. Aucune modification ne sera effectuée avec votre compte.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Bonjour $1,\n\nPour terminer votre requête, '''$2''' a besoin du droit d’accéder à vos informations, y compris votre adresse de messagerie, sur tous les projets de ce site. Aucune modification ne sera apportée à votre compte.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Bonjour $1,\n\nPour terminer votre requête, '''$2''' a besoin du droit d’accéder à des informations, comme votre adresse de messagerie, sur ''$4''. Aucune modification ne sera apportée à votre compte.",
+ "mwoauth-form-button-approve": "Autoriser",
+ "mwoauth-form-button-cancel": "Annuler",
+ "mwoauth-error": "Erreur de connexion de l’application",
+ "mwoauth-grants-heading": "Droits requis :",
+ "mwoauth-grants-nogrants": "L’application n’a demandé aucun droit.",
+ "mwoauth-acceptance-cancelled": "Vous avez choisi de ne pas autoriser $1 à accéder à votre compte. $1 ne fonctionnera pas à moins que vous lui autorisiez l’accès. Vous pouvez revenir à $1 ou [[Special:OAuthManageMyGrants|gérer]] vos applications connectées.",
+ "mwoauth-granttype-normal": "Autorisation de requête pour des droits spécifiques.",
+ "grant-mwoauth-authonly": "Pour la vérification de l'identité de l'utilisateur uniquement, pas possible de lire des pages ni d'agir pour le compte de l'utilisateur.",
+ "grant-mwoauth-authonlyprivate": "Vérification de l’identité de l’utilisateur seulement avec accès à son vrai nom et à son adresse de messagerie, pas possible de lire des pages ni d’agir pour le compte de l’utilisateur.",
+ "mwoauth-listgrants-extra-summary": "== Autorisations spécifiques à OAuth ==\n\nCes droits supplémentaires sont applicables aux consommateurs OAuth.",
+ "mwoauth-oauth-exception": "Une erreur s’est produite dans le protocole OAuth : $1",
+ "mwoauth-callback-not-oob": "oauth_callback doit être défini, et doit valoir \"oob\" (en minuscules)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback doit être positionné, et doit être mis à « oob » (attention à la casse), ou le rappel configuré doit être un préfixe du rappel fourni.",
+ "right-mwoauthproposeconsumer": "Proposer des nouveaux consommateurs OAuth",
+ "right-mwoauthupdateownconsumer": "Mettre à jour les consommateurs OAuth",
+ "right-mwoauthmanageconsumer": "Gérer les consommateurs OAuth",
+ "right-mwoauthsuppress": "Supprimer les consommateurs OAuth",
+ "right-mwoauthviewsuppressed": "Afficher les consommateurs OAuth supprimés",
+ "right-mwoauthviewprivate": "Afficher les données privées OAuth",
+ "right-mwoauthmanagemygrants": "Gérer les droits OAuth",
+ "action-mwoauthmanageconsumer": "gérer les consommateurs OAuth",
+ "action-mwoauthsuppress": "supprimer les consommateurs OAuth",
+ "action-mwoauthmanagemygrants": "gérer vos droits OAuth",
+ "action-mwoauthproposeconsumer": "proposer de nouveaux consommateurs OAuth",
+ "action-mwoauthupdateownconsumer": "mettre à jour les consommateurs OAuth",
+ "action-mwoauthviewprivate": "afficher les données OAuth privées",
+ "action-mwoauthviewsuppressed": "afficher les consommateurs OAuth supprimés",
+ "mwoauth-tag-reserved": "Les balises commençant par <code>OAuth CID:</code> sont réservées à une utilisation par OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Note :</strong> <span class=\"plainlinks\">[$1 OAuth]</span> est plus sécurisé que les mots de passe de robots et devrait être privilégié si le robot l’accepte.",
+ "mwoauth-api-module-disabled": "Le module « $1 » n’est pas disponible avec OAuth.",
+ "echo-category-title-oauth-owner": "Développement de OAuth",
+ "echo-pref-tooltip-oauth-owner": "M'informer sur les événements liés aux applications OAuth que j'ai créées.",
+ "echo-category-title-oauth-admin": "Administrateur de OAuth",
+ "echo-pref-tooltip-oauth-admin": "M'informer sur les événements liés à la revue des applications OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|a proposé}} une nouvelle application OAuth : $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|a actualisé}} l'application OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|a approuvé}} {{GENDER:$3|votre}} application OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|a rejeté}} {{GENDER:$3|votre}} application OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|a invalidé}} {{GENDER:$3|votre}} application OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|a réactivé}} {{GENDER:$3|votre}} OAuth application ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|a proposé}} une nouvelle application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|a mis à jour}} une application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|a approuvé}} {{GENDER:$3|votre}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|a rejeté}} {{GENDER:$3|votre}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|a désactivé}} {{GENDER:$3|votre}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|a réactivé}} {{GENDER:$3|votre}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Révision de l'application",
+ "notification-oauth-app-update-primary-link": "Revoir l'application",
+ "notification-oauth-app-approve-primary-link": "Voir l'application",
+ "notification-oauth-app-reject-primary-link": "Voir l'application",
+ "notification-oauth-app-disable-primary-link": "Voir l'application",
+ "notification-oauth-app-reenable-primary-link": "Voir l'application",
+ "notification-oauth-app-body": "Raison : $1",
+ "mwoauth-oauth-version": "Version du protocole OAuth",
+ "mwoauth-oauth2-is-confidential": "Le client est confidentiel",
+ "mwoauth-oauth2-is-confidential-help": "Un client confidentiel est une application qui peut conserver un mot de passe client confidentiel pour le reste du monde. Les clients non confidentiels sont moins sécurisés",
+ "mwoauth-oauth2-granttypes": "Types d’autorisation OAuth2 autorisés",
+ "mwoauth-oauth2-granttype-auth-code": "Code d’autorisation",
+ "mwoauth-oauth2-granttype-refresh-token": "Rafraîchir le jeton",
+ "mwoauth-oauth2-granttype-client-credentials": "Certificats client",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Impossible de créer un jeton d’accès, l’utilisateur n’a pas approuvé l’utilisation de ce jeton d’accès",
+ "mwoauth-oauth2-error-user-approval-deny": "L’utilisateur a rejeté la requête de l’application client",
+ "mwoauth-oauth-unsupported-version": "Ce point de terminaison n’est pas autorisé pour la version OAuth $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "Le périmètre « $1 » n’est pas autorisé pour cette application",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Les clients propriétaires seuls doivent être autorisés à utiliser client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Impossible de récupérer le jeton d’accès : $1",
+ "mwoauth-oauth2-error-server-error": "Le serveur d’autorisation a rencontré une condition inattendue qui l’a empêché d’achever la requête.",
+ "mwoauth-oauth2-error-invalid-request": "Il manque un paramètre obligatoire pour la requête, ou une valeur de paramètre est non valide, ou un paramètre est répété ou mal construit.",
+ "mwoauth-oauth2-error-unauthorized-client": "Le client n’est pas autorisé à demander un code d’autorisation en utilisant cette méthode.",
+ "mwoauth-oauth2-error-access-denied": "Le propriétaire de la ressource ou le serveur d’autorisation a refusé la requête.",
+ "mwoauth-oauth2-error-unsupported-response-type": "Le serveur d’autorisation ne supporte pas l’obtention d’un code d’autorisation en utilisant cette méthode.",
+ "mwoauth-oauth2-error-invalid-scope": "Le périmètre demandé n’est pas valide, inconnu ou mal formé.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "Le serveur d’autorisation est actuellement incapable de traiter la demande, du fait d’une surcharge temporaire ou d’une maintenance du serveur.",
+ "mwoauth-oauth2-error-invalid-client": "L’authentification du client a échoué (par ex., client inconnu, pas d’authentification de client incluse, ou méthode d’authentification non supportée)",
+ "mwoauth-oauth2-error-request-not-verified": "Essai de récupérer la propriété vérifiée avant de vérifier la requête",
+ "mwoauth-oauth2-invalid-access-token": "Jeton d’accès non valide",
+ "mwoauth-consumer-access-no-user": "L’approbation du serveur doit être liée à un utilisateur valide, utilisateur avec ID 0 fourni",
+ "mwoauth-login-required-reason": "Vous devez vous connecter à votre compte {{SITENAME}} pour autoriser les applications à y accéder.",
+ "mwoauthconsumer-consumer-view": "Voir ce client",
+ "mwoauthconsumer-application-view": "Voir cette application"
+}
diff --git a/OAuth/i18n/frr.json b/OAuth/i18n/frr.json
new file mode 100644
index 00000000..0b3b6eda
--- /dev/null
+++ b/OAuth/i18n/frr.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Murma174"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Ferbünjen werktjüügen:",
+ "mwoauth-prefs-managegrantslink": "Ferbünjen {{PLURAL:$1|werktjüch|werktjüügen}} ferwalte ($1)",
+ "oauthlistconsumers": "List faan OAuth-werktjüügen",
+ "oauthmanagemygrants": "Ferbünjen werktjüügen ferwalte",
+ "echo-category-title-oauth-owner": "OAuth-Ütjwerkin",
+ "echo-pref-tooltip-oauth-owner": "Du mi bööd, wan diar OAuth werktjüügen preewet wurd, diar ik skrewen haa.",
+ "echo-pref-tooltip-oauth-admin": "Du mi bööd, wan diar OAuth werktjüügen preewet wurd."
+}
diff --git a/OAuth/i18n/fy.json b/OAuth/i18n/fy.json
new file mode 100644
index 00000000..3306cc75
--- /dev/null
+++ b/OAuth/i18n/fy.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kening Aldgilles",
+ "PiefPafPier",
+ "Robin van der Vliet",
+ "Robin0van0der0vliet"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Oansletten apps:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|$1 oansletten applikaasje|$1 oansletten applikaasjes|0=Oansletten applikaasjes}} beheare",
+ "mwoauth-consumer-reason": "Reden:",
+ "mwoauthconsumerregistration-navigation": "Navigaasje:",
+ "mwoauthmanageconsumers-reason": "Reden:",
+ "mwoauthmanagemygrants-navigation": "Navigaasje:",
+ "mwoauth-form-button-cancel": "Annulearje",
+ "echo-category-title-oauth-owner": "OAuth-ûntwikkeling",
+ "echo-pref-tooltip-oauth-owner": "Meld my barrens oangeande OAuth-applikaasjes dy't ik makke haw.",
+ "echo-category-title-oauth-admin": "OAuth-behear",
+ "echo-pref-tooltip-oauth-admin": "Meld my barrens oangeande it beoardieljen fan OAuth-applikaasjes."
+}
diff --git a/OAuth/i18n/gl.json b/OAuth/i18n/gl.json
new file mode 100644
index 00000000..f76967c1
--- /dev/null
+++ b/OAuth/i18n/gl.json
@@ -0,0 +1,283 @@
+{
+ "@metadata": {
+ "authors": [
+ "Banjo",
+ "Elisardojm",
+ "Fisterraeomar",
+ "Iváns",
+ "Macofe",
+ "Toliño"
+ ]
+ },
+ "mwoauth-desc": "Permite o uso do OAuth 1.0a para autorización de API",
+ "mwoauth-verified": "Agora, esta aplicación ten permitido acceder a MediaWiki no seu nome.\n\nPara completar o proceso, achegue este valor de verificación á aplicación: '''$1'''",
+ "mwoauth-db-readonly": "A base de datos OAuth está pechada temporalmente. Por favor, probe de novo nuns minutos.",
+ "mwoauth-missing-field": "Falta o valor para o campo \"$1\"",
+ "mwoauth-invalid-field": "Achegouse un valor non válido para o campo \"$1\"",
+ "mwoauth-invalid-field-generic": "O valor proporcionado non é válido",
+ "mwoauth-field-hidden": "(esta información está agochada)",
+ "mwoauth-field-private": "(esta información é privada)",
+ "mwoauth-prefs-managegrants": "Aplicacións conectadas:",
+ "mwoauth-prefs-managegrantslink": "Administrar $1 {{PLURAL:$1|aplicación conectada|aplicacións conectadas}}",
+ "mwoauth-consumer-allwikis": "Todos os proxectos deste sitio",
+ "mwoauth-consumer-key": "Clave do consumidor:",
+ "mwoauth-consumer-name": "Nome da aplicación:",
+ "mwoauth-consumer-version": "Versión do consumidor:",
+ "mwoauth-consumer-user": "Editor:",
+ "mwoauth-consumer-stage": "Estado actual:",
+ "mwoauth-consumer-email": "Enderezo de correo electrónico de contacto:",
+ "mwoauth-consumer-email-help": "Só visible para os que están aprobando os novos consumidores",
+ "mwoauth-consumer-owner-only-label": "Reservado ao propietario:",
+ "mwoauth-consumer-owner-only": "Este consumidor só é para uso de $1.",
+ "mwoauth-consumer-owner-only-help": "Seleccionar esta opción provocará que o consumidor se aprobe e acepte para o seu uso por parte de $1 automaticamente. Ningún outro usuario poderá usalo, e o fluxo de autorización habitual non funcionará. As accións feitas empregando este consumidor non se etiquetarán.",
+ "mwoauth-consumer-description": "Descrición da aplicación:",
+ "mwoauth-consumer-callbackurl": "URL de \"chamada de retorno\" do OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Permite ó consumidor especificar un retorno de chamada nas peticións e usar a URL \"retorno de chamada\" de enriba como prefixo requirido.",
+ "mwoauth-consumer-granttypes": "Tipos de dereitos que están a solicitarse:",
+ "mwoauth-consumer-grantsneeded": "Concesións aplicables:",
+ "mwoauth-consumer-required-grant": "Aplicable ao consumidor",
+ "mwoauth-consumer-wiki": "Proxecto aplicable:",
+ "mwoauth-consumer-wiki-thiswiki": "Proxecto actual ($1)",
+ "mwoauth-consumer-restrictions": "Restricións de uso:",
+ "mwoauth-consumer-restrictions-json": "Restricións de uso (JSON):",
+ "mwoauth-consumer-rsakey": "Clave pública RSA (opcional):",
+ "mwoauth-consumer-rsakey-help": "Introducir unha chave pública para usar o método de sinatura RSA-SHA1. Deixar baleiro para usar HMAC-SHA1 cun segredo aleatorio. En caso de non estar seguro, deixalo baleiro.",
+ "mwoauth-consumer-secretkey": "Pase secreto do consumidor:",
+ "mwoauth-consumer-accesstoken": "Pase de acceso:",
+ "mwoauth-consumer-reason": "Motivo:",
+ "mwoauth-consumer-developer-agreement": "Ao enviar esta aplicación, vostede acepta que reservamos o dereito a desactivar a súa aplicación, a retirar ou restrinxir o acceso ao sitio a vostede ou á súa aplicación, e a realizar calquera outro tipo de acción que vexamos apropiada se cremos, ao noso xuízo, que vostede ou a súa aplicación están a violar algunha política, norma ou principio deste sitio. Podemos cambiar esta política de aplicacións en calquera momento sen aviso previo, segundo o noso criterio e cando o estimemos necesario. Se vostede continúa usando OAuth significa que acepta eses cambios.",
+ "mwoauth-consumer-email-unconfirmed": "Aínda non se confirmou o enderezo de correo electrónico da súa conta.",
+ "mwoauth-consumer-email-mismatched": "O enderezo de correo electrónico achegado debe coincidir co da súa conta.",
+ "mwoauth-consumer-alreadyexists": "Xa existe un consumidor con esa combinación de nome/versión/editor",
+ "mwoauth-consumer-alreadyexistsversion": "Xa existe un consumidor con esa combinación de nome/editor cunha versión igual ou maior (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Non se pode actualizar a información dunha solicitude de consumidor pendente",
+ "mwoauth-consumer-not-proposed": "O consumidor non está proposto actualmente",
+ "mwoauth-consumer-not-disabled": "O consumidor non está desactivado actualmente",
+ "mwoauth-consumer-not-approved": "O consumidor non está aprobado (se cadra, foi desactivado)",
+ "mwoauth-missing-consumer-key": "Non se indicou a clave de consumidor.",
+ "mwoauth-invalid-consumer-key": "Non existe consumidor ningún coa clave achegada.",
+ "mwoauth-invalid-access-token": "Non existe pase de acceso ningún coa clave achegada.",
+ "mwoauth-invalid-access-wrongwiki": "O consumidor só pode usarse no proxecto \"$1\".",
+ "mwoauth-consumer-conflict": "Alguén cambiou os atributos deste consumidor mentres o vía. Inténteo de novo. Se cadra, quere comprobar o rexistro de modificacións.",
+ "mwoauth-consumer-grantshelp": "Cada permiso dá acceso aos permisos de usuario listados que xa teña a conta de usuario. Consulte a [[Special:ListGrants|táboa de permisos]] para obter máis información.",
+ "mwoauth-consumer-stage-proposed": "proposto",
+ "mwoauth-consumer-stage-rejected": "rexeitado",
+ "mwoauth-consumer-stage-expired": "caducado",
+ "mwoauth-consumer-stage-approved": "aprobado",
+ "mwoauth-consumer-stage-disabled": "desactivado",
+ "mwoauth-consumer-stage-suppressed": "suprimido",
+ "oauthconsumerregistration": "Rexistro de consumidor OAuth",
+ "mwoauthconsumerregistration-navigation": "Navegación:",
+ "mwoauthconsumerregistration-propose": "Propoñer un novo consumidor",
+ "mwoauthconsumerregistration-list": "A miña lista de consumidores",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-text": "Os desenvolvedores deben utilizar o formulario inferior para propoñer un novo consumidor OAuth (véxase a [//www.mediawiki.org/wiki/Extension:OAuth documentación da extensión] para atopar máis detalles). Despois de enviar este formulario, recibirá un pase que a súa aplicación usará para identificarse en MediaWiki. Un administrador de OAuth terá que aprobar a súa aplicación antes de poder ser autorizada por outros usuarios.\n\nAlgunhas recomendacións e observacións:\n* Intente utilizar as menos concesións posibles. Evite as concesións que non sexan realmente necesarias agora.\n* As versións son da forma \"maior.menor.lanzamento\" (os dous últimos son opcionais) e aumentan cando son necesarios cambios nas concesións.\n* Achegue unha clave RSA pública (en formato PEM) se fose posible; en caso contrario, haberá que utilizar un pase secreto (menos seguro).\n* Pode empregar o ID dun proxecto para restrinxir o consumidor a un único proxecto neste sitio (utilice \"*\" para todos os proxectos).",
+ "mwoauthconsumerregistration-update-text": "Utilice o formulario inferior para actualizar aspectos dun consumidor OAuth que controle.\n\nTodos os valores que haxa aquí sobrescribirán os anteriores. Non deixe campos en branco a menos que queira limpar eses valores.",
+ "mwoauthconsumerregistration-maintext": "Esta páxina está destinada a que os desenvolvedores propoñan e actualicen aplicacións de consumidores OAuth no rexistro do sitio.\n\nDesde aquí, pode:\n* [[Special:OAuthConsumerRegistration/propose|Solicitar un pase para un novo consumidor]].\n* [[Special:OAuthConsumerRegistration/list|Administrar os consumidores existentes]].\n\nPara obter máis información sobre OAuth, consulte a [//www.mediawiki.org/wiki/Extension:OAuth documentación da extensión].",
+ "mwoauthconsumerregistration-propose-legend": "Nova aplicación de consumidores OAuth",
+ "mwoauthconsumerregistration-update-legend": "Actualizar a aplicación de consumidores OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Propoñer o consumidor",
+ "mwoauthconsumerregistration-update-submit": "Actualizar o consumidor",
+ "mwoauthconsumerregistration-none": "Non controla ninún consumidor OAuth.",
+ "mwoauthconsumerregistration-name": "Consumidor",
+ "mwoauthconsumerregistration-user": "Editor",
+ "mwoauthconsumerregistration-description": "Descrición",
+ "mwoauthconsumerregistration-email": "Correo electrónico de contacto",
+ "mwoauthconsumerregistration-consumerkey": "Clave do consumidor",
+ "mwoauthconsumerregistration-stage": "Estado",
+ "mwoauthconsumerregistration-lastchange": "Última modificación",
+ "mwoauthconsumerregistration-manage": "administrar",
+ "mwoauthconsumerregistration-resetsecretkey": "Restablecer a clave secreta cun novo valor",
+ "mwoauthconsumerregistration-proposed": "Recibiuse a súa solicitude de consumidor OAuth.\n\nAsignóuselle o pase de consumidor '''$1''' e o pase secreto '''$2'''. ''Garde estes datos para unha futura referencia.''",
+ "mwoauthconsumerregistration-created-owner-only": "O seu consumidor OAuth foi creado.\n\nOs seus identificadores son:\n; Identificador do consumidor: $1\n; Secreto do consumidor: $2\n; Identificador de acceso: $3\n; Secreto de acceso: $4\n<em>Por favor garde esta información para futuras referencias.</em>",
+ "mwoauthconsumerregistration-updated": "Actualizouse o seu rexistro de consumidor OAuth.",
+ "mwoauthconsumerregistration-secretreset": "Asignóuselle o pase de consumidor '''$1'''. ''Garde estes datos para unha futura referencia.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Os identificadores do seu consumidor OAuth foron reinicializados. Os novos identificadores son:\n\nOs seus identificadores son:\n; Identificador do consumidor: $1\n; Secreto do consumidor: $2\n; Identificador de acceso: $3\n; Secreto de acceso: $4\n<em>Por favor garde esta información para futuras referencias.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Debe confirmar o seu enderezo de correo antes de crear aplicacións OAuth.\nPor favor, envíe e valide o seu enderezo de correo a través das súas [[Special:Preferences|preferencias de usuario]].",
+ "oauthmanageconsumers": "Administrar os consumidores OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Debe acceder ao sistema para acceder a esta páxina.",
+ "mwoauthmanageconsumers-type": "Colas:",
+ "mwoauthmanageconsumers-showproposed": "Solicitudes propostas",
+ "mwoauthmanageconsumers-showrejected": "Solicitudes rexeitadas",
+ "mwoauthmanageconsumers-showexpired": "Solicitudes caducadas",
+ "mwoauthmanageconsumers-linkproposed": "peticións propostas",
+ "mwoauthmanageconsumers-linkrejected": "peticións rexeitadas",
+ "mwoauthmanageconsumers-linkexpired": "peticións caducadas",
+ "mwoauthmanageconsumers-linkapproved": "peticións aprobadas",
+ "mwoauthmanageconsumers-linkdisabled": "peticións desactivadas",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-maintext": "Esta páxina está destinada a manexar solicitudes de aplicación de consumidor OAuth (véxase http://oauth.net) e a administrar os consumidores OAuth establecidos.",
+ "mwoauthmanageconsumers-queues": "Seleccione unha cola de confirmación de consumidor a continuación:",
+ "mwoauthmanageconsumers-q-proposed": "Cola das solicitudes de consumidor propostas",
+ "mwoauthmanageconsumers-q-rejected": "Cola das solicitudes de consumidor rexeitadas",
+ "mwoauthmanageconsumers-q-expired": "Cola das solicitudes de consumidor caducadas",
+ "mwoauthmanageconsumers-lists": "Seleccione unha lista de estado de consumidor a continuación:",
+ "mwoauthmanageconsumers-l-approved": "Lista dos consumidores aprobados actualmente",
+ "mwoauthmanageconsumers-l-disabled": "Lista dos consumidores desactivados actualmente",
+ "mwoauthmanageconsumers-none-proposed": "Non hai consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-rejected": "Non hai consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-expired": "Non hai consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-approved": "Non hai ningún consumidor que coincida cos criterios.",
+ "mwoauthmanageconsumers-none-disabled": "Non hai ningún consumidor que coincida cos criterios.",
+ "mwoauthmanageconsumers-name": "Consumidor",
+ "mwoauthmanageconsumers-user": "Editor",
+ "mwoauthmanageconsumers-description": "Descrición",
+ "mwoauthmanageconsumers-email": "Correo electrónico de contacto",
+ "mwoauthmanageconsumers-consumerkey": "Clave do consumidor",
+ "mwoauthmanageconsumers-lastchange": "Última modificación",
+ "mwoauthmanageconsumers-review": "revisar/administrar",
+ "mwoauthmanageconsumers-confirm-text": "Utilice este formulario para aprobar, rexeitar, desactivar ou reactivar este consumidor.",
+ "mwoauthmanageconsumers-confirm-legend": "Administrar o consumidor OAuth",
+ "mwoauthmanageconsumers-action": "Cambiar o estado:",
+ "mwoauthmanageconsumers-approve": "Aprobado",
+ "mwoauthmanageconsumers-reject": "Rexeitado",
+ "mwoauthmanageconsumers-rsuppress": "Rexeitado e suprimido",
+ "mwoauthmanageconsumers-disable": "Desactivado",
+ "mwoauthmanageconsumers-dsuppress": "Desactivado e suprimido",
+ "mwoauthmanageconsumers-reenable": "Aprobado",
+ "mwoauthmanageconsumers-reason": "Motivo:",
+ "mwoauthmanageconsumers-confirm-submit": "Actualizar o estado do consumidor",
+ "mwoauthmanageconsumers-success-approved": "Aprobouse a solicitude.",
+ "mwoauthmanageconsumers-success-rejected": "Rexeitouse a solicitude.",
+ "mwoauthmanageconsumers-success-disabled": "Desactivouse o consumidor.",
+ "mwoauthmanageconsumers-success-reanable": "Reactivouse o consumidor.",
+ "mwoauthmanageconsumers-search-name": "consumidores con este nome",
+ "mwoauthmanageconsumers-search-publisher": "consumidores para este usuario",
+ "oauthlistconsumers": "Listar aplicacións OAuth",
+ "mwoauthlistconsumers-legend": "Explorar aplicacións OAuth",
+ "mwoauthlistconsumers-view": "detalles",
+ "mwoauthlistconsumers-none": "Non se atoparon aplicacións en base a estes criterios.",
+ "mwoauthlistconsumers-name": "Nome da aplicación",
+ "mwoauthlistconsumers-version": "Versión do consumidor",
+ "mwoauthlistconsumers-user": "Editor",
+ "mwoauthlistconsumers-description": "Descrición",
+ "mwoauthlistconsumers-wiki": "Proxecto aplicable",
+ "mwoauthlistconsumers-callbackurl": "\"URL de chamada de retorno\" do OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Permite ó consumidor especificar un retorno de chamada nas peticións e usar a URL \"retorno de chamada\" de enriba como prefixo requirido.",
+ "mwoauthlistconsumers-grants": "Concesións aplicables",
+ "mwoauthlistconsumers-basicgrantsonly": "(acceso básico só)",
+ "mwoauthlistconsumers-status": "Estado",
+ "mwoauth-consumer-stage-any": "calquera",
+ "mwoauthlistconsumers-status-proposed": "proposto",
+ "mwoauthlistconsumers-status-approved": "aprobado",
+ "mwoauthlistconsumers-status-disabled": "desactivado",
+ "mwoauthlistconsumers-status-rejected": "rexeitado",
+ "mwoauthlistconsumers-status-expired": "caducado",
+ "oauthmanagemygrants": "Administrar as aplicacións conectadas",
+ "mwoauthmanagemygrants-text": "Esta páxina amosa as aplicacións que poden utilizar a súa conta. Para calquera aplicación, o alcance do seu acceso está limitado polos permisos que se lle outorgaron no momento de autorizala. Se autorizou unha aplicación para que acceda a varios proxectos no seu nome, verá axustes separados a continuación por cada un dos proxectos.\n\nAs aplicacións conectadas acceden á súa conta mediante o protocolo OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Máis información sobre as aplicacións conectadas])</span>",
+ "mwoauthmanagemygrants-navigation": "Navegación:",
+ "mwoauthmanagemygrants-showlist": "Lista de aplicacións conectadas",
+ "mwoauthmanagemygrants-none": "Non hai aplicacións conectadas coa súa conta.",
+ "mwoauthmanagemygrants-user": "Editor:",
+ "mwoauthmanagemygrants-description": "Descrición",
+ "mwoauthmanagemygrants-wikiallowed": "Permitido no proxecto:",
+ "mwoauthmanagemygrants-grants": "Concesións aplicables",
+ "mwoauthmanagemygrants-grantsallowed": "Concesións permitidas:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Concesións aplicables permitidas:",
+ "mwoauthmanagemygrants-review": "administrar o acceso",
+ "mwoauthmanagemygrants-revoke": "revogar o acceso",
+ "mwoauthmanagemygrants-grantaccept": "Concedido",
+ "mwoauthmanagemygrants-update-text": "Utilice o formulario seguinte para modificar os permisos concedidos a unha aplicación para que actúe no seu nome.",
+ "mwoauthmanagemygrants-revoke-text": "Utilice o formulario seguinte para quitar o aceso a unha aplicación para que actúe no seu nome.",
+ "mwoauthmanagemygrants-confirm-legend": "Administrar as aplicacións conectadas",
+ "mwoauthmanagemygrants-update": "Actualizar as concesións",
+ "mwoauthmanagemygrants-renounce": "Desautorizar",
+ "mwoauthmanagemygrants-action": "Cambiar o estado:",
+ "mwoauthmanagemygrants-confirm-submit": "Actualizar o estado do pase de acceso",
+ "mwoauthmanagemygrants-success-update": "As súas preferencias para esta aplicación foron actualizadas.",
+ "mwoauthmanagemygrants-success-renounce": "O acceso da aplicación á súa conta foi revogado.",
+ "mwoauthmanagemygrants-basic-tooltip": "Por que non podo actualizar este permiso? Este permiso dalle permisos básicos á aplicación conectada que os require para funcionar correctamente. Se non quere que esa aplicación conectada teña eses permisos, debe anular o acceso da aplicación.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Por qué non podo actualizar este permiso? Se vostede non desexa que esta aplicación conectada teña este permiso, debe anular o acceso da aplicación.",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|propuxo}} un consumidor OAuth (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|actualizou}} un consumidor OAuth (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|aprobou}} un consumidor OAuth de $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|rexeitou}} un consumidor OAuth de $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|desactivou}} un consumidor OAuth de $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|reactivou}} un consumidor OAuth de $3 (clave de consumidor $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|creou}} un consumidor OAuth reservado ó propietario (clave de consumidor $4)",
+ "mwoauthconsumer-consumer-logpage": "Rexistro de consumidores OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Rexistro de aprobacións, rexeitamentos e desactivacións dos consumidores OAuth rexistrados.",
+ "mwoauth-bad-request-missing-params": "Houbo un erro ó configurar esta aplicación conectada. Contacte co desenvolvedor da aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Faltan parámetros de OAuth, $1 </span>",
+ "mwoauth-bad-request-invalid-action": "Sentímolo, houbo algún erro, ten que poñerse en contacto co autor da aplicación para conseguir axuda con isto.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL descoñecida, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Sentímolo, houbo algún erro, ten que poñerse en [$1 contacto] co autor da aplicación para conseguir axuda con isto.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL descoñecida, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Non se atopou ningunha concesión aprobada para ese pase de autorización",
+ "mwoauthdatastore-request-token-not-found": "Sentímolo, algo funcionou mal ó conectar con esta aplicación.\nVolva atrás e tente acceder á súa conta de novo, ou contacte co autor da aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Identificador OAuth non atopado, $1</span>",
+ "mwoauthdatastore-callback-not-found": "A URL de retrochamada de OAuth non se atopou na caché. Isto é probablemente un erro na forma na que a aplicación fai peticións ó servidor.",
+ "mwoauthdatastore-request-token-already-used": "Esta solicitude xa foi completada e non pode volver a presentarse.\nVolva á aplicación e probe a conectarse á súa conta de novo, ou póñase en contacto co autor da aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Identificador OAuth xa usado, $1</span>",
+ "mwoauthdatastore-bad-token": "Non se atopou ningún pase que coincidise coa solicitude",
+ "mwoauthdatastore-bad-source-ip": "A solicitude chegou dunha dirección IP non válida.",
+ "mwoauthdatastore-bad-verifier": "O código de verificación achegado non é válido",
+ "mwoauthdatastore-invalid-token-type": "O tipo de pase solicitado non é válido",
+ "mwoauthgrants-general-error": "Houbo un erro na súa solicitude OAuth",
+ "mwoauthserver-bad-consumer": "\"$1\" non está aprobada como App Conectada. [$2 Contacte] ó autor da aplicación para solicitar axuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Conexión app OAuth sen aprobar, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Sentímolo pero algo funcionou mal ó conectar con esta aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Clave de OAuth descoñecida, $1</span>",
+ "mwoauthserver-insufficient-rights": "A súa conta non ten permiso para usar Apps Conectadas, contacte co administrador do seu sitio.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Insuficientes dereitos de usuario para OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Pase non válido na súa solicitude",
+ "mwoauthserver-invalid-user": "Para usar Aplicacións conectadas neste sitio, vostede debe ter unha conta en todos os proxectos. Cando teña unha conta en todos os proxectos, vostede pode intentar conectar de novo con \"$1\".\n\n<span class=\"plainlinks mw-mwoautherror-details\">Necesaria conexión unificada, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Algo foi mal ó conectar esta aplicación.\n\n<span class=\"plainlinks mw-mwoautherror-details\">O consumidor non ten unha clave secreta, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" é unha Aplicación Conectada reservada ó propietario. Para procurar o identificador de acceso, vexa [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">O consumidor está reservado ó propietario, $3</span>",
+ "mwoauth-invalid-authorization-title": "Erro de autorización OAuth",
+ "mwoauth-invalid-authorization": "As cabeceiras de autorización da súa solicitude non son válidas: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "As cabeceiras de autorización da súa solicitude non son válidas para $1",
+ "mwoauth-invalid-authorization-invalid-user": "As cabeceiras de autorización da súa solicitude son para un usuario que non existe aquí",
+ "mwoauth-invalid-authorization-wrong-user": "As cabeceiras de autorización da súa solicitude son para un usuario diferente",
+ "mwoauth-invalid-authorization-not-approved": "Parece que a aplicación que está tentando conectar non está correctamente configurada. Contacte co autor de \"$1\" para solicitar axuda.",
+ "mwoauth-invalid-authorization-blocked-user": "As cabeceiras de autorización da súa solicitude son para un usuario que está bloqueado",
+ "mwoauth-form-description-allwikis": "Boas $1:\n\nPara completar a súa solicitude, '''$2''' precisa permiso para realizar as seguintes accións no seu nome en todos os proxectos deste sitio:\n\n\n$4",
+ "mwoauth-form-description-onewiki": "Boas $1:\n\nPara completar a súa solicitude, '''$2''' precisa permiso para realizar as seguintes accións no seu nome en ''$4'':\n\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Boas $1:\n\nPara completar a súa solicitude, '''$2''' precisa permiso para acceder a información no seu nome en todos os proxectos deste sitio. Non se fará ningún cambio coa súa conta.",
+ "mwoauth-form-description-onewiki-nogrants": "Boas $1:\n\nPara completar a súa solicitude, '''$2''' precisa permiso para acceder a información no seu nome en ''$4''. Non se fará ningún cambio coa súa conta.",
+ "mwoauth-form-description-allwikis-privateinfo": "Ola, $1:\n\nPara completar a solicitude, '''$2''' precisa permiso para acceder a datos sobre vostede, como o seu nome real e o seu enderezo de correo electrónico, en tódolos proxectos deste sitio. Non se realizará ningunha modificación a través da súa conta.",
+ "mwoauth-form-description-onewiki-privateinfo": "Ola, $1:\n\nPara completar a solicitude, '''$2''' precisa permiso para acceder a datos como o seu nome real e o seu enderezo de correo electrónico, en ''$4''. Non se realizará ningunha modificación a través da súa conta.",
+ "mwoauth-form-button-approve": "Permitir",
+ "mwoauth-form-button-cancel": "Cancelar",
+ "mwoauth-error": "Erro de conexión da aplicación",
+ "mwoauth-grants-heading": "Permisos solicitados:",
+ "mwoauth-grants-nogrants": "A aplicación non solicitou ningún permiso.",
+ "mwoauth-acceptance-cancelled": "Escolleu que a aplicación \"$1\" non pode acceder á súa conta. \"$1\" non funcionará a menos que lle permita o acceso. Pode regresar a \"$1\" ou [[Special:OAuthManageMyGrants|xestionar]] as súas aplicacións conectadas.",
+ "mwoauth-granttype-normal": "Solicite autorización para permisos específicos.",
+ "grant-mwoauth-authonly": "Só para a verificación da identidade do usuario, sen capacidade de ler páxinas nin de actuar en nome do usuario.",
+ "grant-mwoauth-authonlyprivate": "Só para a verificación da identidade do usuario con acceso ó nome real e enderezo de correo electrónico, sen capacidade de ler páxinas nin de actuar en nome do usuario.",
+ "mwoauth-listgrants-extra-summary": "== OAuth-autorizacións específicas ==\n\nEstas autorizacións adicionais son aplicábeis ós consumidores de OAuth.",
+ "mwoauth-oauth-exception": "Ocorreu un erro no protocolo OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback debe estar definido e ter o valor \"oob\" (distingue entre maiúsculas e minúsculas)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback debe estar definido e ter o valor \"oob\" (distingue entre maiúsculas e minúsculas) ou a chamada de retorno configurada debe ser un prefixo da chamada de retorno indicada.",
+ "right-mwoauthproposeconsumer": "Propoñer novos consumidores OAuth",
+ "right-mwoauthupdateownconsumer": "Actualizar os consumidores OAuth que vostede controle",
+ "right-mwoauthmanageconsumer": "Administrar os consumidores OAuth",
+ "right-mwoauthsuppress": "Eliminar consumidores OAuth",
+ "right-mwoauthviewsuppressed": "Ver os consumidores OAuth eliminados",
+ "right-mwoauthviewprivate": "Ver os datos OAuth privados",
+ "right-mwoauthmanagemygrants": "Administrar as concesións OAuth",
+ "action-mwoauthmanageconsumer": "administrar os consumidores OAuth",
+ "action-mwoauthmanagemygrants": "administrar as súas concesións OAuth",
+ "action-mwoauthproposeconsumer": "propoñer novos consumidores OAuth",
+ "action-mwoauthupdateownconsumer": "actualizar os consumidores OAuth que vostede controle",
+ "action-mwoauthviewsuppressed": "ver os consumidores OAuth eliminados",
+ "mwoauth-tag-reserved": "As etiquetas que comezan con <code>OAuth CID:</code> están reservadas para OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Nota:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> é máis seguro que os contrasinais de bot e debería preferirse se o bot o permite.",
+ "mwoauth-api-module-disabled": "O módulo «$1» non está dispoñible con OAuth.",
+ "echo-category-title-oauth-owner": "Desenvolvemento de OAuth",
+ "echo-pref-tooltip-oauth-owner": "Notificarme sobre eventos relacionados con aplicacións OAuth creadas por min.",
+ "echo-category-title-oauth-admin": "Administrador de OAuth",
+ "echo-pref-tooltip-oauth-admin": "Avisarme sobre eventos relacionados coa revisión de aplicacións OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|propuso}} unha aplicación OAuth nova: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|actualizou}} a aplicación OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|aprobou}} {{GENDER:$3|a súa}} aplicación OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|rechazou}} {{GENDER:$3|a súa}} aplicación OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|desactivou}} {{GENDER:$3|a súa}} aplicación OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reactivou}} {{GENDER:$3|a súa}} aplicación OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|propuso}} unha aplicación OAuth nova en {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|actualizou}} unha aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|aprobou}} {{GENDER:$3|a súa}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|rechazou}} {{GENDER:$3|a súa}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|desactivou}} {{GENDER:$3|a súa}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reactivou}} {{GENDER:$3|a súa}} aplicación OAuth en {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Revisión da aplicación",
+ "notification-oauth-app-update-primary-link": "Revisión da aplicación",
+ "notification-oauth-app-approve-primary-link": "Ver aplicación",
+ "notification-oauth-app-reject-primary-link": "Ver aplicación",
+ "notification-oauth-app-disable-primary-link": "Ver aplicación",
+ "notification-oauth-app-reenable-primary-link": "Ver aplicación",
+ "notification-oauth-app-body": "Motivo: $1"
+}
diff --git a/OAuth/i18n/gsw.json b/OAuth/i18n/gsw.json
new file mode 100644
index 00000000..78c1702e
--- /dev/null
+++ b/OAuth/i18n/gsw.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "J. 'mach' wust"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Verbundeni Awändige:",
+ "mwoauth-prefs-managegrantslink": "Verbundeni {{PLURAL:$1|Awändig|Awändige}} verwalte ($1)",
+ "mwoauth-consumer-allwikis": "Alli Projekt of däm Website",
+ "oauthmanagemygrants": "Verbundeni Awändige verwalte",
+ "mwoauthmanagemygrants-text": "Hie isch e Liste vo allnen Awändige, wo dys Benutzerkonto chöü bruuche. We du eren Awändig erloubsch, das si darf verträtte, de fragt si di, uf was das si darf zuegryffe. Der Zuegriff blybt uf das beschränkt. We du eren Awändig us emne verwandte Projekt erloubsch, das si di darf verträtte, de gsehsch du unde d Ystellige für jedes jewylige verwandte Projekt.\n\nVerbundeni Awändige gryffen über ds OAuth-Protokoll uf dys Benutzerkonto zue. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Meh über verbundeni Awändige lehre])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigation:",
+ "mwoauthmanagemygrants-showlist": "Liste vo verbundnigen Awändige",
+ "mwoauthmanagemygrants-user": "Use’gä vo:",
+ "mwoauthmanagemygrants-wikiallowed": "Erloubt uf Projekt:",
+ "mwoauthmanagemygrants-revoke": "Erloubnis zrüggnäh"
+}
diff --git a/OAuth/i18n/gu.json b/OAuth/i18n/gu.json
new file mode 100644
index 00000000..42d71419
--- /dev/null
+++ b/OAuth/i18n/gu.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Drashti4"
+ ]
+ },
+ "mwoauth-desc": "API અધિકૃતિ માટે OAuth 1.0a નો ઉપયોગ કરવાની મંજૂરી આપે છે",
+ "mwoauth-verified": "એપ્લિકેશનને હવે તમારા વતી મીડિયાવિકિને એક્સેસ કરવાની મંજૂરી છે.\n\nપ્રક્રિયા પૂર્ણ કરવા માટે, એપ્લિકેશનમાં આ ચકાસણી મૂલ્ય પ્રદાન કરો: '''$1'''",
+ "mwoauth-db-readonly": "OAuth ડેટાબેસ અસ્થાયી રૂપે લૉક કરેલું છે. કૃપા કરીને થોડીવારમાં ફરી પ્રયાસ કરો.",
+ "mwoauth-missing-field": "\"$1\" ફીલ્ડ માટે ખૂટેલું મૂલ્ય",
+ "mwoauth-invalid-field": "\"$1\" ફીલ્ડ માટે અમાન્ય મૂલ્ય પ્રદાન કરેલું છે",
+ "mwoauth-invalid-field-generic": "અમાન્ય મૂલ્ય પ્રદાન કરેલ",
+ "mwoauth-field-hidden": "(આ માહિતી છુપાયેલ છે)",
+ "mwoauth-field-private": "(આ માહિતી ખાનગી છે)",
+ "mwoauth-prefs-managegrants": "જોડાણ કરેલ એપ્લિકેશનો:",
+ "mwoauth-prefs-managegrantslink": "જોડાણ $1 સંચાલન {{PLURAL:$1|એપ્લિકેશન|કાર્યક્રમો}}",
+ "mwoauth-consumer-allwikis": "આ સાઇટ પરના બધા પ્રોજેક્ટ્સ",
+ "mwoauth-consumer-key": "ઉપભોક્તા ચાવી:",
+ "mwoauth-consumer-name": "એપ્લિકેશનનું નામ"
+}
diff --git a/OAuth/i18n/he.json b/OAuth/i18n/he.json
new file mode 100644
index 00000000..9681936b
--- /dev/null
+++ b/OAuth/i18n/he.json
@@ -0,0 +1,333 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "GilCahana",
+ "Guycn2",
+ "Strayblues",
+ "YaronSh",
+ "אור שפירא",
+ "דולב"
+ ]
+ },
+ "mwoauth-desc": "מאפשרת שימוש ב־OAuth 1.0a וב־OAuth 2.0 לאישור ב־API",
+ "mwoauth-nosubpage-explanation": "OAuth הוא מנגנון שמאפשר ליישומים חיצוניים לזהות משתמש {{SITENAME}} או לפעול בשמו, אחרי קבלת אישור מאותו המשתמש.\n\nכדי שהדף הזה יעשה משהו, נחוצים עוד פרמטרים. אם נשלחת הנה מיישום חיצוני, זה כנראה קרה בשל שגיאה ביישום; כדאי לך ליצור קשר עם היוצר שלה.",
+ "mwoauth-verified": "עכשיו היישום הזה מורשה להתחבר למדיה־ויקי בשמך.\n\nכדי להשלים את התהליך, יש לתת את ערך האימות הבא ליישום: '''$1'''",
+ "mwoauth-db-readonly": "מד הנתונים של OAuth נעול זמנית. נא לנסות שוב בעוד מספק דקות.",
+ "mwoauth-missing-field": "חסר ערך עבור שדה \"$1\"",
+ "mwoauth-invalid-field": "ערך לא תקין עבור שדה \"$1\"",
+ "mwoauth-invalid-field-generic": "סופק ערך לא תקין",
+ "mwoauth-field-hidden": "(מידע זה מוסתר)",
+ "mwoauth-field-private": "(מידע זה הוא פרטי)",
+ "mwoauth-prefs-managegrants": "יישומים מחוברים:",
+ "mwoauth-prefs-managegrantslink": "ניהול {{PLURAL:$1|היישום המחובר|$1 היישומים המחוברים|0=היישומים המחוברים}}",
+ "mwoauth-consumer-allwikis": "כל המיזמים באתר הזה",
+ "mwoauth-consumer-key": "מפתח צרכן:",
+ "mwoauth-consumer-name": "שם היישום:",
+ "mwoauth-consumer-version": "גרסת צרכן:",
+ "mwoauth-consumer-user": "מפרסם:",
+ "mwoauth-consumer-stage": "מצב נוכחי:",
+ "mwoauth-consumer-email": "כתובת דוא\"ל נוכחית:",
+ "mwoauth-consumer-email-help": "גלוי רק לאלה שמאשרים צרכנים חדשים",
+ "mwoauth-consumer-owner-only-label": "לבעלים בלבד:",
+ "mwoauth-consumer-owner-only": "הצרכן הזה מיועד רק לשימוש על־ידי $1.",
+ "mwoauth-consumer-owner-only-help": "בחירת האפשרות הזאת תגרום לצרכן להיות מאושר אוטומטית וקביל לשימוש על־ידי $1. הוא לא יהיה שמיש על־ידי שום משתמש אחר, וזרימת האישור הרגילה לא תפעל. פעולות שתיעשינה באמצעות הצרכן הזה לא תתויגנה.",
+ "mwoauth-consumer-description": "תיאור היישום:",
+ "mwoauth-consumer-callbackurl": "כתובת מענה (callback) של OAuth:",
+ "mwoauth-consumer-callbackisprefix": "לאפשר לצרכן להגדיר מענה (callback) בבקשות ולהשתמש בכתובת ה־\"callback\" לעיל בתור תחילית דרושה.",
+ "mwoauth-consumer-granttypes": "סוגי הזיכיונות המבוקשים:",
+ "mwoauth-consumer-grantsneeded": "זיכיונות מתאימים:",
+ "mwoauth-consumer-required-grant": "חל על צרכן",
+ "mwoauth-consumer-wiki": "מיזם מתאים:",
+ "mwoauth-consumer-wiki-thiswiki": "מיזם נוכחי ($1):",
+ "mwoauth-consumer-restrictions": "מגבלות שימוש:",
+ "mwoauth-consumer-restrictions-json": "מגבלות השימוש (JSON):",
+ "mwoauth-consumer-rsakey": "מפתח RSA ציבורי (לא חובה):",
+ "mwoauth-consumer-rsakey-help": "נא להזין מפתח ציבורי שישתמש בשיטת החתימה RSA-SHA1. יש להשאיר את זה ריק כדי להשתמש ב־HMAC-SHA1 עם סוד אקראי. אם אינך בטוח מה לכתוב פה, לא צריך לכתוב דבר.",
+ "mwoauth-consumer-secretkey": "אסימון סודי של הצרכן:",
+ "mwoauth-consumer-accesstoken": "אסימון גישה:",
+ "mwoauth-consumer-reason": "סיבה:",
+ "mwoauth-consumer-developer-agreement": "שליחת היישום הזה מהווה את אישורך לכך שאנחנו שומרים על זכותנו לכבות את היישום שלך, להסיר או או להגביל את הגישה שלך או של היישום שלך לאתר ולנקוט בכל דרך פעולה שנראית לנו הולמת אם אנחנו מאמינים, לפי שיפוטנו הבלעדי, שאת או אתה או היישום שלך מפרים כל מדיניות, מדריך או עקרון מנחה של האתר הזה. אנחנו יכולים לשנות את מדיניות היישום הזאת בכל זמן ללא התראה מוקדמת לפי החלטנו הבלעדית וככל שנראה לנחוץ. המשך השימוש שלך ב־OAuth מהווה את הקבלה של השינויים האלה.",
+ "mwoauth-consumer-email-unconfirmed": "כתובת הדוא\"ל עדיין לא אושרה.",
+ "mwoauth-consumer-email-mismatched": "כתובת הדוא\"ל המסופקת צריכה להתאים לכתובת בחשבון.",
+ "mwoauth-consumer-alreadyexists": "צרכן עם שילוב שם/גרסה/מפרסם זה כבר קיים",
+ "mwoauth-consumer-alreadyexistsversion": "צרכן עם שילוב שם/מפרסם זה כבר קיים בגרסה שווה או גבוהה (\"$1\")",
+ "mwoauth-consumer-not-accepted": "אין אפשרות לעדכן מידע עבור בקשת צרכן ממתינה",
+ "mwoauth-consumer-not-proposed": "הצרכן אינו מוצע כעת",
+ "mwoauth-consumer-not-disabled": "הצרכן אינו כבוי כרגע",
+ "mwoauth-consumer-not-approved": "הצרכן לא מאושר (ייתכן שהוא כבוי)",
+ "mwoauth-missing-consumer-key": "לא ניתן מפתח צרכן",
+ "mwoauth-invalid-consumer-key": "אין צרכן עם מפתח זה.",
+ "mwoauth-invalid-access-token": "אין אסימון גישה עם המפתח.",
+ "mwoauth-invalid-access-wrongwiki": "הצרכן יכול לשמש רק במיזם \"$1\".",
+ "mwoauth-consumer-conflict": "מישהו שינה את המאפיינים של הצרכן הזה בזמן שצפית בו. נא לנסות שוב. אפשר לבדוק את יומן השינויים.",
+ "mwoauth-consumer-grantshelp": "כל זיכיון נותן גישה להרשאות המשתמש הרשומות שכבר ניתנו לחשבון משתמש. ר' את הדף [[Special:ListGrants|טבלת הזיכיונות]] למידע נוסף.",
+ "mwoauth-consumer-stage-proposed": "מוצע",
+ "mwoauth-consumer-stage-rejected": "דחוי",
+ "mwoauth-consumer-stage-expired": "פג תוקף",
+ "mwoauth-consumer-stage-approved": "מאושר",
+ "mwoauth-consumer-stage-disabled": "כבוי",
+ "mwoauth-consumer-stage-suppressed": "מועלם",
+ "oauthconsumerregistration": "רישום צרכן OAuth",
+ "mwoauthconsumerregistration-navigation": "ניווט:",
+ "mwoauthconsumerregistration-propose": "הצעת צרכן חדש",
+ "mwoauthconsumerregistration-list": "רשימת הצרכנים שלי",
+ "mwoauthconsumerregistration-main": "ראשי",
+ "mwoauthconsumerregistration-propose-text": "המפתחים צריכים להשתמש בטופס להלן כדי להציע צרכן OAuth חדש (ר' את [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth תיעוד ההרחבה] לפרטים נוספים). אחרי שליחת הטופס הזה יישלח אליך אסימון שהיישום שלך ישתמש בו כדי להזדהות בפני מדיה־ויקי. מנהל OAuth יצטרך לאשר את הפנייה שלך לפני שהיא תאושר על־ידי משתמשים אחרים.\n\nמספר עצות והערות:\n* מומלץ להשתמש בכמה שפחות זיכיונות. מומלץ להימנע שאינם נחוצים כעת.\n* הגרסאות נכתבות בצורה \"major.minor.release\" (לא חובה לכתוב את שני החלקים האחרונים) ועולים בכל פעם שהזיכיונות משתנים.\n* נא לספק מפתח RSA ציבורי (בתסדיר PEM) אם אפשר; אחרת ישמש אסימון סודי אחור (מאובטח פחות).\n* באפשרותך להשתמש במזהה מיזם כדי להגביל את הצרכן למיזם אחד באתר הזה (אפשר להשתמש ב־\"*\" לכל המיזמים).",
+ "mwoauthconsumerregistration-update-text": "השתמשו בטופס שלהלן כדי לעדכן את פרטי צרכן OAuth.\n\nכל הערכים כאן יחליפו את כל הקודמים. אין להשאיר שדות ריקים אלא אם כן אתם מתכוונים לנקות את הערכים האלה.",
+ "mwoauthconsumerregistration-maintext": "הדף הזה מאפשר למפתחים להציע ולעדכן יישומים שיהיו צרכני OAuth ברישום של האתר הזה.\n\nמכאן אפשר:\n* [[Special:OAuthConsumerRegistration/propose|לבקש אסימון בשביל צרכן חדש]].\n* [[Special:OAuthConsumerRegistration/list|לנהל את הצרכנים הנוכחיים שלך]].\n\nלמידע נוסף על OAuth, נא לראות את [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth התיעוד של ההרחבה].",
+ "mwoauthconsumerregistration-propose-legend": "יישום צרכן OAuth חדש",
+ "mwoauthconsumerregistration-update-legend": "עדכון יישום צרכן OAuth",
+ "mwoauthconsumerregistration-propose-submit": "הצעת צרכן",
+ "mwoauthconsumerregistration-update-submit": "עדכון צרכן",
+ "mwoauthconsumerregistration-none": "אין לך שליטה על שום צרכן OAuth.",
+ "mwoauthconsumerregistration-name": "צרכן",
+ "mwoauthconsumerregistration-user": "מפרסם",
+ "mwoauthconsumerregistration-description": "תיאור",
+ "mwoauthconsumerregistration-email": "שליחת דוא\"ל",
+ "mwoauthconsumerregistration-consumerkey": "מפתח צרכן",
+ "mwoauthconsumerregistration-stage": "מצב",
+ "mwoauthconsumerregistration-lastchange": "שינוי אחרון",
+ "mwoauthconsumerregistration-manage": "ניהול",
+ "mwoauthconsumerregistration-resetsecretkey": "אפס מפתח סודי לערך חדש",
+ "mwoauthconsumerregistration-proposed": "ההצעה שלך לצרכן OAuth חדש התקלבה.\n\nהוקצה לך אסימון הצרכן '''$1''' והאסימון הסודי '''$2'''. נא לרשום אותם במקום שזמין לך.",
+ "mwoauthconsumerregistration-created-owner-only": "צרכן ה־OAuth שלך נוצר.\n\nהאסימונים שלך הם:\n; אסימון צרכן: $1\n; סוד צרכן: $2\n; אסימון גישה: $3\n; סוד גישה: $4\n<em>נא לרשום את זה לשימוש עתידי.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "לקוח ה־OAuth 2.0 שלך נוצר.\n\nהאסימונים שלך:\n; מפתח יישום לקוח: $1\n; סוד יישום לקוח: $2\n; אסימון גישה: $3\n;<em>נא לשמור אותך לבדיקה בעתיד.</em>",
+ "mwoauthconsumerregistration-updated": "רישום צרכן OAuth שלך עודכן.",
+ "mwoauthconsumerregistration-secretreset": "ניתן לך אסימון הצרכן הסודי '''$1'''. נא לרשום אותם במקום שזמין לך.",
+ "mwoauthconsumerregistration-secretreset-owner-only": "אסימוני צרכן ה־OAuth שלך אותחלו. האסימונים החדשים הם:\n; אסימון צרכן: $1\n; סוד צרכן: $2\n; אסימון גישה: $3\n; סוד גישה: $4\n<em>נא לרשום את זה לשימוש עתידי.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "אסימון לקוח ה־OAuth 2.0 שלך אותחלו. האסימונים החדשים הם:\n; מפתח יישום לקוח: $1\n; סוד יישום לקוח: $2\n; אסימון גישה: $3\n;<em>נא לשמור אותך לבדיקה בעתיד.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "יש לאשר את כתובת הדוא\"ל לפני יצירת יישומי OAuth.\nנא להגדיר ולאשר את כתובת הדוא\"ל שלך דרך [[Special:Preferences|העדפות המשתמש]] שלך.",
+ "oauthmanageconsumers": "ניהול צרכני OAuth",
+ "mwoauthmanageconsumers-notloggedin": "עליכם להיות מחוברים כדי לגשת לדף זה.",
+ "mwoauthmanageconsumers-type": "תורים:",
+ "mwoauthmanageconsumers-showproposed": "בקשות שהוצעו",
+ "mwoauthmanageconsumers-showrejected": "בקשות דחויות",
+ "mwoauthmanageconsumers-showexpired": "בקשות שפג תוקפן",
+ "mwoauthmanageconsumers-linkproposed": "בקשות שהוצעו",
+ "mwoauthmanageconsumers-linkrejected": "בקשות דחויות",
+ "mwoauthmanageconsumers-linkexpired": "בקשות שפג תוקפן",
+ "mwoauthmanageconsumers-linkapproved": "בקשות מאושרות",
+ "mwoauthmanageconsumers-linkdisabled": "בקשות שכובו",
+ "mwoauthmanageconsumers-main": "ראשי",
+ "mwoauthmanageconsumers-maintext": "הדף הזה מיועד לטיפול בבקשות יישומים צרכנים של OAuth (ר' http://oauth.net) וניהול צרכני OAuth מוּכרים.",
+ "mwoauthmanageconsumers-queues": "בחירת תור אישור צרכן להלן:",
+ "mwoauthmanageconsumers-q-proposed": "תור בקשות צרכן",
+ "mwoauthmanageconsumers-q-rejected": "תור בקשות צרכן דחויות",
+ "mwoauthmanageconsumers-q-expired": "תור בקשות צרכן שפגו",
+ "mwoauthmanageconsumers-lists": "נא לבחור מצב צרכן מתוך רשימה להלן:",
+ "mwoauthmanageconsumers-l-approved": "רשימת הצרכנים מאושרים",
+ "mwoauthmanageconsumers-l-disabled": "רשימת הצרכנים הכבויים",
+ "mwoauthmanageconsumers-none-proposed": "אין צרכנים ברשימה זו.",
+ "mwoauthmanageconsumers-none-rejected": "אין צרכנים ברשימה זו.",
+ "mwoauthmanageconsumers-none-expired": "אין צרכנים ברשימה זו.",
+ "mwoauthmanageconsumers-none-approved": "אין צרכנים בקטגוריה זו.",
+ "mwoauthmanageconsumers-none-disabled": "אין צרכנים בקטגוריה זו.",
+ "mwoauthmanageconsumers-name": "צרכן",
+ "mwoauthmanageconsumers-user": "מפרסם",
+ "mwoauthmanageconsumers-description": "תיאור",
+ "mwoauthmanageconsumers-email": "שליחת דוא\"ל",
+ "mwoauthmanageconsumers-consumerkey": "מפתח צרכן",
+ "mwoauthmanageconsumers-lastchange": "שינוי אחרון",
+ "mwoauthmanageconsumers-review": "סקירה/ניהול",
+ "mwoauthmanageconsumers-confirm-text": "טופס זה מיועד לאשר, לדחות, לכבות, או להפעיל מחדש את הצרכן הזה.",
+ "mwoauthmanageconsumers-confirm-legend": "ניהול צרכן OAuth",
+ "mwoauthmanageconsumers-action": "שינוי מצב:",
+ "mwoauthmanageconsumers-approve": "מאושר",
+ "mwoauthmanageconsumers-reject": "דחוי",
+ "mwoauthmanageconsumers-rsuppress": "דחוי ומועלם",
+ "mwoauthmanageconsumers-disable": "כבוי",
+ "mwoauthmanageconsumers-dsuppress": "כבוי ומועלם",
+ "mwoauthmanageconsumers-reenable": "מאושר",
+ "mwoauthmanageconsumers-reason": "סיבה:",
+ "mwoauthmanageconsumers-confirm-submit": "עדכון מצב צרכן",
+ "mwoauthmanageconsumers-success-approved": "הבקשה אושרה",
+ "mwoauthmanageconsumers-success-rejected": "הבקשה נדחתה.",
+ "mwoauthmanageconsumers-success-disabled": "צרכן כובה.",
+ "mwoauthmanageconsumers-success-reanable": "צרכן אופשר מחדש",
+ "mwoauthmanageconsumers-search-name": "צרכנים עם השם הזה",
+ "mwoauthmanageconsumers-search-publisher": "צרכנים שיצר המשתמש הזה",
+ "oauthlistconsumers": "רשימת יישומי OAuth",
+ "mwoauthlistconsumers-legend": "עיון ביישומי OAuth",
+ "mwoauthlistconsumers-view": "פרטים",
+ "mwoauthlistconsumers-none": "לא נמצאו יישומים שמתאימים לתנאים האלה.",
+ "mwoauthlistconsumers-name": "שם היישום",
+ "mwoauthlistconsumers-version": "גרסת צרכן",
+ "mwoauthlistconsumers-user": "מפרסם",
+ "mwoauthlistconsumers-description": "תיאור",
+ "mwoauthlistconsumers-wiki": "מיזם מתאים",
+ "mwoauthlistconsumers-callbackurl": "כתובת מענה (callback) של OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "לאפשר לצרכן להגדיר מענה (callback) בבקשות ולהשתמש בכתובת ה־\"callback\" לעיל בתור תחילית דרושה.",
+ "mwoauthlistconsumers-grants": "זיכיונות מתאימים",
+ "mwoauthlistconsumers-basicgrantsonly": "(רק גישה בסיסית)",
+ "mwoauthlistconsumers-status": "מצב",
+ "mwoauth-consumer-stage-any": "הכול",
+ "mwoauthlistconsumers-status-proposed": "מוצע",
+ "mwoauthlistconsumers-status-approved": "מאושר",
+ "mwoauthlistconsumers-status-disabled": "כבוי",
+ "mwoauthlistconsumers-status-rejected": "דחוי",
+ "mwoauthlistconsumers-status-expired": "פג־תוקף",
+ "mwoauthlistconsumers-navigation": "ניווט:",
+ "mwoauthlistconsumers-update-link": "עדכון צרכן",
+ "mwoauthlistconsumers-manage-link": "ניהול צרכן",
+ "mwoauthlistconsumers-grants-link": "ניהול זיכיונות",
+ "mwoauthlistconsumers-rclink": "שינויים אחרונים שנעשו על־די היישום הזה",
+ "oauthmanagemygrants": "ניהול יישומים מחוברים",
+ "mwoauthmanagemygrants-text": "בדף הזה נמצאת רשימה של כל היישומים שיכולים להשתמש בחשבון שלך. לכל יישום כזה טווח הגישה מוגבל בהרשאות שנתת לו כאשר אישרת לו לפעול בשמך. אם נתת אישור נפרד ליישום לגשת בשמך למיזמים אחרים, תראה את ההגדרות הנפרדות לכל מיזם כזה להלן.\n\nהיישומים המחוברים ניגשים לחשבון שלך באמצעות פרוטוקול OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth מידע נוסף על יישומים מחוברים])</span>",
+ "mwoauthmanagemygrants-navigation": "ניווט:",
+ "mwoauthmanagemygrants-showlist": "רשימת היישומים המחוברים",
+ "mwoauthmanagemygrants-none": "לא מחובר שום יישום לחשבון שלך.",
+ "mwoauthmanagemygrants-user": "מפרסם:",
+ "mwoauthmanagemygrants-description": "תיאור",
+ "mwoauthmanagemygrants-wikiallowed": "מורשה במיזם:",
+ "mwoauthmanagemygrants-grants": "זיכיונות מתאימים",
+ "mwoauthmanagemygrants-grantsallowed": "זיכיונות מותרים",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "זיכיונות מתאימים מותרים:",
+ "mwoauthmanagemygrants-review": "ניהול גישה",
+ "mwoauthmanagemygrants-revoke": "שלילת גישה",
+ "mwoauthmanagemygrants-grantaccept": "ניתן זיכיון",
+ "mwoauthmanagemygrants-update-text": "הטופס להלן משמש לשינוי ההרשאות שניתנו ליישום לפעול בשמך.",
+ "mwoauthmanagemygrants-revoke-text": "הטופס להלן משמש לשלילת ההרשאות שניתנו ליישום לפעול בשמך.",
+ "mwoauthmanagemygrants-confirm-legend": "ניהול יישום מחובר",
+ "mwoauthmanagemygrants-update": "עדכון זיכיונות",
+ "mwoauthmanagemygrants-renounce": "שלילת אישור",
+ "mwoauthmanagemygrants-action": "שינוי מצב:",
+ "mwoauthmanagemygrants-confirm-submit": "מצב עדכון אסימון גישה",
+ "mwoauthmanagemygrants-success-update": "ההעדפות שלך עבור בקשה זו עודכנו.",
+ "mwoauthmanagemygrants-success-renounce": "הגישה של הבקשה לחשבון שלך בוטלה.",
+ "mwoauthmanagemygrants-basic-tooltip": "מדוע איני {{GENDER:|יכול|יכולה}} לעדכן את הזיכיון הזה? הזיכיון הזה נותן ליישומים מחוברים שלך הרשאות בסיסיות שדרושות לו כדי לתפקד כראוי. אם אינך רוצה שליישום המחובר יהיו הרשאות כאלה, כדאי לך לשלול מהיישום את הגישה.",
+ "mwoauthmanagemygrants-authonly-tooltip": "למה אי־אפשר לעדכן את הזיכיון הזה? אם אינך רוצה שליישום המחובר הזה יהיה זיכיון, כדאי לשלול מהיישום את הגישה.",
+ "mwoauthmanagemygrants-editslink": "העריכות {{GENDER:$1|שלך}} באמצעות היישום הזה",
+ "mwoauthmanagemygrants-actionslink": "הפעולות {{GENDER:$1|שלך}} באמצעות היישום הזה",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|הציע|הציעה}} צרכן OAuth (מפתח הצרכן $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|עדכן|עדכנה}} צרכן OAuth (מפתח הצרכן $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|אישר|אישרה}} צרכן OAuth על־ידי $3 (מפתח הצרכן $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|דחה|דחתה}} צרכן OAuth על־ידי $3 (מפתח הצרכן $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|ביטל|ביטלה}} צרכן OAuth על־ידי $3 (מפתח הצרכן $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|הפעיל|הפעילה}} מחדש צרכן OAuth על־ידי $3 (מפתח הצרכן $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|יצר|יצרה}} צרכן OAuth לבעלים בלבד (מפתח צרכן $4)",
+ "log-action-filter-mwoauthconsumer": "הסוג של פעולת צרכן OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "אישור צרכן OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "יצירת צרכן OAuth לבעלים בלבד",
+ "log-action-filter-mwoauthconsumer-disable": "כיבוי צרכן OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "הצעת צרכן OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "הפעלה מחדש של צרכן OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "דחיית צרכן OAuth",
+ "log-action-filter-mwoauthconsumer-update": "עדכון צרכן OAuth",
+ "mwoauthconsumer-consumer-logpage": "יומן צרכן OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "יומן של אישורים, שלילות וכיבויים של צרכני OAuth רשומים.",
+ "mwoauth-bad-request-missing-params": "סליחה, משהו השתבש עם הגדרות היישום המחובר. <span class=\"plainlinks\">[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth נא ליצור קשר עם התמיכה]</span> כדי לקבל עזרה בתיקון.\n\n<span class=\"plainlinks mw-mwoautherror-details\">חסרים פרמטרים ל־OAuth‏, $1</span>",
+ "mwoauth-bad-request-invalid-action": "סליחה, משהו השתבש ויש ליצור קשר עם יוצר היישום כדי לקבל עזרה עם זה.\n\n<span class=\"plainlinks mw-mwoautherror-details\">כתובת בלתי ידועה, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "סליחה, משהו השתבש. יש [$1 ליצור קשר] עם יוצר היישום כדי לקבל עזרה עם זה.\n\n<span class=\"plainlinks mw-mwoautherror-details\">כתובת לא ידועה, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "לא נמצא שום זיכיון מאושר עבור אסימון האישור הזה.",
+ "mwoauthdatastore-request-token-not-found": "סליחה, משהו השתבש עם היישום הזה.\nנא לחזור ולנסות להתחבר שוב לחשבונך או ליצור קשר עם יוצר היישום.\n\n<span class=\"plainlinks mw-mwoautherror-details\">לא נמצא אסימון OAuth‏, $1</span>",
+ "mwoauthdatastore-callback-not-found": "הכתובת של מענה (callback) של OAuth לא נמצאה במטמון. זאת כנראה שגיאה בצורה שבה היישום שולח בקשות לשרת.",
+ "mwoauthdatastore-request-token-already-used": "הבקשה כבר הושלמה ולא ניתן לשלוח אותה מחדש.\nנא לחזור ליישום ולנסות להתחבר עם החשבון שלך שוב, או ליצור קשר עם מחבר היישום.\n\n<span class=\"plainlinks mw-mwoautherror-details\">אסימון OAuth כבר היה בשימוש, $1</span>",
+ "mwoauthdatastore-bad-token": "לא נמצא אסימון מתאים לבקשתך.",
+ "mwoauthdatastore-bad-source-ip": "הבקשה הגיעה מכתובת IP בלתי־תקינה.",
+ "mwoauthdatastore-bad-verifier": "קוד האימות שסופק לא תקין.",
+ "mwoauthdatastore-invalid-token-type": "האסימון המבוקש אינו תקין.",
+ "mwoauthgrants-general-error": "אירעה שגיאה בבקשת OAuth.",
+ "mwoauthserver-bad-consumer": "\"$1\" אינו נתמך בתור יישום מחובר. נא [$2 ליצור קשר] עם מחבר היישום כדי לקבל עזרה.\n\n<span class=\"plainlinks mw-mwoautherror-details\">יישום ה־OAuth אינו מאושר, $3</span>",
+ "mwoauthserver-bad-consumer-key": "סליחה, משהו השתבש עם היישום הזה.\n\n<span class=\"plainlinks mw-mwoautherror-details\">מפתח OAuth לא ידוע, $1</span>",
+ "mwoauthserver-insufficient-rights": "לחשבון שלך אין אישור להשתמש ביישומים מחוברים. נא ליצור קשר עם מנהל האתר שלך כדי להבין למה.\n\n<span class=\"plainlinks mw-mwoautherror-details\">הרשאות משתמש OAuth לא מספיקות, $1</span>",
+ "mwoauthserver-invalid-request-token": "אסימון לא תקין בבקשתך.",
+ "mwoauthserver-invalid-user": "כדי להשתמש ביישומים מחוברים באתר הזה, צריך להיות לך חשבון בכל המיזמים. כאשר יהיה לך חשבון בכל המיזמים, אפשר לנסות לחבר את \"$1\" שוב.\n\n<span class=\"plainlinks mw-mwoautherror-details\">דרושה כניסה מאוחדת, $2</span>",
+ "mwoauthserver-consumer-no-secret": "סליחה, משהו השתבש בעת ההתחברות עם היישום הזה.\n\n<span class=\"plainlinks mw-mwoautherror-details\">לצרכן אין מפתח סודי, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" הוא יישום מחובר לבעלים בלבד. כדי לאחזר את אסימון הגישה, ר' [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">צרכן לבעלים בלבד, $3</span>",
+ "mwoauth-invalid-authorization-title": "שגיאת אימות OAuth",
+ "mwoauth-invalid-authorization": "כותרות האישור בבקשה שלך אינן תקניות: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "כותרות האישור בבקשה שלך אינן תקינות עבור $1",
+ "mwoauth-invalid-authorization-invalid-user": "כותרות האישור בבקשה שלך הן עבור משתמש שאינו קיים כאן",
+ "mwoauth-invalid-authorization-wrong-user": "כותרות האישור בבקשה שלך מיועדות למשתמש אחר",
+ "mwoauth-invalid-authorization-not-approved": "נראה שהיישום שניסית לחבר מוגדר באופן שגוי. נא ליצור קשר עם היוצר של $1 כדי לקבל עזרה.",
+ "mwoauth-invalid-authorization-blocked-user": "כותרות האישור בבקשה שלך הן עבור משתמש חסום",
+ "mwoauth-form-description-allwikis": "שלום $1,\n\nכדי לבצע את בקשתך, היישום '''$2''' זקוק להרשאה לבצע בשמך את הפעולות הבאות באתר הזה:\n\n$4",
+ "mwoauth-form-description-onewiki": "שלום $1,\n\nכדי לבצע את בקשתך, היישום '''$2''' זקוק להרשאות לעשות בשמך את הפעולות הבאות במיזם \"$4\":\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "שלום $1,\n\nכדי לבצע את בקשתך, היישום '''$2''' זקוק להרשאה לקבל גישה למידע בכל המיזמים בשמך. זה לא יעשה שום שינוי בחשבון שלך.",
+ "mwoauth-form-description-onewiki-nogrants": "שלום $1,\n\nכדי לבצע את בקשתך, היישום '''$2''' זקוק להרשאה לקבל גישה למידע באתר \"$4\" בשמך. זה לא יעשה שום שינוי בחשבון שלך.",
+ "mwoauth-form-description-allwikis-privateinfo": "שלום $1,\n\nכדי להשלים את הבקשה שלך, '''$2''' זקוק להרשאה לגשת למידע עליך, כולל שמך האמתי וכתובת הדוא\"ל שלך, בכל המיזמים באתר הזה. לא ייעשו שינויים עם החשבון שלך.",
+ "mwoauth-form-description-onewiki-privateinfo": "שלום $1,\n\nכדי להשלים את הבקשה שלך, '''$2''' זקוק להרשאה לגשת למידע, כולל שמך האמתי וכתובת הדוא\"ל שלך, במיזם ''$4''. לא ייעשו שינויים עם החשבון שלך.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "שלום $1,\n\nכדי להשלים את הבקשה שלך, היישום '''$2''' זקוק להרשאה לגשת למידע עליך, כולל כתובת הדוא\"ל שלך, בכל המיזמים באתר הזה. החשבון שלך לא ישונה בשום צורה.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "שלום $1,\n\nכדי להשלים את הבקשה שלך, היישום '''$2''' זקוק להרשאה לגשת למידע עליך, כולל כתובת הדוא\"ל שלך, באתר '''$4'''. החשבון שלך לא ישונה בשום צורה.",
+ "mwoauth-form-button-approve": "לאפשר",
+ "mwoauth-form-button-cancel": "ביטול",
+ "mwoauth-error": "שגיאה בחיבור היישום",
+ "mwoauth-grants-heading": "הרשאות מבוקשות:",
+ "mwoauth-grants-nogrants": "היישום לא ביקש שום הרשאות.",
+ "mwoauth-acceptance-cancelled": "בחרת לא לאפשר ל{{GRAMMAR:תחילית|$1}} לגשת לחשבון שלך. $1 לא יעבוד אלא אם כן תיתן לו גישה. ניתן לחזור על $1 או [[Special:OAuthManageMyGrants|לנהל]] את היישומים המחוברים שלך.",
+ "mwoauth-granttype-normal": "בקשת אישור להרשאות מסוימות.",
+ "grant-mwoauth-authonly": "רק בדיקת זהות משתמש, ללא יכולת לקרוא דפים או לפעול בשם המשתמש.",
+ "grant-mwoauth-authonlyprivate": "רק בדיקת זהות משתמש עם גישה לשם אמיתי וכתובת דוא\"ל, ללא יכולת לקרוא דפים או לפעול בשם המשתמש.",
+ "mwoauth-listgrants-extra-summary": "== זיכיונות ייחודיים ל־OAuth ==\n\nהזיכיונות הנוספים האלה חלים על צרכני OAuth.",
+ "mwoauth-oauth-exception": "אירעה שגיאה בפרוטוקול OAuth:$1",
+ "mwoauth-callback-not-oob": "הפרמטר oauth_callback צריך להיות מוגדר והערך שלו חייב להיות \"oob\" (תלוי־רישיות)",
+ "mwoauth-callback-not-oob-or-prefix": "יש להגדיר את oauth_callback לערך \"oob\" (תלוי רישיות), או שהמענה המוגדר צריך להיות תחילית של המענה שניתן.",
+ "right-mwoauthproposeconsumer": "הצעת צרכני OAuth חדשים",
+ "right-mwoauthupdateownconsumer": "עדכון צרכני OAuth שבשליטת המשתמש עצמו",
+ "right-mwoauthmanageconsumer": "ניהול צרכני OAuth",
+ "right-mwoauthsuppress": "העלמת צרכני OAuth",
+ "right-mwoauthviewsuppressed": "צפייה בצרכני OAuth שהועלמו",
+ "right-mwoauthviewprivate": "צפייה בנתוני OAuth פרטיים",
+ "right-mwoauthmanagemygrants": "ניהול זיכיונות OAuth",
+ "action-mwoauthmanageconsumer": "לנהול צרכני OAuth",
+ "action-mwoauthsuppress": "להעלים צרכני OAuth",
+ "action-mwoauthmanagemygrants": "לנהל את זיכיונות ה־OAuth שלך",
+ "action-mwoauthproposeconsumer": "להציע צרכני OAuth חדשים",
+ "action-mwoauthupdateownconsumer": "לעדכן צרכני OAuth שבשליטתך",
+ "action-mwoauthviewprivate": "לצפות בנתונים פרטיים של OAuth",
+ "action-mwoauthviewsuppressed": "לצפות בצרכני OAuth שהועלמו",
+ "mwoauth-tag-reserved": "תגים שמתחילים ב־<code dir=\"ltr\">OAuth CID:</code> שמורים לשימוש על־ידי OAuth.",
+ "mwoauth-botpasswords-note": "<strong>לתשומת לבך:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> בטוח יותר מסיסמאות בוט ויש להשתמש בו כשהבוט תומך בזה.",
+ "mwoauth-api-module-disabled": "המודול \"$1\" אינו זמין עם OAuth.",
+ "echo-category-title-oauth-owner": "פיתוח OAuth",
+ "echo-pref-tooltip-oauth-owner": "להודיע לי על אירועים הקשורים לפיתוח יישומי OAuth שיצרתי.",
+ "echo-category-title-oauth-admin": "מנהל OAuth",
+ "echo-pref-tooltip-oauth-admin": "להודיע לי על אירועים הקשורים לסקירת יישומי OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|הציע|הציעה}} יישום OAuth חדש בשם $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|עדכן|עדכנה}} את יישום ה־OAuth בשם $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|אישר|אישרה}} את יישום ה־OAuth {{GENDER:$3|שלך}} ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|דחה|דחתה}} את יישום ה־OAuth {{GENDER:$3|שלך}} ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|כיבה|כיבתה}} את יישום ה־OAuth {{GENDER:$3|שלך}} ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|הפעיל|הפעילה}} את יישום ה־OAuth {{GENDER:$3|שלך}} ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|הציע|הציעה}} יישום OAuth חדש באתר {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|עדכן|עדכנה}} יישום OAuth באתר {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|אישר|אישרה}} את יישום ה־OAuth {{GENDER:$3|שלך}} באתר {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|דחה|דחתה}} את יישום ה־OAuth {{GENDER:$3|שלך}} באתר {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|כיבה|כיבתה}} את יישום ה־OAuth {{GENDER:$3|שלך}} באתר {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|הפעיל|הפעילה}} מחדש את יישום ה־OAuth {{GENDER:$3|שלך}} באתר {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "סקירת יישום",
+ "notification-oauth-app-update-primary-link": "סקירת יישום",
+ "notification-oauth-app-approve-primary-link": "הצגת יישום",
+ "notification-oauth-app-reject-primary-link": "הצגת יישום",
+ "notification-oauth-app-disable-primary-link": "הצגת יישום",
+ "notification-oauth-app-reenable-primary-link": "הצגת יישום",
+ "notification-oauth-app-body": "סיבה: $1",
+ "mwoauth-oauth-version": "גרסת פרוטוקול OAuth",
+ "mwoauth-oauth2-is-confidential": "הלקוח סודי",
+ "mwoauth-oauth2-is-confidential-help": "לקוח סודי הוא יישום שמסוגל לשמור על סיסמת לקוח בסוד מהעולם. לקוחות לא סודיים מאובטחים פחות",
+ "mwoauth-oauth2-granttypes": "סוגי זיכיונות OAuth2 מותרים",
+ "mwoauth-oauth2-granttype-auth-code": "קוד אישור",
+ "mwoauth-oauth2-granttype-refresh-token": "אסימון רענון",
+ "mwoauth-oauth2-granttype-client-credentials": "נתוני האמנה של לקוח",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "לא ניתן ליצור אסימון גישה, המשתמש לא אישר הוצאה של אסימון הגישה הזה",
+ "mwoauth-oauth2-error-user-approval-deny": "המשתמש דחה את הבקשה מיישום הלקוח",
+ "mwoauth-oauth-unsupported-version": "נקודת הקצה אינה מותרת ל־OAuth מגרסה $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "טווח \"$1\" אינו מותר ליישום הזה",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "ללקוחות לבעלים בלבד צריך להיות מותר להשתמש ב־client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "לא היה אפשר לאחזר אסימון גישה: $1",
+ "mwoauth-oauth2-error-server-error": "שרת האישור נתקל בתנאי בלתי־צפוי שמנע ממנו למלא את הבקשה הזאת.",
+ "mwoauth-oauth2-error-invalid-request": "בבקשה הזאת חסר פרמטר נדרש, היא מכילה ערך פרמטר בלתי־תקין, היא מכילה פרמטר יותר מפעם אחת, או שהיא פגומה בצורה אחרת כלשהי.",
+ "mwoauth-oauth2-error-unauthorized-client": "הלקוח אינו מאושר לבקש קוד אישור באמצעות השיטה הזאת.",
+ "mwoauth-oauth2-error-access-denied": "בעלת המשאב או שרת האישור דחה את הבקשה הזאת.",
+ "mwoauth-oauth2-error-unsupported-response-type": "שרת האישור אינו תומך בקבלת קוד אישור באמצעות השיטה הזאת.",
+ "mwoauth-oauth2-error-invalid-scope": "הטווח המבוקש אינו תקין, אינו ידוע, או פגום.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "שרת האישור אינו יכול לטפל עכשיו בבקשה בשל עומס־יתר זמני או עבודות תחזוקה בשרת.",
+ "mwoauth-oauth2-error-invalid-client": "אימות הלקוח נכשל (למשל, לקוח לא ידוע, לא נכלל אימות לקוח, או שיטת אימות שאינה נתמכת)",
+ "mwoauth-oauth2-error-request-not-verified": "ניסיון לאחזר מאפיין מאומת לפני אימות הבקשה",
+ "mwoauth-oauth2-invalid-access-token": "אסימון גישה בלתי־תקין",
+ "mwoauth-consumer-access-no-user": "אישור לקוח צריך להיות משויך למשתמש תקין, ניתן משתמש עם מזהה 0",
+ "mwoauth-login-required-reason": "עליך להיכנס לחשבון שלך באתר {{SITENAME}} כדי לאשר ליישומים לגשת אליו.",
+ "mwoauthconsumer-consumer-view": "הצגת הצרכן הזה",
+ "mwoauthconsumer-application-view": "הצגת היישום הזה"
+}
diff --git a/OAuth/i18n/hi.json b/OAuth/i18n/hi.json
new file mode 100644
index 00000000..5fc7c81a
--- /dev/null
+++ b/OAuth/i18n/hi.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "राम प्रसाद जोशी",
+ "संजीव कुमार"
+ ]
+ },
+ "mwoauth-consumer-email-unconfirmed": "आप के खाता पर इमेल पता प्रमाणित नही है।",
+ "mwoauthdatastore-bad-source-ip": "यह अनुरोध एक अवैध आईपी पते से आया है।"
+}
diff --git a/OAuth/i18n/hr.json b/OAuth/i18n/hr.json
new file mode 100644
index 00000000..01da8959
--- /dev/null
+++ b/OAuth/i18n/hr.json
@@ -0,0 +1,202 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bugoslav",
+ "Ex13",
+ "Excaliboor",
+ "MaGa",
+ "Neptune, the Mystic",
+ "Vlad5250"
+ ]
+ },
+ "mwoauth-desc": "Omogućuje upotrebu OAuth 1.0a i OAuth 2.0 za autorizaciju API-ja",
+ "mwoauth-verified": "Aplikaciji je dopušten pristup MediaWikiju u vaše ime.\n\nDa biste dovršili postupak, navedite kontrolnu vrijednost aplikacije: '''$1'''",
+ "mwoauth-db-readonly": "Baza podataka OAuth privremeno je zaključana. Pokušajte ponovo za nekoliko minuta.",
+ "mwoauth-missing-field": "Nedostaje vrijednost u polju »$1«",
+ "mwoauth-invalid-field": "U polju »$1« dana je nevažeća vrijednost",
+ "mwoauth-invalid-field-generic": "Navedena je nevažeća vrijednost",
+ "mwoauth-field-hidden": "(ovaj podatak je skriven)",
+ "mwoauth-field-private": "(ovaj podatak je povjerljiv)",
+ "mwoauth-prefs-managegrants": "Povezane aplikacije:",
+ "mwoauth-prefs-managegrantslink": "Upravljaj s ukupno $1 {{PLURAL:$1|povezanom aplikacijom|povezane aplikacije|povezanih aplikacija}}",
+ "mwoauth-consumer-allwikis": "svim projektima",
+ "mwoauth-consumer-key": "Korisnički ključ:",
+ "mwoauth-consumer-name": "Naziv aplikacije:",
+ "mwoauth-consumer-version": "Korisnička inačica:",
+ "mwoauth-consumer-user": "Izdavač:",
+ "mwoauth-consumer-stage": "Trenutno stanje:",
+ "mwoauth-consumer-email": "E-pošta za kontakt:",
+ "mwoauth-consumer-email-help": "Vidljivo samo onima koji odobravaju nove korisnike",
+ "mwoauth-consumer-owner-only-label": "Samo vlasnik:",
+ "mwoauth-consumer-owner-only": "Ovaj je korisnik samo za upotrebu $1.",
+ "mwoauth-consumer-owner-only-help": "Označavanjem ove mogućnosti korisnik će biti automatski odobren i prihvaćen za korištenje od $1. Drugi ga suradnici neće moći koristiti, a normalni tijek autorizacije neće raditi. Radnje ovog korisnika neće biti označene.",
+ "mwoauth-consumer-description": "Opis aplikacije:",
+ "mwoauth-consumer-callbackurl": "URL poziva OAuth:",
+ "mwoauth-consumer-granttypes": "Vrste zahtijevanih dopuštenja:",
+ "mwoauth-consumer-grantsneeded": "Primjenjive dozvole:",
+ "mwoauth-consumer-required-grant": "Primjenjivi korisnik",
+ "mwoauth-consumer-wiki": "Primjenjivi projekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Trenutačni projekt ($1)",
+ "mwoauth-consumer-restrictions": "Ograničenja upotrebe:",
+ "mwoauth-consumer-restrictions-json": "Ograničenja upotrebe (JSON):",
+ "mwoauth-consumer-rsakey": "Javni RSA ključ (nije obavezno):",
+ "mwoauth-consumer-rsakey-help": "Unesite javni ključ za upotrebu metode potpisa RSA-SHA1. Ostavite ga praznim da biste koristili proizvoljnu tajnu HMAC-SHA1. Ako niste sigurni, ostavite prazno.",
+ "mwoauth-consumer-reason": "Razlog:",
+ "mwoauth-consumer-email-unconfirmed": "Vaša adresa e-pošte još nije potvrđena.",
+ "mwoauth-consumer-email-mismatched": "Navedena e-pošta mora odgovarati onoj na Vašem računu.",
+ "mwoauth-consumer-alreadyexists": "Već postoji korisnik s takvom kombinacijom imena/verzije/izdavača",
+ "mwoauth-consumer-not-accepted": "Ne mogu ažurirati informacije o zahtjevima korisnika na čekanju",
+ "mwoauth-consumer-not-proposed": "Korisnik trenutačno nije predložen",
+ "mwoauth-consumer-not-disabled": "Korisnik trenutačno nije onemogućen",
+ "mwoauth-consumer-not-approved": "Korisnik nije odobren (možda je onemogućen)",
+ "mwoauth-missing-consumer-key": "Nije naveden korisnički ključ.",
+ "mwoauth-invalid-consumer-key": "Ne postoji korisnik s takvim ključem.",
+ "mwoauth-invalid-access-token": "Nema pristupnog koda s takvim ključem.",
+ "mwoauth-invalid-access-wrongwiki": "Korisnik može biti korišten samo na projektu »$1«.",
+ "mwoauth-consumer-conflict": "Za vrijeme pregledavanja netko je promijenio atribute ovog korisnika. Pokušajte ponovno. Možete pogledati dnevnik promjena.",
+ "mwoauth-consumer-grantshelp": "Svaka dozvola daje pristup navedenim suradničkim pravima koja su već dodijeljena suradničkom računu. Za više informacija vidi [[Special:ListGrants|tablicu dozvola]].",
+ "oauthconsumerregistration": "Registracija trošača OAutha",
+ "mwoauthconsumerregistration-navigation": "Orijentacija:",
+ "mwoauthconsumerregistration-propose": "Predložite novog korisnika",
+ "mwoauthconsumerregistration-list": "Popis mojih korisnika",
+ "mwoauthconsumerregistration-main": "Glavna",
+ "mwoauthconsumerregistration-update-text": "Donji obrazac služi za promjenu aspekata OAuth trošača koga kontrolirate.\n\nSve ovdje navedene vrijednosti bit će prepisane preko postojećih. Ne ostavljajte prazna polja, osim ako ne želite izbrisati njihove vrijednosti.",
+ "mwoauthconsumerregistration-propose-legend": "Nova korisnička OAuth aplikacija",
+ "mwoauthconsumerregistration-update-legend": "Ažuriranje korisničke OAuth aplikacije",
+ "mwoauthconsumerregistration-propose-submit": "Predloži korisnika",
+ "mwoauthconsumerregistration-update-submit": "Ažuriraj korisnika",
+ "mwoauthconsumerregistration-name": "Korisnik",
+ "mwoauthconsumerregistration-user": "Izdavač",
+ "mwoauthconsumerregistration-description": "Opis",
+ "mwoauthconsumerregistration-email": "E-pošta za kontakt",
+ "mwoauthconsumerregistration-consumerkey": "Korisnički ključ",
+ "mwoauthconsumerregistration-stage": "Stanje",
+ "mwoauthconsumerregistration-lastchange": "Posljednja promjena",
+ "mwoauthconsumerregistration-manage": "upravljaj",
+ "mwoauthconsumerregistration-resetsecretkey": "Ponovno postavite novu vrijednost tajnom ključu",
+ "mwoauthconsumerregistration-updated": "Vaš korisnički OAuth registar je ažururan.",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Morate potvrditi Vašu adresu e-pošte prije stvaranja OAuth aplikacija.\nMolimo unesite i ovjerite Vašu adresu e-pošte u [[Special:Preferences|suradničkim postavkama]].",
+ "oauthmanageconsumers": "Upravljanje korisnicima OAutha",
+ "mwoauthmanageconsumers-notloggedin": "Da biste otvorili ovu stranicu, morate biti prijavljeni.",
+ "mwoauthmanageconsumers-main": "Glavna",
+ "mwoauthmanageconsumers-maintext": "Ova je stranica predviđena za rad sa zahtjevima suradničkih OAuth aplikacija (vidi http://oauth.net) i upravljanje postojećim korisnicima.",
+ "mwoauthmanageconsumers-queues": "Niže odaberite red korisnika za odobrenje:",
+ "mwoauthmanageconsumers-q-proposed": "Red zahtjeva predlaganja korisnika",
+ "mwoauthmanageconsumers-q-rejected": "Red odbijenih zahtjeva korisnika",
+ "mwoauthmanageconsumers-q-expired": "Red isteklih zahtjeva korisnika",
+ "mwoauthmanageconsumers-lists": "Ispod odaberite popis statusa korisnika:",
+ "mwoauthmanageconsumers-l-approved": "Popis trenutačno odobrenih korisnika",
+ "mwoauthmanageconsumers-l-disabled": "Popis trenutačno onemogućenih korisnika",
+ "mwoauthmanageconsumers-name": "Korisnik",
+ "mwoauthmanageconsumers-user": "Izdavač",
+ "mwoauthmanageconsumers-description": "Opis",
+ "mwoauthmanageconsumers-email": "E-pošta za kontakt",
+ "mwoauthmanageconsumers-consumerkey": "Korisnički ključ",
+ "mwoauthmanageconsumers-lastchange": "Posljednja promjena",
+ "mwoauthmanageconsumers-confirm-text": "Ovaj obrazac služi za odobravanje, odbijanje ili ponovno omogućavanje korisnika.",
+ "mwoauthmanageconsumers-action": "Status promjene:",
+ "mwoauthmanageconsumers-approve": "Odobreno",
+ "mwoauthmanageconsumers-reject": "Odbijeno",
+ "mwoauthmanageconsumers-disable": "Onemogućeno",
+ "mwoauthmanageconsumers-reenable": "Odobreno",
+ "mwoauthmanageconsumers-reason": "Razlog:",
+ "mwoauthmanageconsumers-confirm-submit": "Ažuriranje korisničkog statusa",
+ "mwoauthmanageconsumers-success-approved": "Zahtjev je odobren.",
+ "mwoauthmanageconsumers-success-rejected": "Zahtjev je odbijen.",
+ "mwoauthmanageconsumers-success-disabled": "Korisnik je onemogućen.",
+ "mwoauthmanageconsumers-success-reanable": "Korisnik je ponovno omogućen.",
+ "mwoauthmanageconsumers-search-name": "korisnici s ovim imenom",
+ "mwoauthmanageconsumers-search-publisher": "korisnici ovog suradnika",
+ "oauthlistconsumers": "Popis OAuth aplikacija",
+ "mwoauthlistconsumers-legend": "Pregledajte OAuth aplikacije",
+ "mwoauthlistconsumers-view": "detalji",
+ "mwoauthlistconsumers-none": "Nisu pronađene aplikacije prema zadnom kriteriju.",
+ "mwoauthlistconsumers-name": "Naziv aplikacije",
+ "mwoauthlistconsumers-version": "Korisnička inačica",
+ "mwoauthlistconsumers-user": "Izdavač",
+ "mwoauthlistconsumers-description": "Opis",
+ "mwoauthlistconsumers-wiki": "Primjenjivi projekt",
+ "mwoauthlistconsumers-callbackurl": "URL poziva OAuth",
+ "mwoauthlistconsumers-grants": "Primjenjive dozvole",
+ "mwoauthlistconsumers-basicgrantsonly": "(samo osnovni pristup)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "bilo koji",
+ "mwoauthlistconsumers-rclink": "Nedavne promjene ovom aplikacijom",
+ "oauthmanagemygrants": "Upravljanje povezanim aplikacijama",
+ "mwoauthmanagemygrants-text": "Ovo je popis svih aplikacija koje možete koristiti s Vašim suradničkim računom. Opseg djelovanja svake aplikacije ograničen je dopuštenjima koja odobrite prilikom autorizacije. Ukoliko posebno definirate dopuštenja za svaki sestrinski projekt, vidjet ćete posebne konfiguracije za svaki projekt.\n\nPovezane aplikacije pristupaju Vašem suradničkom računu putem protokola OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Saznajte više o povezanim aplikacijama])</span>",
+ "mwoauthmanagemygrants-navigation": "Orijentacija:",
+ "mwoauthmanagemygrants-showlist": "Popis povezanih aplikacija",
+ "mwoauthmanagemygrants-none": "Nema aplikacija povezanih s Vašim računom.",
+ "mwoauthmanagemygrants-user": "Izdavač:",
+ "mwoauthmanagemygrants-description": "Opis",
+ "mwoauthmanagemygrants-wikiallowed": "Dopušteno na:",
+ "mwoauthmanagemygrants-grants": "Primjenjive dozvole",
+ "mwoauthmanagemygrants-grantsallowed": "Dopuštene dozvole",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Moguće dopustiti sljedeća prava:",
+ "mwoauthmanagemygrants-review": "upravljaj pristupom",
+ "mwoauthmanagemygrants-revoke": "opozovi pristup",
+ "mwoauthmanagemygrants-grantaccept": "Odobreno",
+ "mwoauthmanagemygrants-update-text": "Koristite donji obrazac za promjenu dopuštenja koja su odobrena aplikaciji.",
+ "mwoauthmanagemygrants-revoke-text": "Koristite donji obrazac za opoziv pristupa aplikacije.",
+ "mwoauthmanagemygrants-confirm-legend": "Upravljaj povezanom aplikacijom",
+ "mwoauthmanagemygrants-update": "Ažuriraj odobrenja",
+ "mwoauthmanagemygrants-renounce": "Poništi autorizaciju",
+ "mwoauthmanagemygrants-action": "Promijeni status:",
+ "mwoauthmanagemygrants-success-update": "Vaše postavke ove aplikacije su ažurirane.",
+ "mwoauthmanagemygrants-success-renounce": "Pristup aplikacije Vašem računu je opozvan.",
+ "mwoauthmanagemygrants-basic-tooltip": "Zašto ne mogu promijeniti ovo odobrenje? Ono omogućava povezanoj aplikaciji osnovno dopuštenje koje je nužno za njeno djelovanje. Ako ne želite da aplikacija ima ova dopuštenja, opozovite prava pristupa aplikacije.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Vaša}} uređivanja ovom aplikacijom",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Vaše}} radnje ovom aplikacijom",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|predložio|predložila}} je trošač OAutha (trošački ključ $4)",
+ "mwoauthconsumer-consumer-logpage": "Evidencija OAuth trošača",
+ "mwoauthconsumer-consumer-logpagetext": "Evidencija odobravanja, odbacivanja, te onemogućavanja registriranih OAuth potrošača.",
+ "mwoauthserver-bad-consumer": "»$1« nije odobreno kao povezana aplikacija. Ako vam je potrebna pomoć, [$2 kontaktirajte] autora.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Neodobrena OAuth povezana aplikacija, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Nažalost, nešto nije u redu s aplikacijom.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nepoznati ključ OAuth, $1</span>",
+ "mwoauthserver-insufficient-rights": "Vašem računu nije dopuštena uporaba povezanih aplikacija. Za razlog se obratite administratoru mrežnog mjesta.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nedovoljna suradnička prava za OAuth, $1</span>",
+ "mwoauthserver-invalid-user": "Da biste koristili povezane aplikacije na ovoj stranici, morate imati račun na svim projektima. Tada pokušajte ponovo spojiti »$1«.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Potrebna središnja prijava, $2</span>",
+ "mwoauth-invalid-authorization-title": "Pogreška autorizacije OAutha",
+ "mwoauth-invalid-authorization-not-approved": "Aplikacija koju želite povezati ispravno je postavljena. Za pomoć kontaktirajte autora »$1«.",
+ "mwoauth-form-description-allwikis-nogrants": "Pozdrav $1.\n\nDa bi se ispunio Vaš zahtjev, '''$2''' traži dopuštenje pristupu informacijama na svim wikijima ovog mrežnog mjesta u Vaše ime. Nikakve promjene povezane s Vašim suradničkim računom neće se dogoditi.",
+ "mwoauth-form-description-onewiki-nogrants": "Pozdrav $1.\n\nDa bi se ispunio Vaš zahtjev, '''$2''' traži dopuštenje pristupu informacijama na '''$4''' u Vaše ime. Nikakve promjene povezane s Vašim suradničkim računom neće se dogoditi.",
+ "mwoauth-form-description-allwikis-privateinfo": "Pozdrav $1.\n\nDa bi se ispunio Vaš zahtjev, '''$2''' traži dopuštenje pristupu informacijama o Vama, uključujući Vaše stvarno ime i adresu e-pošte na svim projektima ovog mrežnog mjesta. Nikakve promjene povezane s Vašim suradničkim računom neće se dogoditi.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Pozdrav $1.\n\nDa bi se ispunio Vaš zahtjev, '''$2''' traži dopuštenje pristupu informacijama o Vama, uključujući adresu e-pošte na svim projektima ovog mrežnog mjesta. Nikakve promjene povezane s Vašim suradničkim računom neće se dogoditi.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Pozdrav $1.\n\nDa bi se ispunio Vaš zahtjev, '''$2''' traži dopuštenje pristupu informacijama, uključujući adresu e-pošte na '''$4'''. Nikakve promjene povezane s Vašim suradničkim računom neće se dogoditi.",
+ "mwoauth-form-button-approve": "Dopusti",
+ "mwoauth-form-button-cancel": "Odustani",
+ "mwoauth-error": "Pogreška povezivanja aplikacije",
+ "mwoauth-grants-heading": "Zatražene dozvole:",
+ "mwoauth-grants-nogrants": "Aplikacija nije zatražila dopuštenje.",
+ "mwoauth-granttype-normal": "Zahtijevaj autorizaciju za pojedinačne dozvole.",
+ "grant-mwoauth-authonly": "Samo za provjeru identiteta suradnika, bez mogućnosti čitanja stranica i djelovanja u ime korisnika.",
+ "grant-mwoauth-authonlyprivate": "Samo za provjeru identiteta suradnika, s pristupom pravom imenu i e-pošti, bez mogućnosti čitanja stranica i djelovanja u ime suradnika.",
+ "mwoauth-listgrants-extra-summary": "== Specifične dozvole za OAuth ==\n\nOve dodatne dozvole primjenjuju se samo na korisnike OAutha.",
+ "mwoauth-oauth-exception": "Došlo je do pogreške u protokolu OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback mora biti postavljen na \"oob\" (strogo u malim slovima)",
+ "right-mwoauthproposeconsumer": "Predlaganje novih korisnika OAutha",
+ "right-mwoauthupdateownconsumer": "Promjena korisnika OAutha",
+ "right-mwoauthmanageconsumer": "Upravljanje korisnicima OAutha",
+ "right-mwoauthsuppress": "Sakrivanje korisnika OAutha",
+ "right-mwoauthviewsuppressed": "Pogledajte skrivene korisnike OAutha",
+ "right-mwoauthviewprivate": "Pregled osobnih podataka za OAuth",
+ "right-mwoauthmanagemygrants": "Upravljanje dozvolama za OAuth",
+ "action-mwoauthmanageconsumer": "upravljanje korisnicima OAutha",
+ "action-mwoauthmanagemygrants": "upravljanje dozvolama za OAuth",
+ "action-mwoauthproposeconsumer": "predlaganje novih korisnika OAutha",
+ "action-mwoauthupdateownconsumer": "ažuriranje korisnika OAutha kojima upravljate",
+ "action-mwoauthviewsuppressed": "pregled skrivenih korisnika OAutha",
+ "mwoauth-tag-reserved": "Oznake koje počinju s <code>OAuth CID:</code> rezervirane su za upotrebu od strane OAuth.",
+ "mwoauth-api-module-disabled": "Modul »$1« nije dostupan s OAuthom.",
+ "echo-category-title-oauth-owner": "Razvoj OAutha",
+ "echo-category-title-oauth-admin": "Administrator OAutha",
+ "notification-oauth-app-propose-title": "$1 je {{GENDER:$1|predložio|predložila}} novu OAuth aplikaciju: $2",
+ "notification-oauth-app-update-title": "$1 je {{GENDER:$1|ažurirao|ažurirala}} Outh aplikaciju $2",
+ "notification-oauth-app-propose-primary-link": "Provjerite aplikaciju",
+ "notification-oauth-app-update-primary-link": "Provjerite aplikaciju",
+ "notification-oauth-app-approve-primary-link": "Vidi aplikaciju",
+ "notification-oauth-app-reject-primary-link": "Vidi aplikaciju",
+ "notification-oauth-app-disable-primary-link": "Vidi aplikaciju",
+ "notification-oauth-app-reenable-primary-link": "Vidi aplikaciju",
+ "notification-oauth-app-body": "Razlog: $1",
+ "mwoauth-oauth2-invalid-access-token": "Pristupni tajni ključ nije valjan",
+ "mwoauthconsumer-consumer-view": "Prikaži ovoga trošača"
+}
diff --git a/OAuth/i18n/hsb.json b/OAuth/i18n/hsb.json
new file mode 100644
index 00000000..e73f352d
--- /dev/null
+++ b/OAuth/i18n/hsb.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "J budissin"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Zwjazane aplikacije:"
+}
diff --git a/OAuth/i18n/ht.json b/OAuth/i18n/ht.json
new file mode 100644
index 00000000..7b50f58d
--- /dev/null
+++ b/OAuth/i18n/ht.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Tisave"
+ ]
+ },
+ "mwoauth-grant-viewdeleted": "Gade fichye ak paj ki efase yo"
+}
diff --git a/OAuth/i18n/hu.json b/OAuth/i18n/hu.json
new file mode 100644
index 00000000..65940940
--- /dev/null
+++ b/OAuth/i18n/hu.json
@@ -0,0 +1,79 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bencemac",
+ "Csega",
+ "Dorgan",
+ "Tacsipacsi"
+ ]
+ },
+ "mwoauth-desc": "Lehetővé teszi az OAuth 1.0a és OAuth 2.0 használatát API-hitelesítéshez",
+ "mwoauth-verified": "Az alkalmazás most már hozzáférhet a MediaWikihez a nevedben.\n\nA folyamat befejezéséhez add meg a következő megerősítő értéket az alkalmazásnak: '''$1'''",
+ "mwoauth-db-readonly": "Az OAuth adatbázis ideiglenesen le van zárva. Próbálkozz újra pár perc múlva.",
+ "mwoauth-missing-field": "Hiányzik a(z) „$1” mező értéke",
+ "mwoauth-invalid-field": "Érvénytelen a(z) „$1” mező értéke",
+ "mwoauth-invalid-field-generic": "Érvénytelen érték van megadva",
+ "mwoauth-field-hidden": "(ez az információ el van rejtve)",
+ "mwoauth-field-private": "(ez az információ titkos)",
+ "mwoauth-prefs-managegrants": "Összekapcsolt alkalmazások:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|0=Összekapcsolt alkalmazások|$1 összekapcsolt alkalmazás}} kezelése",
+ "mwoauth-consumer-allwikis": "Összes projekt ezen a weboldalon",
+ "mwoauth-consumer-name": "Alkalmazás neve:",
+ "mwoauth-consumer-user": "Közzétevő:",
+ "mwoauth-consumer-stage": "Jelenlegi állapot:",
+ "mwoauth-consumer-email": "Kapcsolattartási e-mail-cím:",
+ "mwoauth-consumer-description": "Alkalmazás leírása:",
+ "mwoauth-consumer-callbackurl": "OAuth „visszatérési” URL:",
+ "mwoauth-consumer-granttypes": "Kért jogosultságok típusai:",
+ "mwoauth-consumer-grantsneeded": "Alkalmazás engedélyei:",
+ "mwoauth-consumer-wiki-thiswiki": "Jelenlegi projekt ($1)",
+ "mwoauth-consumer-rsakey": "Nyilvános RSA kulcs:",
+ "mwoauth-consumer-reason": "Ok:",
+ "mwoauth-consumer-email-unconfirmed": "A fiókodhoz tartozó e-mail címet még nem erősítetted meg.",
+ "mwoauth-consumer-email-mismatched": "A megadott e-mail címnek azonosnak kell lennie a fiókodhoz tartozóval.",
+ "mwoauth-consumer-stage-proposed": "javasolt",
+ "mwoauth-consumer-stage-rejected": "visszadobott",
+ "mwoauth-consumer-stage-expired": "lejárt",
+ "mwoauth-consumer-stage-approved": "ellenőrzött",
+ "mwoauth-consumer-stage-disabled": "letiltott",
+ "mwoauth-consumer-stage-suppressed": "elrejtett",
+ "mwoauthconsumerregistration-navigation": "Navigáció:",
+ "mwoauthconsumerregistration-user": "Közzétevő",
+ "mwoauthconsumerregistration-description": "Leírás",
+ "mwoauthconsumerregistration-email": "Kapcsolattartási e-mail",
+ "mwoauthconsumerregistration-stage": "Státusz",
+ "mwoauthconsumerregistration-lastchange": "Utolsó változtatás",
+ "mwoauthconsumerregistration-manage": "kezelés",
+ "mwoauthmanageconsumers-notloggedin": "A lap megtekintéséhez be kell jelentkezned.",
+ "mwoauthmanageconsumers-user": "Közzétevő",
+ "mwoauthmanageconsumers-description": "Leírás",
+ "mwoauthmanageconsumers-email": "Kapcsolattartási e-mail",
+ "mwoauthmanageconsumers-lastchange": "Utolsó változtatás",
+ "mwoauthmanageconsumers-action": "Állapot módosítása:",
+ "mwoauthmanageconsumers-approve": "Ellenőrzött",
+ "oauthlistconsumers": "OAuth alkalmazások listája",
+ "mwoauthlistconsumers-legend": "OAuth alkalmazások böngészése",
+ "mwoauthlistconsumers-description": "Leírás",
+ "mwoauthlistconsumers-basicgrantsonly": "(csak alapvető hozzáférés)",
+ "mwoauthlistconsumers-status-approved": "ellenőrzött",
+ "mwoauthlistconsumers-status-rejected": "visszadobott",
+ "oauthmanagemygrants": "Összekapcsolt alkalmazások kezelése",
+ "mwoauthmanagemygrants-showlist": "Összekapcsolt alkalmazások listája",
+ "mwoauthmanagemygrants-none": "Nincsen a fiókodhoz kapcsolt alkalmazás.",
+ "mwoauthmanagemygrants-description": "Leírás",
+ "mwoauthmanagemygrants-wikiallowed": "Engedélyezve a következő projekte(ke)n:",
+ "mwoauthmanagemygrants-revoke": "hozzáférés megvonása",
+ "mwoauthmanagemygrants-grantaccept": "Megadva",
+ "mwoauthmanagemygrants-update-text": "Az alábbi űrlap segítségével módosíthatod egy nevedben eljáró alkalmazás engedélyeit.",
+ "mwoauthmanagemygrants-confirm-legend": "Összekapcsolt alkalmazások kezelése",
+ "mwoauthmanagemygrants-renounce": "Engedély visszavonása",
+ "mwoauth-form-description-allwikis": "Helló $1,\n\nA kérésed befejezéséhez a(z) '''$2''' alkalmazásnak szüksége van engedélyre, hogy a következő műveleteket végrehajthassa a nevedben az összes projektben:\n\n$4",
+ "mwoauth-form-description-onewiki": "Helló $1,\n\nA kérésed befejezéséhez a(z) '''$2''' alkalmazásnak szüksége van engedélyre, hogy a következő műveleteket végrehajthassa a nevedben a(z) ''$4'' wikin:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Helló $1,\n\nA kérésed befejezéséhez a(z) '''$2''' alkalmazásnak szüksége van engedélyre, hogy hozzáférhessen információkhoz a nevedben az oldal összes projektjében. Semmilyen változás nem fog történni a fiókoddal.",
+ "mwoauth-form-description-onewiki-nogrants": "Helló $1,\n\nA kérésed befejezéséhez a(z) '''$2''' alkalmazásnak szüksége van engedélyre, hogy hozzáférhessen információkhoz a nevedben a(z) '''$4''' wikin. Semmilyen változás nem fog történni a fiókoddal.",
+ "mwoauth-form-button-approve": "Engedélyezés",
+ "mwoauth-form-button-cancel": "Mégse",
+ "mwoauth-grants-heading": "Kért engedélyek:",
+ "mwoauth-grants-nogrants": "Ez az alkalmazás nem kért semmilyen engedélyt.",
+ "echo-category-title-oauth-owner": "OAuth-fejlesztés"
+}
diff --git a/OAuth/i18n/hy.json b/OAuth/i18n/hy.json
new file mode 100644
index 00000000..3e3c1165
--- /dev/null
+++ b/OAuth/i18n/hy.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kareyac"
+ ]
+ },
+ "mwoauthlistconsumers-navigation": "Նավարկություն"
+}
diff --git a/OAuth/i18n/ia.json b/OAuth/i18n/ia.json
new file mode 100644
index 00000000..dcf54bb5
--- /dev/null
+++ b/OAuth/i18n/ia.json
@@ -0,0 +1,295 @@
+{
+ "@metadata": {
+ "authors": [
+ "McDutchie"
+ ]
+ },
+ "mwoauth-desc": "Permitte usar OAuth 1.0a e OAuth 2.0 pro autorisation via API",
+ "mwoauth-verified": "Le application ha ora le permission de acceder a MediaWiki in tu nomine.\n\nPro concluder le processo, forni iste valor de verification al application: '''$1'''",
+ "mwoauth-db-readonly": "Le base de datos OAuth es temporarimente blocate. Tenta lo de novo in alcun minutas.",
+ "mwoauth-missing-field": "Manca un valor pro le campo \"$1\"",
+ "mwoauth-invalid-field": "Valor non valide fornite pro le campo \"$1\"",
+ "mwoauth-invalid-field-generic": "Valor non valide fornite",
+ "mwoauth-field-hidden": "(iste information es celate)",
+ "mwoauth-field-private": "(iste information es private)",
+ "mwoauth-prefs-managegrants": "Applicationes connectite:",
+ "mwoauth-prefs-managegrantslink": "Gerer {{PLURAL:$1|$1 application|$1 applicationes|0=applicationes}} connectite",
+ "mwoauth-consumer-allwikis": "Tote le projectos sur iste sito",
+ "mwoauth-consumer-key": "Clave de consumitor:",
+ "mwoauth-consumer-name": "Nomine del application:",
+ "mwoauth-consumer-version": "Version del consumitor:",
+ "mwoauth-consumer-user": "Editor:",
+ "mwoauth-consumer-stage": "Stato actual:",
+ "mwoauth-consumer-email": "Adresse de e-mail de contacto:",
+ "mwoauth-consumer-email-help": "Solmente visibile pro le personas que approba nove consumitores",
+ "mwoauth-consumer-owner-only-label": "Reservate al proprietario:",
+ "mwoauth-consumer-owner-only": "Iste consumitor pote solmente esser usate per $1.",
+ "mwoauth-consumer-owner-only-help": "Si iste option es seligite, le consumitor essera automaticamente approbate e su uso per $1 essera acceptate. Illo non essera usabile per alcun altere usator e le processo de autorisation normal non functionara. Le actiones prendite con iste consumitor non essera etiquettate.",
+ "mwoauth-consumer-description": "Description del application:",
+ "mwoauth-consumer-callbackurl": "URL de retorno pro OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Permitte al consumitor specificar un appello de retorno in requestas e usar le URL \"callback\" hic supra como prefixo obligatori.",
+ "mwoauth-consumer-granttypes": "Typos de concession requestate:",
+ "mwoauth-consumer-grantsneeded": "Concessiones applicabile:",
+ "mwoauth-consumer-required-grant": "Applicabile al consumitor",
+ "mwoauth-consumer-wiki": "Projecto applicabile:",
+ "mwoauth-consumer-wiki-thiswiki": "Projecto actual ($1)",
+ "mwoauth-consumer-restrictions": "Limitationes de uso:",
+ "mwoauth-consumer-restrictions-json": "Limitationes de uso (JSON):",
+ "mwoauth-consumer-rsakey": "Clave RSA public (optional):",
+ "mwoauth-consumer-rsakey-help": "Entra un clave public pro usar le methodo de signatura RSA-SHA1. Lassa vacue pro usar HMAC-SHA1 con un secreto aleatori. Si tu non es secur qual eliger, lassa lo vacue.",
+ "mwoauth-consumer-secretkey": "Token secrete de consumitor:",
+ "mwoauth-consumer-accesstoken": "Token de accesso:",
+ "mwoauth-consumer-reason": "Motivo:",
+ "mwoauth-consumer-developer-agreement": "Si tu submitte iste application, tu accepta que nos reserva le derecto de disactivar tu application, remover o restringer le accesso a iste sito pro te o pro tu application, e interprender tote altere action que nos considera appropriate si nos crede, a nostre discretion exclusive, que tu o tu application viola alcun politica, directiva, o principio guidante de iste sito. Nos pote modificar iste Politica de Application a omne momento e sin aviso prior, a nostre discretion exclusive e como nos lo considera necessari. Tu uso continuate de OAuth constitue le acceptation de tal modificationes.",
+ "mwoauth-consumer-email-unconfirmed": "Le adresse de e-mail de tu conto non ha ancora essite confirmate.",
+ "mwoauth-consumer-email-mismatched": "Le adresse de e-mail fornite debe corresponder a illo de tu conto.",
+ "mwoauth-consumer-alreadyexists": "Un consumitor con iste combination de nomine/version/editor jam existe.",
+ "mwoauth-consumer-alreadyexistsversion": "Un consumitor con iste combination de nomine e editor jam existe con un version equal o superior (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Impossibile actualisar information pro un requesta de consumitor pendente.",
+ "mwoauth-consumer-not-proposed": "Le consumitor non es proponite actualmente.",
+ "mwoauth-consumer-not-disabled": "Le consumitor non es actualmente disactivate.",
+ "mwoauth-consumer-not-approved": "Le consumitor non es approbate (illo pote haber essite disactivate)",
+ "mwoauth-missing-consumer-key": "Nulle clave de consumitor ha essite fornite.",
+ "mwoauth-invalid-consumer-key": "Nulle consumitor existe con le clave fornite.",
+ "mwoauth-invalid-access-token": "Nulle token de accesso existe con le clave fornite.",
+ "mwoauth-invalid-access-wrongwiki": "Le consumitor pote solmente esser usate sur le projecto \"$1\".",
+ "mwoauth-consumer-conflict": "Alcuno ha cambiate le attributos de iste consumitor durante que tu lo visualisava. Per favor reproba. Pote esser un bon idea verificar le registro de cambiamentos.",
+ "mwoauth-consumer-grantshelp": "Cata concession da accesso al derectos de usator listate que un conto de usator jam ha. Vide le [[Special:ListGrants|tabella de concessiones]] pro plus information.",
+ "mwoauth-consumer-stage-proposed": "proponite",
+ "mwoauth-consumer-stage-rejected": "rejectate",
+ "mwoauth-consumer-stage-expired": "expirate",
+ "mwoauth-consumer-stage-approved": "approbate",
+ "mwoauth-consumer-stage-disabled": "disactivate",
+ "mwoauth-consumer-stage-suppressed": "supprimite",
+ "oauthconsumerregistration": "Registration de consumitor OAuth",
+ "mwoauthconsumerregistration-navigation": "Navigation:",
+ "mwoauthconsumerregistration-propose": "Proponer nove consumitor",
+ "mwoauthconsumerregistration-list": "Mi lista de consumitores",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-text": "Programmatores deberea usar le formulario hic infra pro proponer un nove consumitor OAuth (vide le [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth documentation del extension] pro plus detalios). Post haber submittite iste formulario, tu recipera un token, le qual tu application va usar pro identificar se pro MediaWiki. Un administrator de OAuth debera approbar tu application ante que illo pote esser autorisate per altere usatores.\n\nEcce alcun recommendationes e remarcas:\n* Concede le minus derectos possibile. Evita concessiones que non es necessari in iste momento.\n* Le versiones es in forma \"major.minor.edition\" (le ultime duo es optional) e augmenta quando cambiamentos de concession es necessari.\n* Forni un clave RSA (in formato PEM) si possibile; alteremente un token secrete (e minus secur) te essera assignate.\n* Tu pote usar un ID de projecto pro limitar le consumitor a un singule projecto in iste sito (usa \"*\" pro tote le projectos).",
+ "mwoauthconsumerregistration-update-text": "Le formulario sequente permitte actualisar aspectos de un consumitor OAuth que tu controla.\n\nTote le valores hic superscribera omne previe valores. Non lassa campos vacue si tu non ha le intention de rader iste valores.",
+ "mwoauthconsumerregistration-maintext": "Iste pagina es pro permitter al disveloppatores de proponer e actualisar applicationes de consumitor OAuth in le base de registration de iste sito.\n\nAb hic, tu pote:\n* [[Special:OAuthConsumerRegistration/propose|Requestar un token pro un nove consumitor]].\n* [[Special:OAuthConsumerRegistration/list|Gerer tu consumitores existente]].\n\nPro plus information sur OAuth, vide le [https://www.mediawiki.org/Special:MyLanguage/wiki/Extension:OAuth documentation del extension].",
+ "mwoauthconsumerregistration-propose-legend": "Nove application de consumitor OAuth",
+ "mwoauthconsumerregistration-update-legend": "Actualisar application de consumitor OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Proponer consumitor",
+ "mwoauthconsumerregistration-update-submit": "Actualisar consumitor",
+ "mwoauthconsumerregistration-none": "Tu non controla alcun consumitor OAuth.",
+ "mwoauthconsumerregistration-name": "Consumitor",
+ "mwoauthconsumerregistration-user": "Editor",
+ "mwoauthconsumerregistration-description": "Description",
+ "mwoauthconsumerregistration-email": "E-mail de contacto",
+ "mwoauthconsumerregistration-consumerkey": "Clave de consumitor",
+ "mwoauthconsumerregistration-stage": "Stato",
+ "mwoauthconsumerregistration-lastchange": "Ultime cambiamento",
+ "mwoauthconsumerregistration-manage": "gerer",
+ "mwoauthconsumerregistration-resetsecretkey": "Reinitialisar le clave secrete a un nove valor",
+ "mwoauthconsumerregistration-proposed": "Tu requesta de consumitor OAuth ha essite recipite.\n\nLe systema te ha assignate un token de consumitor '''$1''' e un token secrete '''$2'''. ''Per favor conserva istes pro referentia futur.''",
+ "mwoauthconsumerregistration-created-owner-only": "Tu consumitor OAuth ha essite create.\n\nTu tokens es:\n; Token de consumitor: $1\n; Secreto de consumitor: $2\n; Token de accesso: $3\n; Secreto de accesso: $4\n<em>Per favor, conserva istes pro referentia futur.</em>",
+ "mwoauthconsumerregistration-updated": "Le registration de consumitor OAuth ha essite actualisate.",
+ "mwoauthconsumerregistration-secretreset": "Le systema te ha assignate un token secrete de consumitor '''$1'''. ''Per favor conserva lo pro referentia futur.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Tu tokens de consumitor OAuth ha essite reinitialisate. Le nove tokens es:\n; Token de consumitor: $1\n; Secreto de consumitor: $2\n; Token de accesso: $3\n; Secreto de accesso: $4\n<em>Per favor, conserva istes pro referentia futur.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Tu debe confirmar tu adresse de e-mail pro poter crear applicationes OAuth.\nPer favor entra e valida tu adresse de e-mail per medio de tu [[Special:Preferences|preferentias de usator]].",
+ "oauthmanageconsumers": "Gerer consumitores OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Tu debe aperir session pro acceder a iste pagina.",
+ "mwoauthmanageconsumers-type": "Caudas:",
+ "mwoauthmanageconsumers-showproposed": "Requestas proponite",
+ "mwoauthmanageconsumers-showrejected": "Requestas rejectate",
+ "mwoauthmanageconsumers-showexpired": "Requestas expirate",
+ "mwoauthmanageconsumers-linkproposed": "requestas proponite",
+ "mwoauthmanageconsumers-linkrejected": "requestas rejectate",
+ "mwoauthmanageconsumers-linkexpired": "requestas expirate",
+ "mwoauthmanageconsumers-linkapproved": "requestas approbate",
+ "mwoauthmanageconsumers-linkdisabled": "requestas disactivate",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-maintext": "Iste pagina es pro gerer requestas de application de consumitor OAuth (vide http://oauth.net) e pro gerer le consumitores OAuth establite.",
+ "mwoauthmanageconsumers-queues": "Selige un cauda de confirmation de consumitor del lista sequente:",
+ "mwoauthmanageconsumers-q-proposed": "Cauda de requestas de consumitor proponite",
+ "mwoauthmanageconsumers-q-rejected": "Cauda de requestas de consumitor rejectate",
+ "mwoauthmanageconsumers-q-expired": "Cauda de requestas de consumitor expirate",
+ "mwoauthmanageconsumers-lists": "Selige un lista de stato de consumitor:",
+ "mwoauthmanageconsumers-l-approved": "Lista de consumitores actualmente approbate",
+ "mwoauthmanageconsumers-l-disabled": "Lista de consumitores actualmente disactivate",
+ "mwoauthmanageconsumers-none-proposed": "Nulle consumitores proponite in iste lista.",
+ "mwoauthmanageconsumers-none-rejected": "Nulle consumitores proponite in iste lista.",
+ "mwoauthmanageconsumers-none-expired": "Nulle consumitores proponite in iste lista.",
+ "mwoauthmanageconsumers-none-approved": "Nulle consumitor corresponde a iste criterios.",
+ "mwoauthmanageconsumers-none-disabled": "Nulle consumitor corresponde a iste criterios.",
+ "mwoauthmanageconsumers-name": "Consumitor",
+ "mwoauthmanageconsumers-user": "Editor",
+ "mwoauthmanageconsumers-description": "Description",
+ "mwoauthmanageconsumers-email": "E-mail de contacto",
+ "mwoauthmanageconsumers-consumerkey": "Clave de consumitor",
+ "mwoauthmanageconsumers-lastchange": "Ultime cambiamento",
+ "mwoauthmanageconsumers-review": "revider/gerer",
+ "mwoauthmanageconsumers-confirm-text": "Usa iste formulario pro approbar, rejectar, disactivar o reactivar iste consumitor.",
+ "mwoauthmanageconsumers-confirm-legend": "Gerer consumitor OAuth",
+ "mwoauthmanageconsumers-action": "Cambiar stato:",
+ "mwoauthmanageconsumers-approve": "Approbate",
+ "mwoauthmanageconsumers-reject": "Rejectate",
+ "mwoauthmanageconsumers-rsuppress": "Rejectate e supprimite",
+ "mwoauthmanageconsumers-disable": "Disactivate",
+ "mwoauthmanageconsumers-dsuppress": "Disactivate e supprimite",
+ "mwoauthmanageconsumers-reenable": "Approbate",
+ "mwoauthmanageconsumers-reason": "Motivo:",
+ "mwoauthmanageconsumers-confirm-submit": "Actualisar stato de consumitor",
+ "mwoauthmanageconsumers-success-approved": "Le requesta ha essite approbate.",
+ "mwoauthmanageconsumers-success-rejected": "Le requesta ha essite rejectate.",
+ "mwoauthmanageconsumers-success-disabled": "Le consumitor ha essite disactivate.",
+ "mwoauthmanageconsumers-success-reanable": "Le consumitor ha essite reactivate.",
+ "mwoauthmanageconsumers-search-name": "consumitores con iste nomine",
+ "mwoauthmanageconsumers-search-publisher": "consumitores de iste usator",
+ "oauthlistconsumers": "Lista de applicationes OAuth",
+ "mwoauthlistconsumers-legend": "Explorar applicationes OAuth",
+ "mwoauthlistconsumers-view": "detalios",
+ "mwoauthlistconsumers-none": "Nulle application trovate que satisface iste criterios.",
+ "mwoauthlistconsumers-name": "Nomine del application",
+ "mwoauthlistconsumers-version": "Version del consumitor",
+ "mwoauthlistconsumers-user": "Editor",
+ "mwoauthlistconsumers-description": "Description",
+ "mwoauthlistconsumers-wiki": "Projecto applicabile",
+ "mwoauthlistconsumers-callbackurl": "URL de retorno pro OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Permitte al consumitor specificar un appello de retorno in requestas e usar le URL \"callback\" hic supra como prefixo obligatori.",
+ "mwoauthlistconsumers-grants": "Concessiones applicabile",
+ "mwoauthlistconsumers-basicgrantsonly": "(accesso basic solmente)",
+ "mwoauthlistconsumers-status": "Stato",
+ "mwoauth-consumer-stage-any": "omne",
+ "mwoauthlistconsumers-status-proposed": "proponite",
+ "mwoauthlistconsumers-status-approved": "approbate",
+ "mwoauthlistconsumers-status-disabled": "disactivate",
+ "mwoauthlistconsumers-status-rejected": "rejectate",
+ "mwoauthlistconsumers-status-expired": "expirate",
+ "mwoauthlistconsumers-rclink": "Modificationes recente per iste application",
+ "oauthmanagemygrants": "Gerer applicationes connectite",
+ "mwoauthmanagemygrants-text": "Iste pagina lista tote le applicationes que pote usar tu conto. Pro cata un de iste applicationes, su perimetro de accesso es limitate per le derectos que tu ha concedite a illo quando tu lo autorisava a ager in tu nomine. Si tu ha separatemente autorisate un application a acceder a differente projectos fratres in tu nomine, alora tu videra un configuration distincte pro cata un de iste projectos hic infra.\n\nLe applicationes connectite accede a tu conto usante le protocollo OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Lege plus sur applicationes connectite])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigation:",
+ "mwoauthmanagemygrants-showlist": "Lista de applicationes connectite",
+ "mwoauthmanagemygrants-none": "Nulle application es connectite a tu conto.",
+ "mwoauthmanagemygrants-user": "Editor:",
+ "mwoauthmanagemygrants-description": "Description",
+ "mwoauthmanagemygrants-wikiallowed": "Permittite in projecto:",
+ "mwoauthmanagemygrants-grants": "Concessiones applicabile",
+ "mwoauthmanagemygrants-grantsallowed": "Concessiones permittite",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Concessiones applicabile permittite:",
+ "mwoauthmanagemygrants-review": "gerer accesso",
+ "mwoauthmanagemygrants-revoke": "revocar accesso",
+ "mwoauthmanagemygrants-grantaccept": "Concedite",
+ "mwoauthmanagemygrants-update-text": "Usa le formulario sequente pro modificar le permissiones concedite a un application pro ager in tu nomine.",
+ "mwoauthmanagemygrants-revoke-text": "Usa le formulario sequente pro revocar le accesso de un application pro ager in tu nomine.",
+ "mwoauthmanagemygrants-confirm-legend": "Gerer application connectite",
+ "mwoauthmanagemygrants-update": "Actualisar concessiones",
+ "mwoauthmanagemygrants-renounce": "Disautorisar",
+ "mwoauthmanagemygrants-action": "Cambiar stato:",
+ "mwoauthmanagemygrants-confirm-submit": "Actualisar le stato de token de accesso",
+ "mwoauthmanagemygrants-success-update": "Le preferentias pro iste application ha essite actualisate.",
+ "mwoauthmanagemygrants-success-renounce": "Le accesso de iste application a tu conto ha essite revocate.",
+ "mwoauthmanagemygrants-basic-tooltip": "Proque non pote io modificar iste concession? Iste concession da a tu application connectite le permissiones de base necessari pro functionar correctemente. Si tu non vole que iste application connectite ha iste derectos, tu debe revocar su accesso.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Proque non pote io modificar iste concession? Si tu non vole que iste application connectite ha iste derecto, tu debe revocar su accesso.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Tu}} modificationes per iste application",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Tu}} actiones per iste application",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|proponeva}} un consumitor OAuth (clave de consumitor $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|actualisava}} un consumitor OAuth (clave de consumitor $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|approbava}} un consumitor OAuth per $3 (clave de consumitor $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|rejectava}} un consumitor OAuth per $3 (clave de consumitor $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|disactivava}} un consumitor OAuth per $3 (clave de consumitor $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|reactivava}} un consumitor OAuth per $3 (clave de consumitor $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|creava}} un consumitor OAuth reservate al proprietario (clave de consumitor $4)",
+ "log-action-filter-mwoauthconsumer": "Typo de action de consumitor OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Approbation de consumitor OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Creation de un consumitor OAuth reservate al proprietario",
+ "log-action-filter-mwoauthconsumer-disable": "Disactivation de consumitor OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Proposition de consumitor OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Reactivation de consumitor OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Rejection de consumitor OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Actualisation de consumitor OAuth",
+ "mwoauthconsumer-consumer-logpage": "Registro de consumitores OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Registro de approbation, rejection e disactivation de consumitores OAuth registrate.",
+ "mwoauth-bad-request-missing-params": "Un error ha occurrite durante le configuration de iste application connectite. Contacta le disveloppator del application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Parametros OAuth mancante, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Un error ha occurrite. Contacta le autor del application pro adjuta.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL incognite, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Un error ha occurrite. [$1 Contacta] le autor del application pro adjuta.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL incognite, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Nulle concession approbate ha essite trovate pro iste token de autorisation.",
+ "mwoauthdatastore-request-token-not-found": "Un error ha occurrite durante le tentativa de connecter iste application.\nRetorna e reproba connecter tu conto, o contacta le autor del application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Token OAuth non trovate, $1</span>",
+ "mwoauthdatastore-callback-not-found": "Le URL de retorno OAuth non ha essite trovate in le cache. Isto es probabilemente un error in le maniera in que le application invia requestas al servitor.",
+ "mwoauthdatastore-request-token-already-used": "Iste requesta ha jam essite concludite e non pote esser resubmittite.\nRetorna al application e tenta connecter tu conto de novo, o contacta le autor del application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Token OAuth jam usate, $1</span>",
+ "mwoauthdatastore-bad-token": "Nulle token ha essite trovate que corresponde a tu requesta.",
+ "mwoauthdatastore-bad-source-ip": "Le requesta proveni de un adresse IP non valide.",
+ "mwoauthdatastore-bad-verifier": "Le codice de verification fornite non es valide.",
+ "mwoauthdatastore-invalid-token-type": "Le typo de token requestate non es valide.",
+ "mwoauthgrants-general-error": "Il habeva un error in tu requesta OAuth.",
+ "mwoauthserver-bad-consumer": "\"$1\" non es approbate como application connectite. [$2 Contacta] le autor del application pro adjuta.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Application OAuth connectite non approbate, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Un error ha occurrite durante le connexion de iste application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Clave OAuth incognite, $1</span>",
+ "mwoauthserver-insufficient-rights": "Tu conto non es autorisate a connecter applicationes. Contacta le administrator del sito pro saper proque.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Derectos de usator OAuth insufficiente, $1</span>",
+ "mwoauthserver-invalid-request-token": "Il ha un token non valide in tu requesta.",
+ "mwoauthserver-invalid-user": "Pro usar applicationes connectite sur iste sito, tu debe haber un conto commun inter tote le projectos. Quando tu ha un conto sur tote le projectos, tu pote tentar connecter \"$1\" de novo.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Conto unificate necessari, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Un error ha occurrite durante le connexion de iste application.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Le consumitor non ha clave secrete, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" es un application connectite reservate al proprietario. Pro obtener le token de accesso, vide [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Le consumitor es reservate al proprietario, $3</span>",
+ "mwoauth-invalid-authorization-title": "Error de autorisation OAuth",
+ "mwoauth-invalid-authorization": "Le capites de autorisation in tu requesta non es valide: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Le capites de autorisation in tu requesta non es valide pro $1",
+ "mwoauth-invalid-authorization-invalid-user": "Le capites de autorisation in tu requesta es pro un usator que non existe hic",
+ "mwoauth-invalid-authorization-wrong-user": "Le capites de autorisation in tu requesta es pro un altere usator",
+ "mwoauth-invalid-authorization-not-approved": "Le application que tu tenta connecter sembla mal configurate. Contacta le autor de \"$1\" pro adjuta.",
+ "mwoauth-invalid-authorization-blocked-user": "Le capites de autorisation in tu requesta es pro un usator blocate",
+ "mwoauth-form-description-allwikis": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de exequer le sequente actiones in tu nomine sur tote le projectos de iste sito:\n\n$4",
+ "mwoauth-form-description-onewiki": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de exequer le sequente actiones in tu nomine sur ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de acceder in tu nomine a informationes sur tote le projectos de iste sito. Nulle modification essera facite con tu conto.",
+ "mwoauth-form-description-onewiki-nogrants": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de acceder in tu nomine a informationes sur ''$4''. Nulle modification essera facite con tu conto.",
+ "mwoauth-form-description-allwikis-privateinfo": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de acceder a informationes sur te, como tu nomine real e adresse de e-mail, sur tote le projectos de iste sito. Nulle modification essera facite con tu conto.",
+ "mwoauth-form-description-onewiki-privateinfo": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de acceder a informationes sur te, como tu nomine real e adresse de e-mail, sur ''$4''. Nulle modification essera facite con tu conto.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de acceder a informationes sur te, como tu adresse de e-mail, sur tote le projectos de iste sito. Nulle modification essera facite con tu conto.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Salute $1,\n\nA fin de concluder tu requesta, '''$2''' require le permission de acceder a informationes sur te, como tu adresse de e-mail, sur ''$4''. Nulle modification essera facite con tu conto.",
+ "mwoauth-form-button-approve": "Autorisar",
+ "mwoauth-form-button-cancel": "Cancellar",
+ "mwoauth-error": "Error de connexion del application",
+ "mwoauth-grants-heading": "Permissiones requestate:",
+ "mwoauth-grants-nogrants": "Le application non ha requestate alcun permission.",
+ "mwoauth-acceptance-cancelled": "Tu ha refusate a \"$1\" le accesso a tu conto. \"$1\" non pote functionar si tu non lo permitte de acceder. Tu pote retornar a \"$1\" o [[Special:OAuthManageMyGrants|gerer]] tu applicationes connectite.",
+ "mwoauth-granttype-normal": "Requestar autorisation pro permissiones specific.",
+ "grant-mwoauth-authonly": "Verification del identitate del usator solmente, sin possibilitate de leger paginas o ager in le nomine del usator.",
+ "grant-mwoauth-authonlyprivate": "Verification del identitate del usator con accesso al nomine real e adresse de e-mail, sin possibilitate de leger paginas o ager in le nomine del usator.",
+ "mwoauth-listgrants-extra-summary": "== Concessiones specific a OAuth ==\n\nIste concessiones additional es applicabile a consumitores OAuth.",
+ "mwoauth-oauth-exception": "Un error ha occurrite in le protocollo OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback debe esser definite, e debe esser mittite a \"oob\" (distingue inter majusculas e minusculas)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback debe esser definite, e debe esser mittite a \"oob\" (in minusculas), o le appello de retorno configurate debe esser un prefixo del appello de retorno fornite.",
+ "right-mwoauthproposeconsumer": "Proponer nove consumitores OAuth",
+ "right-mwoauthupdateownconsumer": "Actualisar le consumitores OAuth que tu controla",
+ "right-mwoauthmanageconsumer": "Gerer consumitores OAuth",
+ "right-mwoauthsuppress": "Supprimer consumitores OAuth",
+ "right-mwoauthviewsuppressed": "Vider consumitores OAuth supprimite",
+ "right-mwoauthviewprivate": "Vider datos OAuth private",
+ "right-mwoauthmanagemygrants": "Gerer concessiones OAuth",
+ "action-mwoauthmanageconsumer": "gerer consumitores OAuth",
+ "action-mwoauthsuppress": "supprimer consumitores OAuth",
+ "action-mwoauthmanagemygrants": "gerer le proprie concessiones OAuth",
+ "action-mwoauthproposeconsumer": "proponer nove consumitores OAUth",
+ "action-mwoauthupdateownconsumer": "actualisar le consumitores OAuth que tu controla",
+ "action-mwoauthviewprivate": "vider datos OAuth private",
+ "action-mwoauthviewsuppressed": "vider consumitores OAUth supprimite",
+ "mwoauth-tag-reserved": "Le etiquettas comenciante per <code>OAuth CID:</code> es reservate pro uso per OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Nota:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> es plus secur que le contrasignos de robot e deberea esser preferite si le robot lo supporta.",
+ "mwoauth-api-module-disabled": "Le modulo \"$1\" non es disponibile con OAuth.",
+ "echo-category-title-oauth-owner": "Disveloppamento de OAuth",
+ "echo-pref-tooltip-oauth-owner": "Notificar me sur eventos associate al applicationes OAuth que io ha create.",
+ "echo-category-title-oauth-admin": "Administrator de OAuth",
+ "echo-pref-tooltip-oauth-admin": "Notificar me sur eventos associate al revision de applicationes OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|proponeva}} un nove application OAuth: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|actualisava}} le application OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|approbava}} {{GENDER:$3|tu}} application OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|rejectava}} {{GENDER:$3|tu}} application OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|disactivava}} {{GENDER:$3|tu}} application OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reactivava}} {{GENDER:$3|tu}} application OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|proponeva}} un nove application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|actualisava}} un application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|approbava}} {{GENDER:$3|tu}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|rejectava}} {{GENDER:$3|tu}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|disactivava}} {{GENDER:$3|tu}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reactivava}} {{GENDER:$3|tu}} application OAuth sur {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Revider application",
+ "notification-oauth-app-update-primary-link": "Revider application",
+ "notification-oauth-app-approve-primary-link": "Vider application",
+ "notification-oauth-app-reject-primary-link": "Vider application",
+ "notification-oauth-app-disable-primary-link": "Vider application",
+ "notification-oauth-app-reenable-primary-link": "Vider application",
+ "notification-oauth-app-body": "Motivo: $1",
+ "mwoauthconsumer-consumer-view": "Vider iste consumitor",
+ "mwoauthconsumer-application-view": "Vider iste application"
+}
diff --git a/OAuth/i18n/id.json b/OAuth/i18n/id.json
new file mode 100644
index 00000000..e6673f17
--- /dev/null
+++ b/OAuth/i18n/id.json
@@ -0,0 +1,80 @@
+{
+ "@metadata": {
+ "authors": [
+ "Diki Ananta",
+ "Ilham151096",
+ "Iwan Novirion",
+ "Raynasution",
+ "Sumbukompor"
+ ]
+ },
+ "mwoauth-invalid-field": "Terjadi nilai tidak valid untuk bidang \"$1\"",
+ "mwoauth-invalid-field-generic": "Terjadi nilai tidak valid",
+ "mwoauth-field-hidden": "(informasi ini tersembunyi)",
+ "mwoauth-field-private": "(informasi ini pribadi)",
+ "mwoauth-prefs-managegrants": "Aplikasi terhubung:",
+ "mwoauth-prefs-managegrantslink": "Kelola {{PLURAL:$1|$1 aplikasi terhubung|$1 aplikasi terhubung|0=aplikasi terhubung}}",
+ "mwoauth-consumer-allwikis": "Semua proyek di situs ini",
+ "mwoauth-consumer-key": "Kunci konsumen:",
+ "mwoauth-consumer-name": "Nama aplikasi:",
+ "mwoauth-consumer-version": "Versi konsumen:",
+ "mwoauth-consumer-user": "Pengembang:",
+ "mwoauth-consumer-stage": "Status terkini:",
+ "mwoauth-consumer-email": "Alamat surel:",
+ "mwoauth-consumer-description": "Deskripsi aplikasi:",
+ "mwoauth-consumer-wiki": "Digunakan pada",
+ "mwoauth-consumer-stage-proposed": "proposal",
+ "mwoauth-consumer-stage-rejected": "ditolak",
+ "mwoauth-consumer-stage-expired": "kadaluarsa",
+ "mwoauth-consumer-stage-approved": "diterima",
+ "mwoauth-consumer-stage-disabled": "nonaktif",
+ "mwoauth-consumer-stage-suppressed": "mati",
+ "mwoauthconsumerregistration-navigation": "Navigasi:",
+ "mwoauthconsumerregistration-user": "Pengembang",
+ "mwoauthconsumerregistration-description": "Deskripsi",
+ "mwoauthconsumerregistration-email": "Email kontak",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Perubahan terakhir",
+ "mwoauthconsumerregistration-manage": "kelola",
+ "mwoauthmanageconsumers-description": "Deskripsi",
+ "mwoauthmanageconsumers-search-publisher": "konsumen dari pengguna ini",
+ "oauthlistconsumers": "Daftar aplikasi OAuth",
+ "mwoauthlistconsumers-legend": "Cari aplikasi OAuth",
+ "mwoauthlistconsumers-view": "rincian",
+ "mwoauthlistconsumers-user": "Pengembang",
+ "mwoauthlistconsumers-description": "Deskripsi",
+ "mwoauthlistconsumers-wiki": "Digunakan pada",
+ "mwoauth-consumer-stage-any": "semua",
+ "mwoauthlistconsumers-status-proposed": "proposal",
+ "mwoauthlistconsumers-status-approved": "diterima",
+ "mwoauthlistconsumers-status-disabled": "nonaktif",
+ "mwoauthlistconsumers-status-rejected": "ditolak",
+ "mwoauthlistconsumers-status-expired": "kadaluarsa",
+ "oauthmanagemygrants": "Kelola aplikasi terhubung",
+ "mwoauthmanagemygrants-text": "Halaman ini berisi daftar aplikasi yang dapat menggunakan akun Anda. Aplikasi-aplikasi tersebut, ruang lingkup aksesnya terbatas pada hak akses yang Anda berikan saat Anda memberi wewenang untuk bertindak atas nama Anda. Jika Anda secara terpisah memberi wewenang pada aplikasi untuk mengakses proyek wiki yang berbeda atas nama Anda, maka Anda akan melihat konfigurasi terpisah untuk setiap proyek seperti di bawah ini.\n\nAplikasi yang terhubung mengakses akun Anda dengan menggunakan protokol OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Pelajari lebih lanjut mengenai aplikasi yang terhubung])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigasi:",
+ "mwoauthmanagemygrants-showlist": "Daftar aplikasi tersambung",
+ "mwoauthmanagemygrants-user": "Pengembang:",
+ "mwoauthmanagemygrants-description": "Deskripsi",
+ "mwoauthmanagemygrants-wikiallowed": "Diizinkan pada proyek:",
+ "mwoauthmanagemygrants-grants": "Akses yang diberikan",
+ "mwoauthmanagemygrants-grantsallowed": "Akses yang diperbolehkan",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Hak yang diberikan:",
+ "mwoauthmanagemygrants-review": "kelola akses",
+ "mwoauthmanagemygrants-revoke": "cabut akses",
+ "mwoauthmanagemygrants-grantaccept": "Diizinkan",
+ "mwoauthmanagemygrants-update-text": "Gunakan formulir di bawah untuk mengubah hak akses yang diberikan kepada aplikasi untuk bertindak atas nama Anda.",
+ "mwoauthmanagemygrants-revoke-text": "Gunakan formulir di bawah ini untuk mencabut akses dari aplikasi yang bertindak atas nama Anda.",
+ "mwoauthmanagemygrants-confirm-legend": "Kelola aplikasi terhubung",
+ "mwoauthmanagemygrants-update": "Perbarui izin",
+ "mwoauthmanagemygrants-renounce": "Cabut akses",
+ "mwoauthmanagemygrants-action": "Ubah status:",
+ "mwoauthmanagemygrants-confirm-submit": "Perbarui status akses token",
+ "mwoauthmanagemygrants-success-update": "Preferensi Anda untuk aplikasi ini sudah diperbarui.",
+ "mwoauth-invalid-authorization-title": "Galat otorisasi OAuth",
+ "mwoauth-form-button-approve": "Izinkan",
+ "mwoauth-form-button-cancel": "Batal",
+ "mwoauth-error": "Galat Koneksi Aplikasi",
+ "mwoauth-grants-heading": "Mohon izin:",
+ "mwoauth-grants-nogrants": "Aplikasi tidak ada mengajukan permohonan izin."
+}
diff --git a/OAuth/i18n/ig.json b/OAuth/i18n/ig.json
new file mode 100644
index 00000000..1bb1d809
--- /dev/null
+++ b/OAuth/i18n/ig.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ukabia"
+ ]
+ },
+ "mwoauthconsumerregistration-navigation": "Nturuụzọ̀:",
+ "mwoauthmanagemygrants-navigation": "Nturuụzọ̀:",
+ "mwoauth-form-button-cancel": "Hapụ̀"
+}
diff --git a/OAuth/i18n/inh.json b/OAuth/i18n/inh.json
new file mode 100644
index 00000000..c94b28f0
--- /dev/null
+++ b/OAuth/i18n/inh.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Adam-Yourist"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Хьатесса приложенеш:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|$1 приложенена урхалду|1=приложенена урхалду}}",
+ "oauthmanagemygrants": "Хьатессача приложенешта урхалдар"
+}
diff --git a/OAuth/i18n/io.json b/OAuth/i18n/io.json
new file mode 100644
index 00000000..2d7fa779
--- /dev/null
+++ b/OAuth/i18n/io.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joao Xavier"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplikaji relata:",
+ "mwoauth-prefs-managegrantslink": "Administrar $1 {{PLURAL:$1|aplikajo|aplikaji}} relata",
+ "mwoauth-consumer-user": "Publikigero:",
+ "mwoauth-consumer-reason": "Motivo:",
+ "mwoauthconsumerregistration-user": "Publikigero",
+ "mwoauth-consumer-stage-any": "irga",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Saluto $1,\n\nPor atendar komplete vua demando, '''$2''' bezonas acesar informi pri vu, inkluzite vua e-posto adreso, en omna projeti de ca retsituo. Nula modifikuri facesos en vua uzero-konto.",
+ "mwoauth-form-button-cancel": "Nuligar",
+ "notification-oauth-app-reject-primary-link": "Montrez 'app'",
+ "notification-oauth-app-disable-primary-link": "Vidar 'app'",
+ "notification-oauth-app-body": "Motivo: $1"
+}
diff --git a/OAuth/i18n/is.json b/OAuth/i18n/is.json
new file mode 100644
index 00000000..28fe43d4
--- /dev/null
+++ b/OAuth/i18n/is.json
@@ -0,0 +1,53 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sveinn í Felli"
+ ]
+ },
+ "mwoauth-consumer-key": "Lykill notanda:",
+ "mwoauth-consumer-name": "Heiti forrits:",
+ "mwoauth-consumer-user": "Útgefandi:",
+ "mwoauth-consumer-stage": "Núverandi staða:",
+ "mwoauth-consumer-reason": "Ástæða:",
+ "mwoauth-consumer-stage-rejected": "hafnað",
+ "mwoauth-consumer-stage-expired": "útrunnið",
+ "mwoauth-consumer-stage-approved": "samþykkt",
+ "mwoauth-consumer-stage-disabled": "óvirkt",
+ "mwoauthconsumerregistration-navigation": "Flakk:",
+ "mwoauthconsumerregistration-main": "Aðal",
+ "mwoauthconsumerregistration-user": "Útgefandi",
+ "mwoauthconsumerregistration-description": "Lýsing",
+ "mwoauthconsumerregistration-email": "Netfang tengiliðar",
+ "mwoauthconsumerregistration-consumerkey": "Lykill notanda",
+ "mwoauthconsumerregistration-stage": "Staða",
+ "mwoauthconsumerregistration-lastchange": "Síðasta breyting",
+ "mwoauthconsumerregistration-manage": "stjórna",
+ "mwoauthmanageconsumers-type": "Biðraðir:",
+ "mwoauthmanageconsumers-main": "Aðal",
+ "mwoauthmanageconsumers-user": "Útgefandi",
+ "mwoauthmanageconsumers-description": "Lýsing",
+ "mwoauthmanageconsumers-email": "Netfang tengiliðar",
+ "mwoauthmanageconsumers-consumerkey": "Lykill notanda",
+ "mwoauthmanageconsumers-lastchange": "Síðasta breyting",
+ "mwoauthmanageconsumers-approve": "Samþykkt",
+ "mwoauthmanageconsumers-reject": "Hafnað",
+ "mwoauthmanageconsumers-disable": "Óvirk",
+ "mwoauthmanageconsumers-reenable": "Samþykkt",
+ "mwoauthmanageconsumers-reason": "Ástæða:",
+ "mwoauthlistconsumers-view": "nánar",
+ "mwoauthlistconsumers-name": "Heiti forrits",
+ "mwoauthlistconsumers-user": "Útgefandi",
+ "mwoauthlistconsumers-description": "Lýsing",
+ "mwoauthlistconsumers-status": "Staða",
+ "mwoauth-consumer-stage-any": "hvað sem er",
+ "mwoauthlistconsumers-status-approved": "samþykkt",
+ "mwoauthlistconsumers-status-disabled": "óvirkt",
+ "mwoauthlistconsumers-status-rejected": "hafnað",
+ "mwoauthlistconsumers-status-expired": "útrunnið",
+ "mwoauthmanagemygrants-navigation": "Flakk:",
+ "mwoauthmanagemygrants-user": "Útgefandi:",
+ "mwoauthmanagemygrants-description": "Lýsing",
+ "mwoauth-form-button-approve": "Leyfa",
+ "mwoauth-form-button-cancel": "Hætta við",
+ "notification-oauth-app-body": "Ástæða: $1"
+}
diff --git a/OAuth/i18n/it.json b/OAuth/i18n/it.json
new file mode 100644
index 00000000..ccf09d60
--- /dev/null
+++ b/OAuth/i18n/it.json
@@ -0,0 +1,268 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alexmar983",
+ "Beta16",
+ "Giromin Cangiaxo",
+ "Nemo bis",
+ "Ricordisamoa",
+ "Sakretsu",
+ "Selven"
+ ]
+ },
+ "mwoauth-desc": "Consente l'utilizzo di OAuth 1.0a e OAuth 2.0 per le API di autorizzazione",
+ "mwoauth-verified": "Ora è consentito all'applicazione di accedere a MediaWiki per tuo conto.\n\nPer completare il processo, inserisci questo valore per la verifica nel'applicazione: '''$1'''",
+ "mwoauth-db-readonly": "Il database di OAuth è temporaneamente bloccato. Riprova tra pochi minuti.",
+ "mwoauth-missing-field": "Valore mancante per il campo \"$1\".",
+ "mwoauth-invalid-field": "Valore non valido per il campo \"$1\".",
+ "mwoauth-invalid-field-generic": "Valore indicato non valido",
+ "mwoauth-field-hidden": "(questa informazione è nascosta)",
+ "mwoauth-field-private": "(questa informazione è privata)",
+ "mwoauth-prefs-managegrants": "Applicazioni collegate:",
+ "mwoauth-prefs-managegrantslink": "Gestisci {{PLURAL:$1|$1 applicazione collegata|$1 applicazioni collegate|0=applicazioni collegate}}",
+ "mwoauth-consumer-allwikis": "Tutti i progetti su questo sito",
+ "mwoauth-consumer-key": "Chiave cliente:",
+ "mwoauth-consumer-name": "Nome applicazione:",
+ "mwoauth-consumer-version": "Versione cliente:",
+ "mwoauth-consumer-user": "Editore:",
+ "mwoauth-consumer-stage": "Stato attuale:",
+ "mwoauth-consumer-email": "Indirizzo email di contatto:",
+ "mwoauth-consumer-owner-only-label": "Solo proprietario:",
+ "mwoauth-consumer-owner-only": "Questo cliente può essere usato solo da $1.",
+ "mwoauth-consumer-description": "Descrizione applicazione:",
+ "mwoauth-consumer-callbackurl": "URL di \"callback\" OAuth:",
+ "mwoauth-consumer-granttypes": "Tipi di assegnazioni che saranno richieste:",
+ "mwoauth-consumer-grantsneeded": "Assegnazioni applicabili:",
+ "mwoauth-consumer-required-grant": "Applicabile al cliente",
+ "mwoauth-consumer-wiki": "Progetti applicabili:",
+ "mwoauth-consumer-wiki-thiswiki": "Progetto attuale ($1)",
+ "mwoauth-consumer-restrictions": "Restrizioni d'uso:",
+ "mwoauth-consumer-restrictions-json": "Restrizioni d'uso (JSON):",
+ "mwoauth-consumer-rsakey": "Chiave RSA pubblica (facoltativa):",
+ "mwoauth-consumer-secretkey": "Token segreto cliente:",
+ "mwoauth-consumer-accesstoken": "Token di accesso:",
+ "mwoauth-consumer-reason": "Motivo:",
+ "mwoauth-consumer-email-unconfirmed": "Il tuo indirizzo email non è stato ancora confermato.",
+ "mwoauth-consumer-email-mismatched": "L'indirizzo email fornito deve corrispondere a quello della tua utenza.",
+ "mwoauth-consumer-alreadyexists": "Un cliente con questa combinazione di nome/versione/editore esiste già",
+ "mwoauth-consumer-alreadyexistsversion": "Un cliente con questa combinazione di nome/editore esiste già con una versione uguale o superiore (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Non è possibile aggiornare le informazioni per una richiesta in sospeso",
+ "mwoauth-consumer-not-proposed": "Il cliente non è attualmente proposto",
+ "mwoauth-consumer-not-disabled": "Il cliente non è attualmente disabilitato",
+ "mwoauth-consumer-not-approved": "Il cliente non è approvato (potrebbe essere stato disabilitato)",
+ "mwoauth-missing-consumer-key": "Non è stata fornita alcuna chiave cliente.",
+ "mwoauth-invalid-consumer-key": "Non esiste alcun cliente con la chiave specificata.",
+ "mwoauth-invalid-access-token": "Non esiste alcun token di accesso con la chiave specificata.",
+ "mwoauth-invalid-access-wrongwiki": "Il cliente può essere utilizzato solo nel progetto \"$1\".",
+ "mwoauth-consumer-conflict": "Qualcuno ha cambiato gli attributi di questo cliente, come si visto. Per favore riprova. Si consiglia di controllare il registro delle modifiche.",
+ "mwoauth-consumer-grantshelp": "Ogni concessione dà accesso ai diritti elencati per cui l'utenza già dispone. Vedi la [[Special:ListGrants|tabella delle assegnazioni]] per ulteriori informazioni.",
+ "mwoauth-consumer-stage-proposed": "proposto",
+ "mwoauth-consumer-stage-rejected": "respinto",
+ "mwoauth-consumer-stage-expired": "scaduto",
+ "mwoauth-consumer-stage-approved": "approvato",
+ "mwoauth-consumer-stage-disabled": "disabilitato",
+ "mwoauth-consumer-stage-suppressed": "soppresso",
+ "oauthconsumerregistration": "Registrazione cliente OAuth",
+ "mwoauthconsumerregistration-navigation": "Navigazione:",
+ "mwoauthconsumerregistration-propose": "Proponi nuovo cliente",
+ "mwoauthconsumerregistration-list": "Miei clienti",
+ "mwoauthconsumerregistration-main": "Principale",
+ "mwoauthconsumerregistration-propose-text": "Gli sviluppatori dovrebbero usare il seguente modulo per proporre un nuovo cliente OAuth (vedi la [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth documentazione dell'estensione] per ulteriori dettagli). Dopo l'invio di questo modulo, riceverai un token che l'applicazione utilizzerà per identificarsi in MediaWiki. Un amministratore di OAuth dovrà approvare l'applicazione prima che questa potrà essere autorizzata da altri utenti.\n\nAlcune raccomandazioni e osservazioni:\n* cerca di utilizzare meno assegnazioni possibili. Cerca di evitare di assegnare diritti che non sono realmente necessari ora\n* le versioni sono nella forma \"major.minor.release\" (gli ultimi due sono opzionali) ed aumentala nel caso siano necessarie ulteriori assegnazioni di diritti\n* fornisce una chiave RSA pubblica (in formato PEM) se possibile; altrimenti dovrà essere utilizzato un token segreto (meno sicuro)\n* è possibile utilizzare un ID progetto per limitare il cliente ad un singolo progetto su questo sito (usa \"*\" per tutti i progetti).",
+ "mwoauthconsumerregistration-update-text": "Utilizza il modulo qui sotto per aggiornare gli aspetti di un cliente OAuth che controlli.\n\nI valori qui sovrascriveranno tutti quelli precedenti. Non lasciarli in bianco se non hai intenzione di cancellare quei valori.",
+ "mwoauthconsumerregistration-maintext": "Questa pagina consente agli sviluppatori di proporre e aggiornare le applicazioni OAuth registrate in questo sito.\n\nDa qui è possibile:\n* [[Special:OAuthConsumerRegistration/propose|richiedere un token per un nuovo cliente]]\n* [[Special:OAuthConsumerRegistration/list|gestire i tuoi clienti esistenti]].\n\nPer ulteriori informazioni su OAuth, vedi la [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth documentazione dell'estensione].",
+ "mwoauthconsumerregistration-propose-legend": "Nuova applicazione cliente OAuth",
+ "mwoauthconsumerregistration-update-legend": "Aggiorna applicazione cliente OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Proponi cliente",
+ "mwoauthconsumerregistration-update-submit": "Aggiorna cliente",
+ "mwoauthconsumerregistration-none": "Non controlli alcun cliente OAuth.",
+ "mwoauthconsumerregistration-name": "Cliente",
+ "mwoauthconsumerregistration-user": "Editore",
+ "mwoauthconsumerregistration-description": "Descrizione",
+ "mwoauthconsumerregistration-email": "Email di contatto",
+ "mwoauthconsumerregistration-consumerkey": "Chiave cliente",
+ "mwoauthconsumerregistration-stage": "Stato",
+ "mwoauthconsumerregistration-lastchange": "Ultima modifica",
+ "mwoauthconsumerregistration-manage": "gestisci",
+ "mwoauthconsumerregistration-resetsecretkey": "Reimposta la chiave segreta ad un nuovo valore",
+ "mwoauthconsumerregistration-proposed": "La tua richiesta per il cliente OAuth è stata ricevuta.\n\nTi è stato assegnato il token cliente '''$1''' e il token segreto '''$2'''. ''Registra questi dati per riferimenti futuri.''",
+ "mwoauthconsumerregistration-updated": "La registrazione del tuo cliente OAuth è stata aggiornata.",
+ "mwoauthconsumerregistration-secretreset": "Ti è stato assegnato il token segreto '''$1'''. ''Prendi nota per farne uso in futuro.''",
+ "oauthmanageconsumers": "Gestione clienti OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Devi effettuare l'accesso per accedere a questa pagina.",
+ "mwoauthmanageconsumers-type": "Code:",
+ "mwoauthmanageconsumers-showproposed": "Richieste proposte",
+ "mwoauthmanageconsumers-showrejected": "Richieste respinte",
+ "mwoauthmanageconsumers-showexpired": "Richieste scadute",
+ "mwoauthmanageconsumers-linkproposed": "richieste proposte",
+ "mwoauthmanageconsumers-linkrejected": "richieste respinte",
+ "mwoauthmanageconsumers-linkexpired": "richieste scadute",
+ "mwoauthmanageconsumers-linkapproved": "richieste approvate",
+ "mwoauthmanageconsumers-linkdisabled": "richieste disabilitate",
+ "mwoauthmanageconsumers-main": "Principale",
+ "mwoauthmanageconsumers-maintext": "Questa pagina è usata per la gestione delle applicazioni OAuth (vedi http://oauth.net), delle richieste e dei clienti istituiti.",
+ "mwoauthmanageconsumers-queues": "Seleziona una coda di conferma dei clienti da sotto:",
+ "mwoauthmanageconsumers-q-proposed": "Coda delle richieste proposte",
+ "mwoauthmanageconsumers-q-rejected": "Coda delle richieste respinte",
+ "mwoauthmanageconsumers-q-expired": "Coda delle richieste scadute",
+ "mwoauthmanageconsumers-lists": "Seleziona uno stato del cliente da sotto:",
+ "mwoauthmanageconsumers-l-approved": "Elenco dei clienti attualmente approvati",
+ "mwoauthmanageconsumers-l-disabled": "Elenco dei clienti attualmente disabilitati",
+ "mwoauthmanageconsumers-none-proposed": "Nessun cliente in questa lista.",
+ "mwoauthmanageconsumers-none-rejected": "Nessun cliente in questa lista.",
+ "mwoauthmanageconsumers-none-expired": "Nessun cliente in questa lista.",
+ "mwoauthmanageconsumers-none-approved": "Nessun cliente soddisfa questo criterio.",
+ "mwoauthmanageconsumers-none-disabled": "Nessun cliente soddisfa questo criterio.",
+ "mwoauthmanageconsumers-name": "Cliente",
+ "mwoauthmanageconsumers-user": "Editore",
+ "mwoauthmanageconsumers-description": "Descrizione",
+ "mwoauthmanageconsumers-email": "Email di contatto",
+ "mwoauthmanageconsumers-consumerkey": "Chiave cliente",
+ "mwoauthmanageconsumers-lastchange": "Ultima modifica",
+ "mwoauthmanageconsumers-review": "rivedi/gestisci",
+ "mwoauthmanageconsumers-confirm-text": "Usa questo modulo per approvare, respingere, disabilitare o riabilitare questo cliente.",
+ "mwoauthmanageconsumers-confirm-legend": "Gestione cliente OAuth",
+ "mwoauthmanageconsumers-action": "Modifica stato:",
+ "mwoauthmanageconsumers-approve": "Approvato",
+ "mwoauthmanageconsumers-reject": "Respinto",
+ "mwoauthmanageconsumers-rsuppress": "Respinto e soppresso",
+ "mwoauthmanageconsumers-disable": "Disabilitato",
+ "mwoauthmanageconsumers-dsuppress": "Disabilitato e soppresso",
+ "mwoauthmanageconsumers-reenable": "Approvato",
+ "mwoauthmanageconsumers-reason": "Motivo:",
+ "mwoauthmanageconsumers-confirm-submit": "Aggiorna stato cliente",
+ "mwoauthmanageconsumers-success-approved": "La richiesta è stata approvata.",
+ "mwoauthmanageconsumers-success-rejected": "La richiesta è stata respinta.",
+ "mwoauthmanageconsumers-success-disabled": "Il cliente è stato disabilitato.",
+ "mwoauthmanageconsumers-success-reanable": "Il cliente è stato riabilitato.",
+ "mwoauthmanageconsumers-search-name": "clienti con questo nome",
+ "mwoauthmanageconsumers-search-publisher": "clienti di questo utente",
+ "oauthlistconsumers": "Elenco delle applicazioni OAuth",
+ "mwoauthlistconsumers-legend": "Naviga applicazioni OAuth",
+ "mwoauthlistconsumers-view": "dettagli",
+ "mwoauthlistconsumers-none": "Nessuna applicazione trovata che soddisfa questo criterio.",
+ "mwoauthlistconsumers-name": "Nome applicazione",
+ "mwoauthlistconsumers-version": "Versione cliente",
+ "mwoauthlistconsumers-user": "Editore",
+ "mwoauthlistconsumers-description": "Descrizione",
+ "mwoauthlistconsumers-wiki": "Progetti applicabili",
+ "mwoauthlistconsumers-callbackurl": "URL di \"callback\" OAuth",
+ "mwoauthlistconsumers-grants": "Assegnazioni applicabili",
+ "mwoauthlistconsumers-basicgrantsonly": "(solo accesso di base)",
+ "mwoauthlistconsumers-status": "Stato",
+ "mwoauth-consumer-stage-any": "qualsiasi",
+ "mwoauthlistconsumers-status-proposed": "proposto",
+ "mwoauthlistconsumers-status-approved": "approvato",
+ "mwoauthlistconsumers-status-disabled": "disabilitato",
+ "mwoauthlistconsumers-status-rejected": "respinto",
+ "mwoauthlistconsumers-status-expired": "scaduto",
+ "oauthmanagemygrants": "Gestione applicazioni connesse",
+ "mwoauthmanagemygrants-text": "Questa pagina elenca tutte le applicazioni che possono utilizzare la tua utenza. Per tali applicazioni, l'ambito del loro accesso è limitata dalle autorizzazioni concesse all'applicazione quando è stata autorizzata ad agire per vostro conto. Se autorizzi separatamente un'applicazione all'accesso per vostro conto su diversi progetti \"fratelli\", poi vedrai configurazioni separate per ognuno dei progetti sotto.\n\nLe applicazioni connesse accedono alla tua utenza usando il protocollo OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Ulteriori informazioni sulle applicazioni connesse])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigazione:",
+ "mwoauthmanagemygrants-showlist": "Elenco applicazioni connesse",
+ "mwoauthmanagemygrants-none": "Non ci sono applicazioni collegate alla tua utenza.",
+ "mwoauthmanagemygrants-user": "Editore:",
+ "mwoauthmanagemygrants-description": "Descrizione",
+ "mwoauthmanagemygrants-wikiallowed": "Consentito su progetto:",
+ "mwoauthmanagemygrants-grants": "Assegnazioni applicabili",
+ "mwoauthmanagemygrants-grantsallowed": "Diritti consentiti",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Assegnazioni applicabili consentite:",
+ "mwoauthmanagemygrants-review": "gestisci accesso",
+ "mwoauthmanagemygrants-revoke": "revoca l'accesso",
+ "mwoauthmanagemygrants-grantaccept": "Assegnazioni",
+ "mwoauthmanagemygrants-update-text": "Utilizza il seguente modulo per modificare le autorizzazioni concesse a un'applicazione di agire per vostro conto.",
+ "mwoauthmanagemygrants-revoke-text": "Utilizza il seguente modulo per revocare le autorizzazioni concesse a un'applicazione di agire per vostro conto.",
+ "mwoauthmanagemygrants-confirm-legend": "Gestione applicazione connessa",
+ "mwoauthmanagemygrants-update": "Aggiorna le assegnazioni",
+ "mwoauthmanagemygrants-renounce": "Rimuovi l'autorizzazione",
+ "mwoauthmanagemygrants-action": "Modifica stato:",
+ "mwoauthmanagemygrants-confirm-submit": "Aggiorna lo stato del token di accesso",
+ "mwoauthmanagemygrants-success-update": "Le tue preferenze per questa applicazione sono state aggiornate.",
+ "mwoauthmanagemygrants-success-renounce": "L'accesso dell'applicazione alla tua utenza è stato revocato.",
+ "mwoauthmanagemygrants-basic-tooltip": "Perché non posso aggiornare questa assegnazione? Questa concessione offre le autorizzazioni di base per l'applicazione connessa che sono richieste per funzionare correttamente. Se non si desidera che questa applicazione connessa abbia questi diritti, si dovrebbe revocare l'accesso all'applicazione.",
+ "mwoauthmanagemygrants-editslink": "Le {{GENDER:$1|tue}} modifiche con questa applicazione",
+ "mwoauthmanagemygrants-actionslink": "Le {{GENDER:$1|tue}} azioni con questa applicazione",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|ha proposto}} un cliente OAuth (chiave cliente $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|ha aggiornato}} un cliente OAuth (chiave cliente $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|ha approvato}} un cliente OAuth di $3 (chiave cliente $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|ha respinto}} un cliente OAuth di $3 (chiave cliente $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|ha disabilitato}} un cliente OAuth di $3 (chiave cliente $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|ha riabilitato}} un cliente OAuth di $3 (chiave cliente $4)",
+ "mwoauthconsumer-consumer-logpage": "Clienti OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Registro dei clienti OAuth approvati, respinti o disabilitati.",
+ "mwoauth-bad-request-missing-params": "Spiacenti, qualcosa è andato storto nella configurazione della connessione per questa applicazione. <span class=\"plainlinks\">[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Contatta il supporto]</span> per avere aiuto su come correggere.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth parametri mancanti, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Spiacenti, qualcosa è andato storto, dovresti contattare l'autore dell'applicazione per avere aiuto su come correggere.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL sconosciuto, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Spiacenti, qualcosa è andato storto, dovresti [$1 contattare] l'autore dell'applicazione per avere aiuto su come correggere.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL sconosciuto, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Non è stata trovata alcuna assegnazione approvata per il token di autorizzazione.",
+ "mwoauthdatastore-request-token-not-found": "Spiacenti, qualcosa è andato storto, durante la connessione a questa applicazione. Torna indietro e prova a connetterti nuovamente, o contattare l'autore dell'applicazione.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Token OAuth non trovato, $1</span>",
+ "mwoauthdatastore-bad-token": "Non è stato trovato alcun token che corrisponde alla tua richiesta.",
+ "mwoauthdatastore-bad-source-ip": "La richiesta proviene da un indirizzo IP non valido.",
+ "mwoauthdatastore-bad-verifier": "Il codice di verifica fornito non è valido.",
+ "mwoauthdatastore-invalid-token-type": "Il tipo di token richiesto non è valido.",
+ "mwoauthgrants-general-error": "Si è verificato un errore nella tua richiesta OAuth.",
+ "mwoauthserver-bad-consumer": "\"$1\" non è un'applicazione approvata. [$2 Contatta] l'autore dell'applicazione per avere aiuto.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Applicazione non approvata, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Spiacenti, qualcosa è andato storto, durante la connessione a questa applicazione.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Chiave OAuth sconosciuta, $1</span>",
+ "mwoauthserver-insufficient-rights": "Non è consentito alla tua utenza di usare le applicazioni, contatta l'amministratore del sito per capirne il motivo.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Diritti utente OAuth insufficienti, $1</span>",
+ "mwoauthserver-invalid-request-token": "Token non valido nella tua richiesta.",
+ "mwoauthserver-invalid-user": "Per usare le applicazioni connesse su questo sito, è obbligatoria un'utenza unificata fra tutti i progetti. Una volta che avrai l'utenza su tutti i progetti, prova a connetterti a \"$1\" nuovamente.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Necessaria l'utenza globale, $2</span>",
+ "mwoauth-invalid-authorization-title": "Errore autorizzazione OAuth",
+ "mwoauth-invalid-authorization": "L'intestazione dell'autorizzazione nella tua richiesta non è valida: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "L'intestazione dell'autorizzazione nella tua richiesta non è valida per $1",
+ "mwoauth-invalid-authorization-invalid-user": "L'intestazione dell'autorizzazione nella tua richiesta si riferisce ad un utente che non esiste qui",
+ "mwoauth-invalid-authorization-wrong-user": "L'intestazione dell'autorizzazione nella tua richiesta si riferisce ad un altro utente",
+ "mwoauth-invalid-authorization-not-approved": "L'applicazione a cui stai tentando di connetterti sembra essere impostata in modo errato. Contatta l'autore di \"$1\" per avere aiuto.",
+ "mwoauth-invalid-authorization-blocked-user": "L'intestazione dell'autorizzazione nella tua richiesta si riferisce ad un utente che è bloccato",
+ "mwoauth-form-description-allwikis": "Ciao $1,\n\nPer completare la tua richiesta, '''$2''' necessita dell'autorizzazione per eseguire le seguenti azioni per tuo conto su tutti i progetti di questo sito:\n\n$4",
+ "mwoauth-form-description-onewiki": "Ciao $1,\n\nPer completare la tua richiesta, '''$2''' necessita dell'autorizzazione per eseguire le seguenti azioni per tuo conto su ''$4'':\n\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Ciao $1,\n\nPer completare la tua richiesta, '''$2''' necessita dell'autorizzazione per accedere alle informazioni per tuo conto su tutti i progetti di questo sito. Nessuna modifica sarà effettuata con la tua utenza.",
+ "mwoauth-form-description-onewiki-nogrants": "Ciao $1,\n\nPer completare la tua richiesta, '''$2''' necessita dell'autorizzazione per accedere alle informazioni per tuo conto su ''$4''. Nessuna modifica sarà effettuata con la tua utenza.",
+ "mwoauth-form-button-approve": "Consenti",
+ "mwoauth-form-button-cancel": "Annulla",
+ "mwoauth-error": "Errore di connessione dell'applicazione",
+ "mwoauth-grants-heading": "Autorizzazioni richieste:",
+ "mwoauth-grants-nogrants": "L'applicazione non ha richiesto alcuna autorizzazione.",
+ "mwoauth-acceptance-cancelled": "Hai scelto di non consentire a \"$1\" ad accedere alla tua utenza. \"$1\" non funzionerà a meno che non gli permetti l'accesso. Puoi tornare a \"$1\" o nella [[Special:OAuthManageMyGrants|gestione]] delle tue applicazioni connesse.",
+ "grant-mwoauth-authonly": "Solo verifica identità utente, nessuna capacità di leggere pagine o agire per conto di un utente.",
+ "grant-mwoauth-authonlyprivate": "Verifica identità utente solo con accesso al nome reale e indirizzo email, nessuna capacità di leggere pagine o agire per conto di un utente.",
+ "mwoauth-listgrants-extra-summary": "== Assegnazioni specifiche OAuth ==\n\nQueste assegnazioni supplementari sono applicabili ai clienti OAuth.",
+ "mwoauth-oauth-exception": "Si è verificato un errore nel protocollo OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback deve essere impostato a \"oob\" (in minuscolo)",
+ "right-mwoauthproposeconsumer": "Propone nuovi clienti OAuth",
+ "right-mwoauthupdateownconsumer": "Aggiorna clienti OAuth che controlla",
+ "right-mwoauthmanageconsumer": "Gestisce clienti OAuth",
+ "right-mwoauthsuppress": "Sopprime clienti OAuth",
+ "right-mwoauthviewsuppressed": "Visualizza clienti OAuth soppressi",
+ "right-mwoauthviewprivate": "Visualizza dati privati OAuth",
+ "right-mwoauthmanagemygrants": "Gestisce assegnazioni OAuth",
+ "action-mwoauthmanageconsumer": "gestire clienti OAuth",
+ "action-mwoauthmanagemygrants": "gestire le tue assegnazioni OAuth",
+ "action-mwoauthproposeconsumer": "proporre nuovi clienti OAuth",
+ "action-mwoauthupdateownconsumer": "aggiornare clienti OAuth che controlli",
+ "action-mwoauthviewsuppressed": "visualizzare clienti OAuth soppressi",
+ "mwoauth-tag-reserved": "Etichette che iniziano con <code>OAuth CID:</code> sono riservate all'utilizzo da parte di OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Nota:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> è più sicuro delle password bot e dovrebbe essere preferito ogni volta che il bot lo supporta.",
+ "mwoauth-api-module-disabled": "Il modulo \"$1\" non è disponibile con OAuth.",
+ "echo-category-title-oauth-owner": "Sviluppo OAuth",
+ "echo-pref-tooltip-oauth-owner": "Avvisami su eventi relativi alle applicazioni OAuth che ho creato.",
+ "echo-category-title-oauth-admin": "Amministrazione OAuth",
+ "echo-pref-tooltip-oauth-admin": "Avvisami su eventi relativi alla revisione di applicazioni OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|ha proposto}} una nuova applicazione OAuth: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|ha aggiornato}} l'applicazione OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|ha approvato}} {{GENDER:$3|la tua}} applicazione OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|ha respinto}} {{GENDER:$3|la tua}} applicazione OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|ha disabilitato}} {{GENDER:$3|la tua}} applicazione OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|ha riabilitato}} {{GENDER:$3|la tua}} applicazione OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|ha proposto}} una nuova applicazione OAuth su {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|ha aggiornato}} l'applicazione OAuth su {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|ha approvato}} {{GENDER:$3|la tua}} applicazione OAuth su {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|ha respinto}} {{GENDER:$3|la tua}} applicazione OAuth su {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|ha disabilitato}} {{GENDER:$3|la tua}} applicazione OAuth su {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|ha riabilitato}} {{GENDER:$3|la tua}} applicazione OAuth su {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Revisiona applicazione",
+ "notification-oauth-app-update-primary-link": "Revisiona applicazione",
+ "notification-oauth-app-approve-primary-link": "Visualizza applicazione",
+ "notification-oauth-app-reject-primary-link": "Visualizza applicazione",
+ "notification-oauth-app-disable-primary-link": "Visualizza applicazione",
+ "notification-oauth-app-reenable-primary-link": "Visualizza applicazione",
+ "notification-oauth-app-body": "Motivo: $1",
+ "mwoauth-oauth-version": "Versione protocollo OAuth"
+}
diff --git a/OAuth/i18n/ja.json b/OAuth/i18n/ja.json
new file mode 100644
index 00000000..45d0c784
--- /dev/null
+++ b/OAuth/i18n/ja.json
@@ -0,0 +1,230 @@
+{
+ "@metadata": {
+ "authors": [
+ "Buntschann",
+ "Fryed-peach",
+ "Kkairri",
+ "Macofe",
+ "Omotecho",
+ "Otokoume",
+ "Shirayuki",
+ "Sujiniku",
+ "Whym",
+ "Yusuke1109"
+ ]
+ },
+ "oauth": "OAuth",
+ "mwoauth-desc": "API 認証に OAuth 1.0a および OAuth 2.0 を使用できるようにする",
+ "mwoauth-verified": "このアプリケーションがあなたに代わって MediaWiki にアクセスすることが許可されました。\n\nこの手続きを完了するには次の検証トークンをアプリケーションに提供してください: '''$1'''",
+ "mwoauth-db-readonly": "OAuth データベースは一時的にロックされています。しばらくしてからもう一度お試しください。",
+ "mwoauth-missing-field": "「$1」フィールドの値がありません",
+ "mwoauth-invalid-field": "「$1」フィールドに指定した値は無効です",
+ "mwoauth-invalid-field-generic": "指定した値は無効です",
+ "mwoauth-field-hidden": "(この情報は非表示です)",
+ "mwoauth-field-private": "(この情報は非公開です)",
+ "mwoauth-prefs-managegrants": "接続済みのアプリ:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|$1 件の接続済みアプリケーション|0=接続済みアプリケーション}}を管理",
+ "mwoauth-consumer-allwikis": "このサイト上のすべてのプロジェクト",
+ "mwoauth-consumer-key": "コンシューマー キー:",
+ "mwoauth-consumer-name": "アプリケーション名:",
+ "mwoauth-consumer-version": "コンシューマーのバージョン:",
+ "mwoauth-consumer-user": "発行者:",
+ "mwoauth-consumer-stage": "現在の状態:",
+ "mwoauth-consumer-email": "連絡先メールアドレス:",
+ "mwoauth-consumer-owner-only-label": "オーナー専用:",
+ "mwoauth-consumer-owner-only": "このコンシューマーは$1でのみ使用されます。",
+ "mwoauth-consumer-owner-only-help": "このオプションを選択すると、コンシューマーは自動的に承認され、$1で利用できるようになります。それは他のユーザーによって使用できなくなり、通常の許可フローは機能しません。このコンシューマーを使用して実行されたアクションはタグ付けされません。",
+ "mwoauth-consumer-description": "アプリケーションの説明:",
+ "mwoauth-consumer-callbackurl": "OAuth コールバック URL:",
+ "mwoauth-consumer-granttypes": "申請中の助成金:",
+ "mwoauth-consumer-wiki-thiswiki": "現在のプロジェクト ($1)",
+ "mwoauth-consumer-restrictions": "使用制限:",
+ "mwoauth-consumer-restrictions-json": "使用制限 (JSON):",
+ "mwoauth-consumer-rsakey": "公開 RSA キー(オプション):",
+ "mwoauth-consumer-secretkey": "コンシューマー秘密トークン:",
+ "mwoauth-consumer-accesstoken": "アクセス トークン:",
+ "mwoauth-consumer-reason": "理由:",
+ "mwoauth-consumer-email-unconfirmed": "アカウントのメールアドレスがまだ確認されていません。",
+ "mwoauth-consumer-email-mismatched": "指定したメールアドレスは、アカウントのものと一致しません。",
+ "mwoauth-consumer-alreadyexists": "この名前/バージョン/発行者の組み合わせを持つコンシューマーは既に存在します",
+ "mwoauth-consumer-not-proposed": "このコンシューマーは現在提案されていません。",
+ "mwoauth-consumer-not-disabled": "このコンシューマーは現在無効化されていません",
+ "mwoauth-consumer-not-approved": "このコンシューマーは承認されていません (無効化された可能性があります)。",
+ "mwoauth-missing-consumer-key": "コンシューマー キーを指定していません。",
+ "mwoauth-invalid-consumer-key": "指定したキーのコンシューマーは存在しません。",
+ "mwoauth-invalid-access-token": "指定したキーのアクセス トークンは存在しません。",
+ "mwoauth-invalid-access-wrongwiki": "プロジェクト「$1」のみで使用できるコンシューマーです。",
+ "mwoauth-consumer-grantshelp": "それぞれの助成金は、特定の利用者アカウントが既に有している権限の一覧を付与します。詳しくは助成金一覧[[Special:ListGrants|table of grants]] をご覧ください。",
+ "mwoauth-consumer-stage-proposed": "提案中",
+ "mwoauth-consumer-stage-rejected": "却下済み",
+ "mwoauth-consumer-stage-expired": "期限切れ",
+ "mwoauth-consumer-stage-approved": "承認済",
+ "mwoauth-consumer-stage-disabled": "無効",
+ "oauthconsumerregistration": "OAuth コンシューマー登録",
+ "mwoauthconsumerregistration-navigation": "ナビゲーション:",
+ "mwoauthconsumerregistration-propose": "新しいコンシューマーの提案",
+ "mwoauthconsumerregistration-list": "自分のコンシューマー一覧",
+ "mwoauthconsumerregistration-main": "メイン",
+ "mwoauthconsumerregistration-update-legend": "OAuth コンシューマー アプリケーションの更新",
+ "mwoauthconsumerregistration-propose-submit": "コンシューマーの提案",
+ "mwoauthconsumerregistration-update-submit": "コンシューマーを更新",
+ "mwoauthconsumerregistration-none": "あなたが制御できる Oauth コンシューマーはありません。",
+ "mwoauthconsumerregistration-name": "コンシューマー",
+ "mwoauthconsumerregistration-user": "発行者",
+ "mwoauthconsumerregistration-description": "説明",
+ "mwoauthconsumerregistration-email": "連絡先メールアドレス",
+ "mwoauthconsumerregistration-consumerkey": "コンシューマー キー",
+ "mwoauthconsumerregistration-stage": "状態",
+ "mwoauthconsumerregistration-lastchange": "最新の変更",
+ "mwoauthconsumerregistration-manage": "管理",
+ "mwoauthconsumerregistration-created-owner-only": "OAuth コンシューマーが作成されました。\n\nトークンは以下のとおりです:\n; コンシューマートークン: $1\n; コンシューマーシークレット: $2\n; アクセストークン: $3\n; アクセスシークレット: $4\n<em>今後のためにこれらの情報を控えておいてください。</em>",
+ "mwoauthconsumerregistration-updated": "あなたの OAuth コンシューマー レジストリを更新しました。",
+ "mwoauthconsumerregistration-secretreset": "「'''$1'''」のコンシューマー秘密トークンを割り当てました。''今後のためこれを記録しておいてください。''",
+ "oauthmanageconsumers": "OAuthコンシューマー管理",
+ "mwoauthmanageconsumers-notloggedin": "このページにアクセスするにはログインしてください。",
+ "mwoauthmanageconsumers-type": "キュー:",
+ "mwoauthmanageconsumers-showproposed": "提案された要求",
+ "mwoauthmanageconsumers-showrejected": "拒否されたリクエスト",
+ "mwoauthmanageconsumers-showexpired": "期限切れのリクエスト",
+ "mwoauthmanageconsumers-main": "メイン",
+ "mwoauthmanageconsumers-queues": "以下からコンシューマー確認のキューを選択:",
+ "mwoauthmanageconsumers-q-proposed": "提案中のコンシューマー依頼のキュー",
+ "mwoauthmanageconsumers-q-rejected": "却下済みのコンシューマー依頼のキュー",
+ "mwoauthmanageconsumers-q-expired": "期限切れのコンシューマー依頼のキュー",
+ "mwoauthmanageconsumers-lists": "以下からコンシューマーの状態の一覧を選択:",
+ "mwoauthmanageconsumers-l-approved": "承認済みのコンシューマーの一覧",
+ "mwoauthmanageconsumers-l-disabled": "無効化されたコンシューマーの一覧",
+ "mwoauthmanageconsumers-none-approved": "この条件に該当するコンシューマーはありません。",
+ "mwoauthmanageconsumers-none-disabled": "この条件に該当するコンシューマーはありません。",
+ "mwoauthmanageconsumers-name": "コンシューマー",
+ "mwoauthmanageconsumers-user": "発行者",
+ "mwoauthmanageconsumers-description": "説明",
+ "mwoauthmanageconsumers-email": "連絡先メール",
+ "mwoauthmanageconsumers-consumerkey": "コンシューマー キー",
+ "mwoauthmanageconsumers-lastchange": "最新の変更",
+ "mwoauthmanageconsumers-review": "査読/管理",
+ "mwoauthmanageconsumers-confirm-text": "このフォームでは、このコンシューマーを承認、却下、無効化、再有効化できます。",
+ "mwoauthmanageconsumers-confirm-legend": "OAuth コンシューマーの管理",
+ "mwoauthmanageconsumers-action": "状態の変更:",
+ "mwoauthmanageconsumers-approve": "承認済",
+ "mwoauthmanageconsumers-reject": "却下済み",
+ "mwoauthmanageconsumers-disable": "無効",
+ "mwoauthmanageconsumers-reenable": "承認済",
+ "mwoauthmanageconsumers-reason": "理由:",
+ "mwoauthmanageconsumers-confirm-submit": "コンシューマーの状態を更新",
+ "mwoauthmanageconsumers-success-approved": "リクエストを承認しました。",
+ "mwoauthmanageconsumers-success-rejected": "リクエストを却下しました。",
+ "mwoauthmanageconsumers-success-disabled": "コンシューマーを無効にしました。",
+ "mwoauthmanageconsumers-success-reanable": "コンシューマーを再度有効にしました。",
+ "mwoauthmanageconsumers-search-name": "この名前のコンシューマー",
+ "mwoauthmanageconsumers-search-publisher": "この利用者のコンシューマー",
+ "oauthlistconsumers": "OAuthアプリケーション一覧",
+ "mwoauthlistconsumers-legend": "OAuth アプリケーションの参照",
+ "mwoauthlistconsumers-view": "詳細",
+ "mwoauthlistconsumers-none": "この条件に該当するアプリケーションが見つかりません。",
+ "mwoauthlistconsumers-name": "アプリケーション名",
+ "mwoauthlistconsumers-version": "コンシューマーのバージョン",
+ "mwoauthlistconsumers-user": "発行者",
+ "mwoauthlistconsumers-description": "説明",
+ "mwoauthlistconsumers-callbackurl": "OAuth コールバック URL",
+ "mwoauthlistconsumers-grants": "該当する権限群",
+ "mwoauthlistconsumers-basicgrantsonly": "(基本的なアクセスのみ)",
+ "mwoauthlistconsumers-status": "状態",
+ "mwoauth-consumer-stage-any": "すべて",
+ "mwoauthlistconsumers-status-approved": "承認済",
+ "mwoauthlistconsumers-status-disabled": "無効",
+ "mwoauthlistconsumers-status-rejected": "却下",
+ "mwoauthlistconsumers-status-expired": "期限切れ",
+ "mwoauthlistconsumers-rclink": "このアプリケーションによる最近の変更",
+ "oauthmanagemygrants": "接続済みアプリケーションの管理",
+ "mwoauthmanagemygrants-text": "このページでは、あなたのアカウントを使用できるアプリケーションをすべて列挙します。各アプリケーションについて、あなたの代わりに実行することを承認した際に許可した範囲に、そのアプリケーションの権限が制限されています。If you separately authorized an application to access different sister projects on your behalf, then you will see separate configuration for each such project below.\n\n接続されたアプリケーションは、OAuth プロトコル を使って、あなたのアカウントにアクセスします。<span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth 接続されたアプリケーションについて詳しく知る])</span>",
+ "mwoauthmanagemygrants-navigation": "ナビゲーション:",
+ "mwoauthmanagemygrants-showlist": "接続済みアプリケーション一覧",
+ "mwoauthmanagemygrants-none": "あなたのアカウントに接続されているアプリケーションはありません。",
+ "mwoauthmanagemygrants-user": "発行者:",
+ "mwoauthmanagemygrants-description": "説明",
+ "mwoauthmanagemygrants-wikiallowed": "許可されているプロジェクト:",
+ "mwoauthmanagemygrants-grants": "該当する権限群",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "該当し応募できる権限群:",
+ "mwoauthmanagemygrants-review": "アクセスを管理",
+ "mwoauthmanagemygrants-revoke": "アクセスを取り消す",
+ "mwoauthmanagemygrants-grantaccept": "付与",
+ "mwoauthmanagemygrants-update-text": "以下の書式を使い、あなたの代理として操作するアプリケーションに付与する権限を変更します。",
+ "mwoauthmanagemygrants-confirm-legend": "接続済みアプリケーションの管理",
+ "mwoauthmanagemygrants-update": "権限付与を更新",
+ "mwoauthmanagemygrants-renounce": "認証解除",
+ "mwoauthmanagemygrants-action": "状態の変更:",
+ "mwoauthmanagemygrants-confirm-submit": "アクセス トークンの状態を更新",
+ "mwoauthmanagemygrants-success-update": "このアプリケーションの設定が更新されました。",
+ "mwoauthmanagemygrants-success-renounce": "あなたのアカウントへのアプリケーションのアクセスは取り消されました。",
+ "mwoauthmanagemygrants-basic-tooltip": "この権限の付与の変更ができない理由は? この権限の付与では、あなたが接続するアプリケーションの正常な動作に必要な基礎的権限を与えます。それら権限を与えたくない場合には、あなた自身でこのアプリケーションのアクセス権を無効にしなければなりません。",
+ "mwoauthmanagemygrants-editslink": "このアプリケーションによる{{GENDER:$1|あなたの}}編集",
+ "mwoauthmanagemygrants-actionslink": "このアプリケーションによる{{GENDER:$1|あなたの}}操作",
+ "logentry-mwoauthconsumer-propose": "$1 が OAuth コンシューマーを{{GENDER:$2|提案}} (コンシューマー キー $4)",
+ "logentry-mwoauthconsumer-update": "$1 が OAuth コンシューマーを{{GENDER:$2|更新}} (コンシューマー キー $4)",
+ "logentry-mwoauthconsumer-approve": "$1 が $3 による OAuth コンシューマーを{{GENDER:$2|承認}} (コンシューマー キー $4)",
+ "logentry-mwoauthconsumer-reject": "$1 が $3 による OAuth コンシューマーを{{GENDER:$2|却下}} (コンシューマー キー $4)",
+ "logentry-mwoauthconsumer-disable": "$1 が $3 による OAuth コンシューマーを{{GENDER:$2|無効化}} (コンシューマー キー $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 が $3 による OAuth コンシューマーを{{GENDER:$2|再有効化}} (コンシューマー キー $4)",
+ "mwoauthconsumer-consumer-logpage": "OAuth コンシューマー記録",
+ "mwoauthdatastore-access-token-not-found": "その認証トークンに対して承認された助成金は見つかりませんでした。",
+ "mwoauthdatastore-bad-token": "該当するトークンは見つかりませんでした。",
+ "mwoauthdatastore-bad-verifier": "指定した認証コードは無効でした。",
+ "mwoauthgrants-general-error": "OAuth リクエストでエラーが発生しました。",
+ "mwoauthserver-insufficient-rights": "あなたには Connected Apps を実行する許可がありません。理由を見つけるには、あなたのサイトの管理者に連絡してください。\n\n<span class=\"plainlinks mw-mwoautherror-details\">Insufficient OAuth user rights, $1</span>",
+ "mwoauthserver-invalid-request-token": "リクエストに無効なトークンがあります。",
+ "mwoauth-invalid-authorization-title": "OAuth 認証エラー",
+ "mwoauth-form-description-allwikis": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''はこのサイトのすべてのプロジェクトで以下のアクションを実行するための許可が必要です:\n\n$4",
+ "mwoauth-form-description-onewiki": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''はあなたに代わって '''$4'''の次のアクションを実行するための許可を必要とします:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''はこのサイトのすべてのプロジェクトに関する情報にアクセスするための許可を必要とします。 あなたのアカウントは変更されません。",
+ "mwoauth-form-description-onewiki-nogrants": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''は'''$4'''の情報にアクセスする許可を必要とします。あなたのアカウントは変更されません。",
+ "mwoauth-form-description-allwikis-privateinfo": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''は、このサイトのすべてのプロジェクトであなたの本名やメールアドレスを含むあなたに関する情報にアクセスする許可を必要とします。 あなたのアカウントは変更されません。",
+ "mwoauth-form-description-onewiki-privateinfo": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''は、あなたの本名とメールアドレスを含む情報 ''$4''にアクセスするための許可が必要です。 あなたのアカウントは変更されません。",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''はこのサイトのすべてのプロジェクトに関するあなたの電子メールアドレスを含む情報にアクセスするための許可を必要とします。 あなたのアカウントは変更されません。",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "$1 さんこんにちは、\n\nリクエストを完了するために、 '''$2'''は''$4''に関するあなたの電子メールアドレスを含む情報にアクセスするための許可を必要とします。 あなたのアカウントは変更されません。",
+ "mwoauth-form-button-approve": "許可",
+ "mwoauth-form-button-cancel": "キャンセル",
+ "mwoauth-error": "アプリケーション接続エラー",
+ "mwoauth-grants-heading": "要求されたアクセス許可:",
+ "mwoauth-grants-nogrants": "アプリケーションはアクセス権を要求していません。",
+ "mwoauth-listgrants-extra-summary": "== OAuth特定の助成金 ==\n\n以下のその他の助成金も OAuth 利用者に適用されます。",
+ "mwoauth-oauth-exception": "OAuth プロトコルでエラーが発生しました: $1",
+ "mwoauth-callback-not-oob": "oauth_callback に「oob」を設定してください (大文字小文字を区別)",
+ "right-mwoauthproposeconsumer": "新しいコンシューマーを提案",
+ "right-mwoauthupdateownconsumer": "自身が制御できるOAuthコンシューマーを更新",
+ "right-mwoauthmanageconsumer": "OAuthコンシューマーを管理",
+ "right-mwoauthsuppress": "OAuthコンシューマーを秘匿",
+ "right-mwoauthviewsuppressed": "秘匿されたOAuthコンシューマーを閲覧",
+ "right-mwoauthviewprivate": "非公開OAuthデータを閲覧",
+ "right-mwoauthmanagemygrants": "OAuth付与を管理",
+ "action-mwoauthmanageconsumer": "OAuthコンシューマーの管理",
+ "action-mwoauthmanagemygrants": "OAuth付与の管理",
+ "action-mwoauthproposeconsumer": "新しいコンシューマーの提案",
+ "action-mwoauthupdateownconsumer": "自分が制御できるOAuthコンシューマーの更新",
+ "action-mwoauthviewsuppressed": "秘匿されたOAuthコンシューマーの閲覧",
+ "mwoauth-api-module-disabled": "\"$1\"モジュールはOAuthで利用できません。",
+ "echo-category-title-oauth-owner": "OAuthの開発",
+ "echo-pref-tooltip-oauth-owner": "自身が作成した OAuth アプリケーションについて動きがあったときに通知する。",
+ "echo-category-title-oauth-admin": "OAuth管理者",
+ "echo-pref-tooltip-oauth-admin": "OAuth アプリケーションの再検討について動きがあったときに通知する。",
+ "notification-oauth-app-propose-title": "$1 が 新しい OAuth アプリを{{GENDER:$1|提案しました}}: $2",
+ "notification-oauth-app-update-title": "$1 が OAuth アプリ $2 を{{GENDER:$1|更新しました}}",
+ "notification-oauth-app-approve-title": "$1 が{{GENDER:$3|あなたの}} OAuth アプリ ($2) を{{GENDER:$1|承認しました}}",
+ "notification-oauth-app-reject-title": "$1 が{{GENDER:$3|あなたの}} OAuth アプリ ($2) を{{GENDER:$1|拒否しました}}",
+ "notification-oauth-app-disable-title": "$1 が{{GENDER:$3|あなたの}} OAuth アプリ ($2) を{{GENDER:$1|無効化しました}}",
+ "notification-oauth-app-reenable-title": "$1 が{{GENDER:$3|あなたの}} OAuth アプリ ($2) を{{GENDER:$1|再有効化しました}}",
+ "notification-oauth-app-propose-subject": "$1 が {{SITENAME}} 上の新しい OAuth アプリを{{GENDER:$1|提案しました}}",
+ "notification-oauth-app-update-subject": "$1 が {{SITENAME}} 上の OAuth アプリを{{GENDER:$1|更新しました}}",
+ "notification-oauth-app-approve-subject": "$1 が {{SITENAME}} 上の{{GENDER:$3|あなたの}} OAuth アプリを{{GENDER:$1|承認しました}}",
+ "notification-oauth-app-reject-subject": "$1 が {{SITENAME}} 上の{{GENDER:$3|あなたの}} OAuth アプリを{{GENDER:$1|拒否しました}}",
+ "notification-oauth-app-disable-subject": "$1 が {{SITENAME}} 上の{{GENDER:$3|あなたの}} OAuth アプリを{{GENDER:$1|無効化しました}}",
+ "notification-oauth-app-reenable-subject": "$1 が {{SITENAME}} 上で{{GENDER:$3|あなたの}} OAuth アプリを{{GENDER:$1|再有効化しました}}",
+ "notification-oauth-app-propose-primary-link": "アプリの査読",
+ "notification-oauth-app-approve-primary-link": "アプリを表示",
+ "notification-oauth-app-reject-primary-link": "アプリを表示",
+ "notification-oauth-app-disable-primary-link": "アプリを表示",
+ "notification-oauth-app-reenable-primary-link": "アプリを表示",
+ "notification-oauth-app-body": "理由: $1",
+ "mwoauth-oauth2-granttype-refresh-token": "トークンの更新",
+ "mwoauth-oauth2-invalid-access-token": "無効なアクセストークン"
+}
diff --git a/OAuth/i18n/jv.json b/OAuth/i18n/jv.json
new file mode 100644
index 00000000..c3d2c71b
--- /dev/null
+++ b/OAuth/i18n/jv.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "NoiX180"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplikasi-aplikasi kang ginayut:",
+ "mwoauth-prefs-managegrantslink": "Tata $1 {{PLURAL:$1|aplikasi|aplikasi}} kang ginayut",
+ "oauthmanagemygrants": "Tata aplikasi-aplikasi kang ginayut",
+ "mwoauthmanagemygrants-confirm-legend": "Tata aplikasi kang ginayut"
+}
diff --git a/OAuth/i18n/ka.json b/OAuth/i18n/ka.json
new file mode 100644
index 00000000..aff31a73
--- /dev/null
+++ b/OAuth/i18n/ka.json
@@ -0,0 +1,89 @@
+{
+ "@metadata": {
+ "authors": [
+ "David1010",
+ "MIKHEIL",
+ "Mehman97",
+ "Otogi"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "დაკავშირებული აპლიკაციები:",
+ "mwoauth-prefs-managegrantslink": "$1 დაკავშირებული {{PLURAL:$1|აპლიკაციის}} მართვა",
+ "mwoauth-consumer-allwikis": "ყველა პროექტი ამ საიტზე",
+ "mwoauth-consumer-name": "აპლიკაციის სახელი:",
+ "mwoauth-consumer-version": "კლიენტის ვერსია:",
+ "mwoauth-consumer-user": "გამომქვეყნებელი:",
+ "mwoauth-consumer-stage": "მიმდინარე სტატუსი:",
+ "mwoauth-consumer-email": "საკონტაქტო ელ.ფოსტა:",
+ "mwoauth-consumer-description": "აპლიკაციის აღწერა:",
+ "mwoauth-consumer-wiki": "შესაფერისი პროექტი:",
+ "mwoauth-consumer-reason": "მიზეზი:",
+ "mwoauth-consumer-stage-proposed": "შეთავაზებული",
+ "mwoauth-consumer-stage-rejected": "უარყოფილია",
+ "mwoauth-consumer-stage-expired": "ვადაგასულია",
+ "mwoauth-consumer-stage-approved": "დამოწმებულია",
+ "mwoauth-consumer-stage-disabled": "გათიშულია",
+ "mwoauth-consumer-stage-suppressed": "აკრძალულია",
+ "mwoauthconsumerregistration-navigation": "ნავიგაცია:",
+ "mwoauthconsumerregistration-main": "მთავარი",
+ "mwoauthconsumerregistration-name": "კლიენტი",
+ "mwoauthconsumerregistration-user": "გამომქვეყნებელი",
+ "mwoauthconsumerregistration-description": "აღწერა",
+ "mwoauthconsumerregistration-email": "საკონტაქტო ელ.ფოსტა",
+ "mwoauthconsumerregistration-consumerkey": "კლიენტის გასაღები",
+ "mwoauthconsumerregistration-stage": "სტატუსი",
+ "mwoauthconsumerregistration-lastchange": "ბოლო ცვლილება",
+ "mwoauthconsumerregistration-manage": "მართვა",
+ "mwoauthmanageconsumers-showrejected": "უარყოფილი თხოვნები",
+ "mwoauthmanageconsumers-showexpired": "ვადაგასული თხოვნები",
+ "mwoauthmanageconsumers-linkproposed": "შეთავაზებული მოთხოვნები",
+ "mwoauthmanageconsumers-linkrejected": "უარყოფილი მოთხოვნები",
+ "mwoauthmanageconsumers-linkapproved": "დადასტურებული მოთხოვნები",
+ "mwoauthmanageconsumers-main": "მთავარი",
+ "mwoauthmanageconsumers-name": "კლიენტი",
+ "mwoauthmanageconsumers-user": "გამომქვეყნებელი",
+ "mwoauthmanageconsumers-description": "აღწერა",
+ "mwoauthmanageconsumers-email": "საკონტაქტო ელ.ფოსტა",
+ "mwoauthmanageconsumers-consumerkey": "კლიენტის გასაღები",
+ "mwoauthmanageconsumers-lastchange": "ბოლო ცვლილება",
+ "mwoauthmanageconsumers-review": "გადახედვა/მართვა",
+ "mwoauthmanageconsumers-approve": "დამოწმებულია",
+ "mwoauthmanageconsumers-reject": "უარყოფილია",
+ "mwoauthmanageconsumers-rsuppress": "უარყოფილი და აკრძალულია",
+ "mwoauthmanageconsumers-disable": "გათიშულია",
+ "mwoauthmanageconsumers-dsuppress": "გათიშული და აკრძალულია",
+ "mwoauthmanageconsumers-reenable": "დამოწმებულია",
+ "mwoauthmanageconsumers-reason": "მიზეზი:",
+ "oauthlistconsumers": "OAuth-აპლიკაციების სია",
+ "mwoauthlistconsumers-legend": "OAuth-აპლიკაციების ძებნა",
+ "mwoauthlistconsumers-view": "დეტალები",
+ "mwoauthlistconsumers-name": "აპლიკაციის სახელი",
+ "mwoauthlistconsumers-version": "კლიენტის ვერსია",
+ "mwoauthlistconsumers-user": "გამომქვეყნებელი",
+ "mwoauthlistconsumers-description": "აღწერა",
+ "mwoauthlistconsumers-wiki": "შესაფერისი პროექტი",
+ "mwoauthlistconsumers-status": "სტატუსი",
+ "mwoauth-consumer-stage-any": "ნებისმიერი",
+ "mwoauthlistconsumers-status-proposed": "შეთავაზებული",
+ "mwoauthlistconsumers-status-approved": "დამტკიცებული",
+ "mwoauthlistconsumers-status-disabled": "გამორთული",
+ "mwoauthlistconsumers-status-rejected": "უარყოფილი",
+ "mwoauthlistconsumers-status-expired": "ვადაგასული",
+ "oauthmanagemygrants": "დაკავშირებული აპლიკაციების მართვა",
+ "mwoauthmanagemygrants-text": "ამ გვერდზე ჩამოთვლილია ყველა აპლიკაცია, რომლებსაც შეუძლიათ თქვენი ანგარიშის გამოყენება. თითოეული ამგვარი აპლიკაციის წვდომის ადგილი შეზღუდულია გარჩევადობით, რომელიც თქვენ მას წარუდგინეთ მოქმედებისათვის თქვენი სახელით. თუ თქვენ წარუდგინეთ აპლიკაციას წვდომა თქვენი ანგარიშისადმი სხვადასხვა დობილ პროექტებში, მაშინ თქვენ ქვემოთ შეგიძლიათ იხილოთ ცალკეული მომართვები თითოეული ამგვარი პროექტისათვის.\n\nაპლიკაციის შეერთება ღებულობს წვდომას თქვენი ანგარიშისადმი OAuth-ის პროტოკოლის მეშვეობით. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth მეტის გაგება ჩართულ აპლიკაციებზე])</span>",
+ "mwoauthmanagemygrants-navigation": "ნავიგაცია:",
+ "mwoauthmanagemygrants-showlist": "დაკავშირებული აპლიკაციების სია",
+ "mwoauthmanagemygrants-none": "თქვენ ანგარიშთან ჯერ არ არის დაკავშირებული აპლიკაციები.",
+ "mwoauthmanagemygrants-user": "გამომქვეყნებელი:",
+ "mwoauthmanagemygrants-description": "აღწერა",
+ "mwoauthmanagemygrants-wikiallowed": "მოწონებულია პროექტისათვის:",
+ "mwoauthmanagemygrants-review": "წვდომის მართვა",
+ "mwoauthmanagemygrants-revoke": "წვდომის გაუქმება",
+ "mwoauthmanagemygrants-confirm-legend": "დაკავშირებული აპლიკაციის მართვა",
+ "mwoauthmanagemygrants-action": "სტატუსის შეცვლა:",
+ "log-action-filter-mwoauthconsumer-reenable": "OAuth სამომხმარებლო რეპუტაცია",
+ "mwoauth-form-button-approve": "ნების დართვა",
+ "mwoauth-form-button-cancel": "გაუქმება",
+ "echo-category-title-oauth-owner": "OAuth-ის განვითარება",
+ "echo-pref-tooltip-oauth-owner": "შემატყობინე OAuth-აპლიკაციებთან დაკავშირებული მოვლენები, რომელიც მე შევქმენი."
+}
diff --git a/OAuth/i18n/kiu.json b/OAuth/i18n/kiu.json
new file mode 100644
index 00000000..6fc23347
--- /dev/null
+++ b/OAuth/i18n/kiu.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mirzali"
+ ]
+ },
+ "mwoauth-form-button-cancel": "Bıtexelne"
+}
diff --git a/OAuth/i18n/kjp.json b/OAuth/i18n/kjp.json
new file mode 100644
index 00000000..569c004b
--- /dev/null
+++ b/OAuth/i18n/kjp.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Rul1902"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "ထိုဝ်ၜုဂ်ထဝေ့ အဲပ်ုလ်ုဖး -",
+ "mwoauth-prefs-managegrantslink": "ထိုဝ်ၜုဂ်ထဝေ့ {{PLURAL:$1|အ်ုပ်ုလေဝ်ကေႋယှေန်ႋ|အ်ုပ်ုလေဝ်ကေႋယှေန်လ်ုဖး}} $1 ဏါင်းဏှ် မ်ုစီရေင့်အင်းတာင်",
+ "mwoauthconsumerregistration-navigation": "ပ်ုယုံ့:",
+ "mwoauthconsumerregistration-description": "ဆ်ုဏဲဖၠဟ်",
+ "mwoauthmanageconsumers-description": "ဆ်ုဏဲဖၠဟ်",
+ "mwoauthmanageconsumers-reason": "ဖှ်ေအ်ုၯာင်ႋအ်ုကျံင်း:",
+ "mwoauthlistconsumers-description": "ဆ်ုဏဲဖၠဟ်",
+ "mwoauthmanagemygrants-navigation": "ပ်ုယုံ့:",
+ "mwoauthmanagemygrants-description": "ဆ်ုဏဲဖၠဟ်"
+}
diff --git a/OAuth/i18n/kk-cyrl.json b/OAuth/i18n/kk-cyrl.json
new file mode 100644
index 00000000..00117425
--- /dev/null
+++ b/OAuth/i18n/kk-cyrl.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arystanbek"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Қосылған қосымшалар:",
+ "mwoauth-prefs-managegrantslink": "Қосылған $1 қосымшаны басқару"
+}
diff --git a/OAuth/i18n/km.json b/OAuth/i18n/km.json
new file mode 100644
index 00000000..89ab2b0a
--- /dev/null
+++ b/OAuth/i18n/km.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "គីមស៊្រុន"
+ ]
+ },
+ "mwoauth-field-hidden": "(ព័ត៌មាននេះត្រូវបានលាក់មិនអោយអ្នកដទៃមើលឃើញ)",
+ "mwoauth-field-private": "(ព័ត៌មាននេះត្រូវបានលាក់មិនអោយអ្នកដទៃមើលឃើញ)",
+ "mwoauth-prefs-managegrants": "កម្មវិធីភ្ជាប់មករួច៖",
+ "mwoauth-prefs-managegrantslink": "ចាត់ចែង{{PLURAL:$1|កម្មវិធី|កម្មវិធី}}ភ្ជាប់មករួចចំនួន$1",
+ "mwoauth-consumer-allwikis": "គម្រោងទាំងអស់នៅលើវិបសាយនេះ"
+}
diff --git a/OAuth/i18n/ko.json b/OAuth/i18n/ko.json
new file mode 100644
index 00000000..4e6ea0a9
--- /dev/null
+++ b/OAuth/i18n/ko.json
@@ -0,0 +1,267 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hwangjy9",
+ "Hym411",
+ "LiteHell",
+ "Macofe",
+ "MemphisA5",
+ "Priviet",
+ "Revi",
+ "Ykhwong",
+ "아라"
+ ]
+ },
+ "mwoauth-desc": "API 인증을 위해 OAuth 1.0a를 사용할 수 있게 합니다",
+ "mwoauth-verified": "프로세스를 완성하기 위해서는 이 증명값을 이 응용프로그램에 입력해야 합니다: '''$1'''",
+ "mwoauth-db-readonly": "OAuth 데이터베이스가 현재 잠겨 있습니다. 다시 시도해 주세요.",
+ "mwoauth-missing-field": "\"$1\" 필드에 대한 값이 없습니다",
+ "mwoauth-invalid-field": "\"$1\" 필드에 제공한 값이 잘못되었습니다",
+ "mwoauth-invalid-field-generic": "제공한 값이 잘못되었습니다",
+ "mwoauth-field-hidden": "(이 정보는 숨겨져 있습니다)",
+ "mwoauth-field-private": "(이 정보는 비공개입니다)",
+ "mwoauth-prefs-managegrants": "연결된 앱:",
+ "mwoauth-prefs-managegrantslink": "연결된 {{PLURAL:$1|애플리케이션}} $1개 관리",
+ "mwoauth-consumer-allwikis": "이 사이트의 모든 프로젝트",
+ "mwoauth-consumer-key": "컨슈머 키:",
+ "mwoauth-consumer-name": "애플리케이션 이름:",
+ "mwoauth-consumer-version": "컨슈머 버전:",
+ "mwoauth-consumer-user": "게시자:",
+ "mwoauth-consumer-stage": "현재 상태:",
+ "mwoauth-consumer-email": "연락처 이메일 주소:",
+ "mwoauth-consumer-owner-only-label": "소유자 전용:",
+ "mwoauth-consumer-owner-only": "$1 사용자만 사용할 수 있습니다.",
+ "mwoauth-consumer-description": "애플리케이션 설명:",
+ "mwoauth-consumer-callbackurl": "OAuth \"콜백\" URL:",
+ "mwoauth-consumer-granttypes": "요청된 승인 유쳥들:",
+ "mwoauth-consumer-grantsneeded": "적용할 수 있는 부여:",
+ "mwoauth-consumer-required-grant": "컨슈머에게 적용할 수 있음",
+ "mwoauth-consumer-wiki": "적용할 수 있는 프로젝트:",
+ "mwoauth-consumer-wiki-thiswiki": "현재 프로젝트($1)",
+ "mwoauth-consumer-restrictions": "사용 제한:",
+ "mwoauth-consumer-restrictions-json": "사용 제한 (JSON):",
+ "mwoauth-consumer-rsakey": "공개 RSA 키 (선택 사항):",
+ "mwoauth-consumer-secretkey": "컨슈머 비밀 토큰:",
+ "mwoauth-consumer-accesstoken": "접근 토큰:",
+ "mwoauth-consumer-reason": "이유:",
+ "mwoauth-consumer-email-unconfirmed": "계정 이메일 주소가 아직 확인되지 않았습니다.",
+ "mwoauth-consumer-email-mismatched": "제공한 이메일 주소는 계정과 일치하지 않습니다.",
+ "mwoauth-consumer-alreadyexists": "이 이름/버전/게시자 조합으로 된 컨슈머가 이미 존재합니다",
+ "mwoauth-consumer-alreadyexistsversion": "이 이름/게시자 조합으로 된 컨슈머가 같거나 높은 버전(\"$1\")으로 이미 존재합니다",
+ "mwoauth-consumer-not-accepted": "보류 중인 컨슈머 요청에 대한 정보를 업데이트할 수 없습니다",
+ "mwoauth-consumer-not-proposed": "컨슈머가 현재 제안되어 있지 않습니다",
+ "mwoauth-consumer-not-disabled": "컨슈머가 현재 비활성화되어 있지 않습니다",
+ "mwoauth-consumer-not-approved": "컨슈머가 현재 승인되어 있지 않습니다 (비활성화되었을 수 있습니다)",
+ "mwoauth-missing-consumer-key": "컨슈머 키가 입력되지 않습니다.",
+ "mwoauth-invalid-consumer-key": "주어진 키로 된 컨슈머가 존재하지 않습니다.",
+ "mwoauth-invalid-access-token": "주어진 키로 된 접근 토큰이 존재하지 않습니다.",
+ "mwoauth-invalid-access-wrongwiki": "컨슈머는 \"$1\" 프로젝트에서만 사용할 수 있습니다.",
+ "mwoauth-consumer-conflict": "누군가 당신이 보기 전에 이 고객의 속성을 바꿨습니다. 다시 시도해주세요. 바뀜 기록을 확인하실 수 있습니다.",
+ "mwoauth-consumer-grantshelp": "각각 부여된 값은 목록에서 사용자 계정을 이미 갖고 있는 사용자 권한에 접근할 수 있는 권한을 줍니다. 자세한 정보는 [[Special:ListGrants|부여 표]]을 보세요.",
+ "mwoauth-consumer-stage-proposed": "제안됨",
+ "mwoauth-consumer-stage-rejected": "거부됨",
+ "mwoauth-consumer-stage-expired": "만료됨",
+ "mwoauth-consumer-stage-approved": "승인됨",
+ "mwoauth-consumer-stage-disabled": "비활성화됨",
+ "mwoauth-consumer-stage-suppressed": "표시하지 않음",
+ "oauthconsumerregistration": "OAuth 컨슈머 등록",
+ "mwoauthconsumerregistration-navigation": "둘러보기:",
+ "mwoauthconsumerregistration-propose": "새 컨슈머 제안",
+ "mwoauthconsumerregistration-list": "내 컨슈머 목록",
+ "mwoauthconsumerregistration-main": "대문",
+ "mwoauthconsumerregistration-propose-text": "개발자는 다음 양식을 사용하여 새로운 OAuth 고객을 제안해야 합니다.(자세한 정보는 [//www.mediawiki.org/wiki/Extension:OAuth 확장 기능 문서]를 참조하세요) 이 양식을 제출하고 나면 당신의 지원서가 미디어위키에서 자체적으로 식별하는데 사용할 토큰을 받게 됩니다. OAuth 관리자는 다른 사용자가 권한을 받기 전에 지원서를 승인해야 합니다.\n\n몇 가지 권고 및 참고사항:\n* 가능한 적은 권한을 사용하도록 노력해 주세요. 실제로 지금 필요하지 않은 권한은 사용을 삼가세요.\n* 버전은 \"major.minor.release\" 형태이며 (마지막 2가지는 선택 사항) 권한 변경이 필요할 때 숫자가 증가합니다.\n* 가능하면 공개 RSA 키(PEM 포맷)를 제공해 주세요. 아니면 비밀 토큰(덜 안전함)을 사용해야 합니다.\n* 고객이 특정한 CIDR 범위 내의 IP에서만 접근하도록 제한하려면 JSON 제한 필드를 사용해 주세요.\n* 고객이 이 사이트의 하나의 프로젝트에만 접근할 수 있도록 제한하려면 프로젝트 ID를 사용하면 됩니다. (모든 프로젝트에는 \"*\" 을 사용하세요.)\n* 제공된 이메일 주소가 당신의 계정의 이메일 주소와 일치해야 합니다. (인증되어야 합니다)",
+ "mwoauthconsumerregistration-update-text": "아래의 양식을 사용하여 당신이 관리하는 OAuth 고객의 속성을 업데이트하세요. \n\n여기의 모든 값은 이전 값을 덮어쓸 것입니다. 이 값을 지우려고 하지 않는다면 빈 필드는 비워두세요.",
+ "mwoauthconsumerregistration-maintext": "이 문서는 개발자가 제안하고 이 사이트의 레지스트리에서 OAuth 컨슈머 응용 프로그램을 업데이트할 수 있도록 합니다.\n\n여기서 할 수 있는 작업:\n* [[Special:OAuthConsumerRegistration/propose|새로운 고객에게 토큰을 요청]].\n* [[Special:OAuthConsumerRegistration/list|기존 고객 관리]].\n\nOAuth에 대한 더 자세한 정보를 얻으려면 [//www.mediawiki.org/wiki/Extension:OAuth 확장 기능 문서]를 참조하세요.",
+ "mwoauthconsumerregistration-propose-legend": "새 OAuth 컨슈머 애플리케이션",
+ "mwoauthconsumerregistration-update-legend": "OAuth 컨슈머 애플리케이션 업데이트",
+ "mwoauthconsumerregistration-propose-submit": "컨슈머 제안",
+ "mwoauthconsumerregistration-update-submit": "컨슈머 업데이트",
+ "mwoauthconsumerregistration-none": "어떠한 OAuth 컨슈머도 제어하지 않습니다.",
+ "mwoauthconsumerregistration-name": "컨슈머",
+ "mwoauthconsumerregistration-user": "게시자",
+ "mwoauthconsumerregistration-description": "설명",
+ "mwoauthconsumerregistration-email": "연락처 이메일",
+ "mwoauthconsumerregistration-consumerkey": "컨슈머 키",
+ "mwoauthconsumerregistration-stage": "상태",
+ "mwoauthconsumerregistration-lastchange": "마지막으로 바뀜",
+ "mwoauthconsumerregistration-manage": "관리",
+ "mwoauthconsumerregistration-resetsecretkey": "비밀 키를 새 값으로 재설정",
+ "mwoauthconsumerregistration-proposed": "당신의 OAuth 고객 요청을 받았습니다.\n\n'''$1''' 컨슈머 토큰과 '''$2''' 비밀 토큰을 재할당했습니다.\n\"나중에 참조하기 위해서 이것을 기록해주세요.\"",
+ "mwoauthconsumerregistration-created-owner-only": "당신의 OAuth 컨슈머가 생성되었습니다.\n당신의 토큰:\n;컨슈머 토큰: $1\n;컨슈머 비밀: $2\n;접근 토큰: $3\n;접근 비밀: $4",
+ "mwoauthconsumerregistration-updated": "OAuth 컨슈머 등록을 업데이트했습니다.",
+ "mwoauthconsumerregistration-secretreset": "고객에게 '''$1'''의 비밀 토큰을 할당했습니다. \"나중에 참조하기 위해서 이것을 기록해주세요.\"",
+ "oauthmanageconsumers": "OAuth 컨슈머 관리",
+ "mwoauthmanageconsumers-notloggedin": "이 페이지에 접근하려면 로그인해야 합니다.",
+ "mwoauthmanageconsumers-type": "대기열:",
+ "mwoauthmanageconsumers-showproposed": "제안된 요청",
+ "mwoauthmanageconsumers-showrejected": "거부된 요청",
+ "mwoauthmanageconsumers-showexpired": "만료된 요청",
+ "mwoauthmanageconsumers-main": "주요",
+ "mwoauthmanageconsumers-maintext": "이 문서는 OAuth(http://oauth.net 를 참조하세요) 컨슈머 지원서 요청을 관리하고 인증된 OAuth 컨슈머를 관리합니다.",
+ "mwoauthmanageconsumers-queues": "아래에서 컨슈머 확인 대기열을 선택하세요:",
+ "mwoauthmanageconsumers-q-proposed": "제안된 컨슈머 요청의 대기열",
+ "mwoauthmanageconsumers-q-rejected": "거부된 컨슈머 요청의 대기열",
+ "mwoauthmanageconsumers-q-expired": "만료된 컨슈머 요청의 대기열",
+ "mwoauthmanageconsumers-lists": "아래에서 컨슈머 상태 목록을 선택:",
+ "mwoauthmanageconsumers-l-approved": "현재 승인된 컨슈머 목록",
+ "mwoauthmanageconsumers-l-disabled": "현재 비활성화된 컨슈머 목록",
+ "mwoauthmanageconsumers-none-proposed": "이 목록에 제안된 컨슈머가 없습니다.",
+ "mwoauthmanageconsumers-none-rejected": "이 목록에 제안된 컨슈머가 없습니다.",
+ "mwoauthmanageconsumers-none-expired": "이 목록에 제안된 컨슈머가 없습니다.",
+ "mwoauthmanageconsumers-none-approved": "이 조건에 맞는 컨슈머가 없습니다.",
+ "mwoauthmanageconsumers-none-disabled": "이 조건에 맞는 컨슈머가 없습니다.",
+ "mwoauthmanageconsumers-name": "컨슈머",
+ "mwoauthmanageconsumers-user": "게시자",
+ "mwoauthmanageconsumers-description": "설명",
+ "mwoauthmanageconsumers-email": "연락처 이메일",
+ "mwoauthmanageconsumers-consumerkey": "컨슈머 키",
+ "mwoauthmanageconsumers-lastchange": "마지막으로 바뀜",
+ "mwoauthmanageconsumers-review": "검토/관리",
+ "mwoauthmanageconsumers-confirm-text": "이 컨슈머를 승인, 거부, 비활성화, 또는 다시 활성화하려면 이 양식을 사용하세요.",
+ "mwoauthmanageconsumers-confirm-legend": "OAuth 컨슈머 관리",
+ "mwoauthmanageconsumers-action": "상태 바꾸기:",
+ "mwoauthmanageconsumers-approve": "승인됨",
+ "mwoauthmanageconsumers-reject": "거부됨",
+ "mwoauthmanageconsumers-rsuppress": "거부 및 표시하지 않음",
+ "mwoauthmanageconsumers-disable": "비활성화됨",
+ "mwoauthmanageconsumers-dsuppress": "비활성화 및 표시하지 않음",
+ "mwoauthmanageconsumers-reenable": "승인됨",
+ "mwoauthmanageconsumers-reason": "이유:",
+ "mwoauthmanageconsumers-confirm-submit": "컨슈머 상태 업데이트",
+ "mwoauthmanageconsumers-success-approved": "요청이 승인되었습니다.",
+ "mwoauthmanageconsumers-success-rejected": "요청이 거부되었습니다.",
+ "mwoauthmanageconsumers-success-disabled": "컨슈머가 비활성화되었습니다.",
+ "mwoauthmanageconsumers-success-reanable": "컨슈머가 다시 활성화되었습니다.",
+ "mwoauthmanageconsumers-search-name": "이 이름을 가진 컨슈머",
+ "mwoauthmanageconsumers-search-publisher": "이 사용자의 컨슈머",
+ "oauthlistconsumers": "OAuth 애플리케이션 목록",
+ "mwoauthlistconsumers-legend": "OAuth 애플리케이션 찾아보기",
+ "mwoauthlistconsumers-view": "자세한 사항",
+ "mwoauthlistconsumers-none": "이 조건에 맞는 애플리케이션을 찾을 수 없습니다.",
+ "mwoauthlistconsumers-name": "애플리케이션 이름",
+ "mwoauthlistconsumers-version": "컨슈머 버전",
+ "mwoauthlistconsumers-user": "게시자",
+ "mwoauthlistconsumers-description": "설명",
+ "mwoauthlistconsumers-wiki": "적용할 수 있는 프로젝트",
+ "mwoauthlistconsumers-callbackurl": "OAuth \"콜백 URL\"",
+ "mwoauthlistconsumers-grants": "적용할 수 있는 부여",
+ "mwoauthlistconsumers-basicgrantsonly": "(기본 액세스의 경우에만)",
+ "mwoauthlistconsumers-status": "상태",
+ "mwoauth-consumer-stage-any": "모두",
+ "mwoauthlistconsumers-status-proposed": "제안됨",
+ "mwoauthlistconsumers-status-approved": "승인됨",
+ "mwoauthlistconsumers-status-disabled": "비활성화됨",
+ "mwoauthlistconsumers-status-rejected": "거부됨",
+ "mwoauthlistconsumers-status-expired": "만료됨",
+ "mwoauthlistconsumers-rclink": "이 애플리케이션의 최근 바뀜",
+ "oauthmanagemygrants": "연결된 애플리케이션 관리",
+ "mwoauthmanagemygrants-text": "이 문서는 당신의 계정을 사용할 수 있는 모든 애플리케이션을 나열합니다. 이러한 애플리케이션은 당신을 대신하여 인증할 때 애플리케이션에 부여한 권한이 접근 범위를 제한합니다. 만약 애플리케이션이 당신을 대신해 자매 프로젝트에 접근하도록 하기 위해서 애플리케이션에 개별적으로 권한을 부여한다면 각각의 프로젝트들에 대한 개별 설정이 아래에 보일 것입니다.\n\n연결된 애플리케이션들은 OAuth 프로토콜을 사용하여 계정에 접근합니다. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth 연결된 애플리케이션에 대해 더 알아보세요])</span>",
+ "mwoauthmanagemygrants-navigation": "둘러보기:",
+ "mwoauthmanagemygrants-showlist": "연결된 애플리케이션 목록",
+ "mwoauthmanagemygrants-none": "계정에 연결된 애플리케이션이 없습니다.",
+ "mwoauthmanagemygrants-user": "게시자:",
+ "mwoauthmanagemygrants-description": "설명",
+ "mwoauthmanagemygrants-wikiallowed": "프로젝트에 허용됨:",
+ "mwoauthmanagemygrants-grants": "적용할 수 있는 부여",
+ "mwoauthmanagemygrants-grantsallowed": "허용된 부여",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "적용할 수 있는 허용된 부여:",
+ "mwoauthmanagemygrants-review": "접근 관리",
+ "mwoauthmanagemygrants-revoke": "접근 취소",
+ "mwoauthmanagemygrants-grantaccept": "컨슈머 부여됨",
+ "mwoauthmanagemygrants-update-text": "당신을 대신하여 애플리케이션에 부여된 권한을 수정하려면 아래 양식을 사용하세요.",
+ "mwoauthmanagemygrants-revoke-text": "당신을 대신하여 역할을 하는 애플리케이션에 접근할 권한을 취소하려면 아래 양식을 사용하세요.",
+ "mwoauthmanagemygrants-confirm-legend": "연결된 애플리케이션 관리",
+ "mwoauthmanagemygrants-update": "접근 토큰 업데이트",
+ "mwoauthmanagemygrants-renounce": "접근 토큰 인증 해제",
+ "mwoauthmanagemygrants-action": "상태 바꾸기:",
+ "mwoauthmanagemygrants-confirm-submit": "접근 토큰 상태 업데이트",
+ "mwoauthmanagemygrants-success-update": "이 응용 프로그램의 환경 설정이 업데이트되었습니다.",
+ "mwoauthmanagemygrants-success-renounce": "당신의 계정에 대한 응용 프로그램의 접근이 취소되었습니다.",
+ "mwoauthmanagemygrants-basic-tooltip": "왜 내가 이 권한 부여를 업데이트할 수 없나요? 이 권한 부여는 당신의 연결된 애플리케이션에 제대로 작동하는데 필요한 기본 권한을 부여합니다. 만약 당신이 이 연결된 애플리케이션에 이 권한을 부여하고 싶지 않다면 애플리케이션의 접근을 취소해야 합니다.",
+ "logentry-mwoauthconsumer-propose": "$1님이 OAuth 컨슈머를 {{GENDER:$2|제안했습니다}} (컨슈머 키 $4)",
+ "logentry-mwoauthconsumer-update": "$1님이 OAuth 컨슈머를 {{GENDER:$2|업데이트했습니다}} (컨슈머 키 $4)",
+ "logentry-mwoauthconsumer-approve": "$1님이 $3에 의해 OAuth 컨슈머를 {{GENDER:$2|승인했습니다}} (컨슈머 키 $4)",
+ "logentry-mwoauthconsumer-reject": "$1님이 $3에 의해 OAuth 컨슈머를 {{GENDER:$2|거부했습니다}} (컨슈머 키 $4)",
+ "logentry-mwoauthconsumer-disable": "$1님이 $3에 의해 OAuth 컨슈머를 {{GENDER:$2|비활성화했습니다}} (컨슈머 키 $4)",
+ "logentry-mwoauthconsumer-reenable": "$1님이 $3에 의해 OAuth 컨슈머를 {{GENDER:$2|다시 활성화했습니다}} (컨슈머 키 $4)",
+ "mwoauthconsumer-consumer-logpage": "OAuth 컨슈머 기록",
+ "mwoauthconsumer-consumer-logpagetext": "OAuth 컨슈머에 등록된 승인, 거부와 비활성화의 기록입니다.",
+ "mwoauth-bad-request-missing-params": "죄송합니다, 이 연결된 애플리케이션을 구성하는 데 문제가 발생했습니다. 애플리케이션의 개발자에게 문의하십시오.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth 변수 없음, $1</span>",
+ "mwoauth-bad-request-invalid-action": "죄송합니다. 문제가 발생했습니다. 도움을 받으려면 응용 프로그램 개발자에게 연락해야 합니다.\n\n<span class=\"plainlinks mw-mwoautherror-details\">알 수 없는 URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "죄송합니다. 문제가 발생했습니다. 도움을 받으려면 응용 프로그램 개발자에게 [$1 연락]해야 합니다.\n\n<span class=\"plainlinks mw-mwoautherror-details\">알 수 없는 URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "인증 토큰에 대한 승인된 부여를 찾을 수 없습니다.",
+ "mwoauthdatastore-request-token-not-found": "죄송합니다. 이 응용 프로그램에 연결하는 도중 문제가 발생하였습니다.\n뒤로 돌아가서 계정에 연결을 다시 시도하시거나 응용 프로그램 개발자에게 연락하십시오.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth 토큰 찾을 수 없음, $1</span>",
+ "mwoauthdatastore-request-token-already-used": "이 요청은 이미 완료되었으므로 다시 제출할 수 없습니다.\n애플리케이션으로 돌아간 다음 계정에 다시 접속을 시도하거나 애플리케이션 개발자에게 문의하십시오.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth 토큰이 이미 사용됨, $1</span>",
+ "mwoauthdatastore-bad-token": "요청과 일치하는 토큰을 찾을 수 없습니다.",
+ "mwoauthdatastore-bad-source-ip": "유효하지 않은 IP 주소로부터 요청이 왔습니다.",
+ "mwoauthdatastore-bad-verifier": "제공한 인증 코드는 올바르지 않습니다.",
+ "mwoauthdatastore-invalid-token-type": "요청한 토큰 유형이 잘못되었습니다.",
+ "mwoauthgrants-general-error": "OAuth 요청에 오류가 있습니다.",
+ "mwoauthserver-bad-consumer": "\"$1\" 앱은 연결된 앱으로 승인되지 않습니다. 애플리케이션 개발자에게 도움을 [$2 요청하세요].\n\n<span class=\"plainlinks mw-mwoautherror-details\">연결된 OAuth 앱이 승인되지 않음, $3</span>",
+ "mwoauthserver-bad-consumer-key": "죄송합니다. 이 응용 프로그램과 연결하는 도중 문제가 발생했습니다.\n\n<span class=\"plainlinks mw-mwoautherror-details\">알 수 없는 OAuth 키, $1</span>",
+ "mwoauthserver-insufficient-rights": "당신의 계정은 연결된 응용 프로그램을 사용하는 것이 허용되지 않으며 그 이유에 대해서는 사이트 관리자에게 문의하세요.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth 사용자 권한 부족, $1</span>",
+ "mwoauthserver-invalid-request-token": "요청에 잘못된 토큰이 있습니다.",
+ "mwoauthserver-invalid-user": "이 사이트에서 연결된 응용 프로그램을 사용하려면 모든 프로젝트에서 계정을 가지고 있어야 합니다. 모든 프로젝트에서 계정을 취득하고 나면 \"$1\"에 다시 연결을 시도할 수 있습니다.\n\n<span class=\"plainlinks mw-mwoautherror-details\">통합 로그인이 필요합니다, $2</span>",
+ "mwoauth-invalid-authorization-title": "OAuth 인증 오류",
+ "mwoauth-invalid-authorization": "요청에 있는 인증 헤더가 올바르지 않습니다: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "요청에 있는 인증 헤더가 $1에 대해 올바르지 않습니다",
+ "mwoauth-invalid-authorization-invalid-user": "요청에 있는 인증 헤더가 여기에서 존재하지 않는 사용자를 위한 것입니다",
+ "mwoauth-invalid-authorization-wrong-user": "요청에 있는 인증 헤더가 다른 사용자를 위한 것입니다",
+ "mwoauth-invalid-authorization-not-approved": "연결하려는 앱이 잘못 설정된 것으로 보입니다. \"$1\"의 저자에게 도움을 요청하세요.",
+ "mwoauth-invalid-authorization-blocked-user": "요청에 있는 인증 헤더가 차단된 사용자를 위한 것입니다",
+ "mwoauth-form-description-allwikis": "$1님 안녕하세요,\n\n'''$2''' 애플리케이션이 당신을 대신하여 이 사이트의 모든 프로젝트에 다음 동작을 수행할 권한이 필요합니다:\n\n$4",
+ "mwoauth-form-description-onewiki": "$1님 안녕하세요,\n\n'''$2''' 애플리케이션이 당신을 대신하여 ''$4'' 프로젝트에 다음 동작을 수행할 권한이 필요합니다:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "$1님 안녕하세요,\n\n'''$2''' 애플리케이션이 당신을 대신하여 이 사이트의 모든 프로젝트에 정보를 접근할 권한이 필요합니다. 계정에는 어떠한 변화도 이루어지지 않습니다.",
+ "mwoauth-form-description-onewiki-nogrants": "$1님 안녕하세요,\n\n'''$2''' 애플리케이션이 당신을 대신하여 ''$4'' 프로젝트에 정보를 접근할 권한이 필요합니다. 계정에는 어떠한 변화도 이루어지지 않습니다.",
+ "mwoauth-form-button-approve": "허용합니다",
+ "mwoauth-form-button-cancel": "취소합니다.",
+ "mwoauth-error": "애플리케이션 연결 오류",
+ "mwoauth-grants-heading": "요청된 권한:",
+ "mwoauth-grants-nogrants": "애플리케이션은 권한을 요청하지 않았습니다.",
+ "mwoauth-acceptance-cancelled": "\"$1\" 고객이 당신의 계정에 접근하지 못하도록 선택했습니다. 접근할 수 없으면 \"$1\" 고객이 제대로 동작하지 못할 수도 있습니다. \"$1\"에 돌아가거나 연결된 응용 프로그램을 [[Special:OAuthManageMyGrants|관리]]해주세요.",
+ "mwoauth-granttype-normal": "특정한 권한에 대한 승인 요청.",
+ "grant-mwoauth-authonly": "인증 전용, API 접근 불가.",
+ "grant-mwoauth-authonlyprivate": "인증 전용. 특수:OAuth/identify을 통해 사용자 이름과 이메일 주소로 접근. API 접근 불가.",
+ "mwoauth-listgrants-extra-summary": "== OAuth 특정 권한 ==\n\n아래의 추가 부여는 OAuth 사용자들에게 적용됩니다.",
+ "mwoauth-oauth-exception": "OAuth 프로토콜에 오류가 발생했습니다: $1",
+ "mwoauth-callback-not-oob": "oauth_callback은 \"oob\"로 설정해야 합니다 (대소문자 구분)",
+ "right-mwoauthproposeconsumer": "새 OAuth 컨슈머 제안",
+ "right-mwoauthupdateownconsumer": "내가 제어할 수 있는 OAuth 컨슈머 업데이트",
+ "right-mwoauthmanageconsumer": "OAuth 컨슈머 관리",
+ "right-mwoauthsuppress": "OAuth 컨슈머 억제",
+ "right-mwoauthviewsuppressed": "표시하지 않은 OAuth 컨슈머 보기",
+ "right-mwoauthviewprivate": "비공개 OAuth 데이터 보기",
+ "right-mwoauthmanagemygrants": "OAuth 부여 관리",
+ "action-mwoauthmanageconsumer": "OAuth 컨슈머 관리",
+ "action-mwoauthmanagemygrants": "내 OAuth 부여 관리",
+ "action-mwoauthproposeconsumer": "새 OAuth 컨슈머 제안",
+ "action-mwoauthupdateownconsumer": "내가 제어할 수 있는 OAuth 컨슈머 업데이트",
+ "action-mwoauthviewsuppressed": "표시하지 않은 OAuth 컨슈머 보기",
+ "mwoauth-botpasswords-note": "<strong>주의:</strong> <span class=\"plainlinks\">[$1 OAuth]</span>가 봇 비밀번호 보다 더 안전하며 봇이 이를 지원하면 이 기능이 더 선호됩니다.",
+ "mwoauth-api-module-disabled": "\"$1\" 모듈은 OAuth와 함께 사용할 수 없습니다.",
+ "echo-category-title-oauth-owner": "OAuth 개발",
+ "echo-pref-tooltip-oauth-owner": "내가 만든 OAuth 애플리케이션에 관한 이벤트를 내게 알립니다.",
+ "echo-category-title-oauth-admin": "OAuth 관리자",
+ "echo-pref-tooltip-oauth-admin": "OAuth 애플리케이션의 검토에 관한 이벤트를 내게 알립니다.",
+ "notification-oauth-app-propose-title": "$1님이 새로운 OAuth 앱을 {{GENDER:$1|제안했습니다}}: $2",
+ "notification-oauth-app-update-title": "$1님이 OAuth 앱 $2을(를) {{GENDER:$1|업데이트했습니다}}",
+ "notification-oauth-app-approve-title": "$1님이 {{GENDER:$3|당신의}} OAuth 앱($2)을 {{GENDER:$2|승인했습니다}}",
+ "notification-oauth-app-reject-title": "$1님이 {{GENDER:$3|당신의}} OAuth 앱($2)을 {{GENDER:$2|거부했습니다}}",
+ "notification-oauth-app-disable-title": "$1님이 {{GENDER:$3|당신의}} OAuth 앱($2)을 {{GENDER:$2|비활성화했습니다}}",
+ "notification-oauth-app-reenable-title": "$1님이 {{GENDER:$3|당신의}} OAuth 앱($2)을 {{GENDER:$2|다시 활성화했습니다}}",
+ "notification-oauth-app-propose-subject": "$1님이 {{SITENAME}}에서 새로운 OAuth 앱을 {{GENDER:$1|제안했습니다}}",
+ "notification-oauth-app-update-subject": "$1님이 {{SITENAME}}에서 OAuth 앱을 {{GENDER:$1|업데이트했습니다}}",
+ "notification-oauth-app-approve-subject": "$1님이 {{SITENAME}}에서 {{GENDER:$3|당신의}} OAuth 앱을 {{GENDER:$2|승인했습니다}}",
+ "notification-oauth-app-reject-subject": "$1님이 {{SITENAME}}에서 {{GENDER:$3|당신의}} OAuth 앱($2)을 {{GENDER:$2|거부했습니다}}",
+ "notification-oauth-app-disable-subject": "$1님이 {{SITENAME}}에서 {{GENDER:$3|당신의}} OAuth 앱을 {{GENDER:$2|비활성화했습니다}}",
+ "notification-oauth-app-reenable-subject": "$1님이 {{SITENAME}}에서 {{GENDER:$3|당신의}} OAuth 앱($2)을 {{GENDER:$2|다시 활성화했습니다}}",
+ "notification-oauth-app-propose-primary-link": "앱 평가",
+ "notification-oauth-app-update-primary-link": "앱 평가",
+ "notification-oauth-app-approve-primary-link": "앱 보기",
+ "notification-oauth-app-reject-primary-link": "앱 보기",
+ "notification-oauth-app-disable-primary-link": "앱 보기",
+ "notification-oauth-app-reenable-primary-link": "앱 보기",
+ "notification-oauth-app-body": "이유: $1",
+ "mwoauth-login-required-reason": "애플리케이션의 접근 인가를 위해 {{SITENAME}} 계정으로 로그인해야 합니다.",
+ "mwoauthconsumer-application-view": "이 애플리케이션 보기"
+}
diff --git a/OAuth/i18n/krc.json b/OAuth/i18n/krc.json
new file mode 100644
index 00000000..7a84f449
--- /dev/null
+++ b/OAuth/i18n/krc.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ernác",
+ "Iltever"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Ишлеген къошакъ программала:",
+ "mwoauth-prefs-managegrantslink": "$1 джандырылгъан {{PLURAL:$1|къошакъ программаны}} джюрютюу",
+ "mwoauthmanageconsumers-user": "Басмагъа чыгъаргъан",
+ "mwoauthmanageconsumers-description": "Ачыкълау",
+ "mwoauthmanageconsumers-email": "Контактлы e-mail адрес",
+ "mwoauthmanageconsumers-consumerkey": "Клиентни идентификатору",
+ "mwoauthmanageconsumers-lastchange": "Ахыр тюрлениу",
+ "mwoauthmanageconsumers-reject": "Къабыл этилмеди",
+ "mwoauthmanageconsumers-disable": "Джукъланыбды",
+ "mwoauthmanageconsumers-reason": "Чурум:",
+ "mwoauthlistconsumers-view": "айгъакълаула",
+ "mwoauthlistconsumers-status": "Статус",
+ "mwoauthlistconsumers-status-rejected": "къабыл этилмеди",
+ "oauthmanagemygrants": "Джандырылгъан къошакъ программаланы джюрютюу",
+ "mwoauthmanagemygrants-user": "Басмагъа чыгъаргъан:",
+ "mwoauthmanagemygrants-description": "Ачыкълау"
+}
diff --git a/OAuth/i18n/ksh.json b/OAuth/i18n/ksh.json
new file mode 100644
index 00000000..20ef5107
--- /dev/null
+++ b/OAuth/i18n/ksh.json
@@ -0,0 +1,85 @@
+{
+ "@metadata": {
+ "authors": [
+ "Purodha"
+ ]
+ },
+ "oauth": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"an open standard for authorization\">OAuth</i>",
+ "mwoauth-desc": "Määd e müjjelesch, <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"an open standard for authorization\">OAuth</i> Väsjohn 1.0a för et Enlogge övver de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i> ze bruche.",
+ "mwoauth-db-readonly": "De Dahtebangk vum <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"an open standard for authorization\">OAuth</i> es em Momang jeschpächt. Versöhg et en e paa Menotte wider.",
+ "mwoauth-missing-field": "Ene Wäät för dat Väld „$1“ fählt",
+ "mwoauth-invalid-field": "Ene onjöltejje Wäät för dat Väld „$1“ wood enjejovve",
+ "mwoauth-invalid-field-generic": "Ene onjöltejje Wäät wood enjejovve",
+ "mwoauth-field-hidden": "(di Aanjahbe sin verschtoche)",
+ "mwoauth-field-private": "(di Aanjahbe sin nit öffentlesch)",
+ "mwoauth-consumer-allwikis": "Alle Projäkte op heh dä ẞait",
+ "mwoauth-consumer-name": "Dä Nahme vun dä Aanwändong:",
+ "mwoauth-consumer-user": "Öffentlesch jemaat vum:",
+ "mwoauth-consumer-stage": "Der aktoölle Zohschtand:",
+ "mwoauth-consumer-email": "De <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß för der Kuntak:",
+ "mwoauth-consumer-description": "Donn di Aanwändong beschrieve:",
+ "mwoauth-consumer-wiki-thiswiki": "Et aktoälle Projäk ($1)",
+ "mwoauth-consumer-rsakey": "Der öffentlesche <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Rivest-Shamir-Adleman cryptosystem\">RSA</i>-Schlößel:",
+ "mwoauth-consumer-reason": "Jrond:",
+ "mwoauth-consumer-email-unconfirmed": "De <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß för Dinge Zohjang es not nit beschtähtesch.",
+ "mwoauth-consumer-email-mismatched": "De aanjejovve <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß moß desällve sin, wi för Dinge Zohjang.",
+ "mwoauth-consumer-stage-proposed": "vörjeschlahre",
+ "mwoauth-consumer-stage-rejected": "affjelehnt",
+ "mwoauth-consumer-stage-expired": "afjeloufe:",
+ "mwoauth-consumer-stage-approved": "johdjeheiße",
+ "mwoauth-consumer-stage-disabled": "ußjeschalldt",
+ "mwoauth-consumer-stage-suppressed": "onerdrök",
+ "mwoauthconsumerregistration-user": "Der Verlaach",
+ "mwoauthconsumerregistration-description": "Beschrieve als",
+ "mwoauthconsumerregistration-email": "De <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß för der Kunkat",
+ "mwoauthconsumerregistration-stage": "Zohschtand",
+ "mwoauthconsumerregistration-lastchange": "De läzde Änderong",
+ "mwoauthconsumerregistration-manage": "verwallde",
+ "mwoauthconsumerregistration-resetsecretkey": "Säz dä jeheime Schlößel obb ene neue Wäät",
+ "mwoauthmanageconsumers-notloggedin": "Do moß enjelogg sin, öm heh di Sigg ze sin ze krijje.",
+ "mwoauthmanageconsumers-type": "wahdeschlange:",
+ "mwoauthmanageconsumers-showproposed": "Vorjeschlahre Wönsch",
+ "mwoauthmanageconsumers-showrejected": "Affjelehnte Wönsch",
+ "mwoauthmanageconsumers-showexpired": "Affjeloufe Wönsch",
+ "mwoauthmanageconsumers-user": "Verlaach",
+ "mwoauthmanageconsumers-email": "De <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß för der Kunkat",
+ "mwoauthmanageconsumers-lastchange": "De läzde Änderong",
+ "mwoauthmanageconsumers-review": "bekike un verwallde",
+ "mwoauthmanageconsumers-action": "Der Zohschtand ändere:",
+ "mwoauthmanageconsumers-approve": "Aanjenumme",
+ "mwoauthmanageconsumers-reject": "Affjelehnt",
+ "mwoauthmanageconsumers-rsuppress": "Affjelehnt un ongerdrök",
+ "mwoauthmanageconsumers-disable": "Ußjeschalldt",
+ "mwoauthmanageconsumers-dsuppress": "\nUßjeschalldt un ongerdrök",
+ "mwoauthmanageconsumers-reenable": "Wider aanjenumme",
+ "mwoauthmanageconsumers-reason": "Jrond:",
+ "mwoauthmanageconsumers-confirm-submit": "Lohß jonn!",
+ "mwoauthmanageconsumers-success-approved": "Der Wonsch wood aanjenumme.",
+ "mwoauthmanageconsumers-success-rejected": "Der Wonsch wood affjelehnt.",
+ "mwoauthlistconsumers-view": "Einzelheijte",
+ "mwoauthlistconsumers-none": "Kein Projramme jefonge, op di dat zohtreff.",
+ "mwoauthlistconsumers-name": "Projrammnahme",
+ "mwoauthlistconsumers-status": "Zohschtand",
+ "mwoauth-consumer-stage-any": "jehde",
+ "mwoauthlistconsumers-status-proposed": "vörjeschlonn",
+ "mwoauthlistconsumers-status-approved": "aanjenumme",
+ "mwoauthlistconsumers-status-disabled": "ußjeschalldt",
+ "mwoauthlistconsumers-status-rejected": "affjelehnt",
+ "mwoauthlistconsumers-status-expired": "afjeloufe",
+ "mwoauthmanagemygrants-user": "Der Verlaach:",
+ "mwoauthmanagemygrants-wikiallowed": "Em Projäk zohjelohße:",
+ "mwoauthmanagemygrants-review": "der Zohang verwallde",
+ "mwoauthmanagemygrants-revoke": "der Zohang ophävve",
+ "mwoauthmanagemygrants-grantaccept": "Zohjelohze",
+ "mwoauthmanagemygrants-action": "Der Zohschtand ändere:",
+ "mwoauthdatastore-bad-source-ip": "Dä Wonsch kohm vun ene onjöltejje <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß",
+ "mwoauth-form-description-allwikis": "Daach $1,\n\nÖm Dinge Opdraacht fähdesch ze maache, moß '''$2''' en Älaupneß han, en Dingem Nahme op alle Projäkte heh en Dingem Nahme dat heh all ze donn:\n\n$4",
+ "mwoauth-form-description-onewiki": "Daach $1,\n\nÖm Dinge Opdraacht fähdesch ze maache, moß '''$2''' en Älaupneß han, en Dingem Nahme op Dahte op ''$4'' dat heh all ze donn:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Daach $1,\n\nÖm Dinge Opdraacht fähdesch ze maache, moß '''$2''' en Älaupneß han, op alle Projäkte vun heh en Dingem Nahme zohzejriefe. Et weed nix verändert onger Dingem Nahme.",
+ "mwoauth-form-description-onewiki-nogrants": "Tach $1,\n\nÖm Dinge Opdraacht fähdesch ze maache, moß '''$2''' en Älaupneß han, op Dahte op ''$4'' en Dingem Nahme zohzejriefe. Et weed nix verändert onger Dingem Nahme.",
+ "mwoauth-form-button-approve": "Zohlohße",
+ "mwoauth-form-button-cancel": "Ophühre",
+ "mwoauth-grants-heading": "De jewönschte Rääschte:",
+ "right-mwoauthviewprivate": "Prevahte Dahte vum <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"an open standard for authorization\">OAuth</i> belohre",
+ "mwoauth-tag-reserved": "Makehronge met <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">OAuth CID:</code> am Aanfang sin för <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"an open standard for authorization\">OAuth</i> resärvehrt."
+}
diff --git a/OAuth/i18n/ku-latn.json b/OAuth/i18n/ku-latn.json
new file mode 100644
index 00000000..b17e66a6
--- /dev/null
+++ b/OAuth/i18n/ku-latn.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "George Animal"
+ ]
+ },
+ "notification-oauth-app-approve-primary-link": "Sepanê bibîne",
+ "notification-oauth-app-reject-primary-link": "Sepanê bibîne",
+ "notification-oauth-app-disable-primary-link": "Sepanê bibîne",
+ "notification-oauth-app-reenable-primary-link": "Sepanê bibîne",
+ "notification-oauth-app-body": "Sedem: $1"
+}
diff --git a/OAuth/i18n/lb.json b/OAuth/i18n/lb.json
new file mode 100644
index 00000000..3054d422
--- /dev/null
+++ b/OAuth/i18n/lb.json
@@ -0,0 +1,132 @@
+{
+ "@metadata": {
+ "authors": [
+ "Les Meloures",
+ "Macofe",
+ "Robby",
+ "Soued031"
+ ]
+ },
+ "oauth": "OAuth",
+ "mwoauth-desc": "Erlaabt de Gebrauch vun OAuth 1.0a an OAuth 2.0 fir API Autorisatioun",
+ "mwoauth-verified": "D'Applikatioun ass elo berechtegt op MediaWiki an Ärem Numm zouzegräifen.\n\nFir de Prozess ofzeschléissen, gitt wgl. dëse Kontrollwäert un d'Applatioun virun: '''$1'''",
+ "mwoauth-db-readonly": "D'OAuth-Datebank ass temporär gespaart. Probéiert wgl. an e puer Minutte nach eng Kéier.",
+ "mwoauth-missing-field": "De Wäert fir d'Feld \"$1\" feelt",
+ "mwoauth-invalid-field": "Net valabele Wäert fir ''$1'' uginn",
+ "mwoauth-invalid-field-generic": "Net valabele Wäert uginn",
+ "mwoauth-field-hidden": "(dës Informatioun ass verstoppt)",
+ "mwoauth-field-private": "(dës Informatioun ass privat)",
+ "mwoauth-prefs-managegrants": "Verbonnen Applicatiounen:",
+ "mwoauth-prefs-managegrantslink": "$1 Verbonnen {{PLURAL:$1|$1 Verbonnen Applicatioun|$1 Verbonnen Applicatioune|0=verbonnen Applicatioune}} geréieren",
+ "mwoauth-consumer-allwikis": "All Projeten op dësem Site",
+ "mwoauth-consumer-name": "Numm vun der Applicatioun:",
+ "mwoauth-consumer-stage": "Aktuelle Status:",
+ "mwoauth-consumer-email": "Kontakt-E-Mail-Adress:",
+ "mwoauth-consumer-owner-only-label": "Besëtzer-nëmmen:",
+ "mwoauth-consumer-description": "Beschreiwung vum Programm:",
+ "mwoauth-consumer-wiki-thiswiki": "Aktuelle Projet ($1)",
+ "mwoauth-consumer-reason": "Grond:",
+ "mwoauth-consumer-email-unconfirmed": "D'E-Mail-Adress vun Ärem Benotzerkont gouf nach net confirméiert.",
+ "mwoauth-consumer-not-disabled": "De Konsument ass elo net desaktivéiert",
+ "mwoauth-invalid-consumer-key": "Et gëtt kee Konsument mat dem Schlëssel deen ugi gouf.",
+ "mwoauth-consumer-stage-proposed": "geplangt",
+ "mwoauth-consumer-stage-rejected": "refuséiert",
+ "mwoauth-consumer-stage-expired": "ofgelaf",
+ "mwoauth-consumer-stage-disabled": "desaktivéiert",
+ "mwoauthconsumerregistration-navigation": "Navigatioun:",
+ "mwoauthconsumerregistration-main": "Haaptsäit",
+ "mwoauthconsumerregistration-update-submit": "Konsument aktualiséieren",
+ "mwoauthconsumerregistration-name": "Konsument",
+ "mwoauthconsumerregistration-description": "Beschreiwung",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Lescht Ännerung",
+ "mwoauthconsumerregistration-manage": "geréieren",
+ "mwoauthconsumerregistration-resetsecretkey": "De Geheimschlëssel op en neie Wäert setzen",
+ "mwoauthmanageconsumers-notloggedin": "Dir musst ageloggt si fir op dës Säit ze kommen.",
+ "mwoauthmanageconsumers-showproposed": "Proposéiert Ufroen",
+ "mwoauthmanageconsumers-showrejected": "Refuséiert Ufroen",
+ "mwoauthmanageconsumers-showexpired": "Ofgelafen Ufroen",
+ "mwoauthmanageconsumers-linkproposed": "proposéiert Ufroen",
+ "mwoauthmanageconsumers-linkrejected": "refuséiert Ufroen",
+ "mwoauthmanageconsumers-linkexpired": "ofgelafen Ufroen",
+ "mwoauthmanageconsumers-linkapproved": "akzeptéiert Ufroen",
+ "mwoauthmanageconsumers-linkdisabled": "desaktivéiert Ufroen",
+ "mwoauthmanageconsumers-main": "Haapt",
+ "mwoauthmanageconsumers-name": "Konsument",
+ "mwoauthmanageconsumers-user": "Editeur",
+ "mwoauthmanageconsumers-description": "Beschreiwung",
+ "mwoauthmanageconsumers-email": "Kontakt-E-Mail",
+ "mwoauthmanageconsumers-lastchange": "Lescht Ännerung",
+ "mwoauthmanageconsumers-review": "nokucken/geréieren",
+ "mwoauthmanageconsumers-reject": "Refuséiert",
+ "mwoauthmanageconsumers-disable": "Desaktivéiert",
+ "mwoauthmanageconsumers-reenable": "Akzeptéiert",
+ "mwoauthmanageconsumers-reason": "Grond:",
+ "mwoauthmanageconsumers-success-approved": "Ufro gouf ugeholl.",
+ "mwoauthmanageconsumers-success-rejected": "Ufro gouf refuséiert.",
+ "oauthlistconsumers": "OAuth-Programmer opzielen",
+ "mwoauthlistconsumers-view": "Detailer",
+ "mwoauthlistconsumers-name": "Numm vun der Applicatioun",
+ "mwoauthlistconsumers-description": "Beschreiwung",
+ "mwoauthlistconsumers-wiki": "Applicabele Projet",
+ "mwoauthlistconsumers-status-proposed": "proposéiert",
+ "mwoauthlistconsumers-status-disabled": "desaktivéiert",
+ "mwoauthlistconsumers-status-expired": "ofgelaf",
+ "mwoauthlistconsumers-navigation": "Navigatioun:",
+ "mwoauthlistconsumers-grants-link": "Rechter geréieren",
+ "oauthmanagemygrants": "Verbonnen Applicatioune geréieren",
+ "mwoauthmanagemygrants-navigation": "Navigatioun:",
+ "mwoauthmanagemygrants-description": "Beschreiwung",
+ "mwoauthmanagemygrants-wikiallowed": "Um Projet erlaabt:",
+ "mwoauthmanagemygrants-grantaccept": "Accordéiert",
+ "mwoauthmanagemygrants-update": "Rechter aktualiséieren",
+ "mwoauthmanagemygrants-renounce": "Autorisatioun ewechhuelen",
+ "mwoauthmanagemygrants-action": "Status änneren:",
+ "mwoauthmanagemygrants-success-update": "Är Astellunge fir dës Applikatioun goufen aktualiséiert.",
+ "mwoauthmanagemygrants-success-renounce": "Den Accès vun der Applikatioun op Är Benotzerkont gouf ewechgeholl.",
+ "mwoauthdatastore-request-token-not-found": "Leider ass eppes schif gaange beim Verbanne mat dëser Applikatioun.\nGitt zréck a probéiert !Are Benotzerkont nach eng Kéier ze verbannen oder kontaktéiert den Auteur vun der Applikatioun.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth Token net fonnt, $1</span>",
+ "mwoauthdatastore-bad-token": "Et gouf keen Token fonnt deen op Är Ufro passt.",
+ "mwoauthdatastore-bad-source-ip": "D'Ufro koum vun enger net autoriséierter IP-Adress.",
+ "mwoauthgrants-general-error": "An Ärer OAuth-Ufro war e Feeler.",
+ "mwoauthserver-invalid-request-token": "Net valabelen Token an Ärer Ufro.",
+ "mwoauth-invalid-authorization-title": "OAuth Autorisatioun's-Feeler",
+ "mwoauth-invalid-authorization-blocked-user": "D'Autorisatiounen an Ärer Ufro si fir ee Benotzer dee gespaart ass",
+ "mwoauth-form-description-allwikis": "Bonjour $1,\n\nFir Är Ufro ofzeschléissen, brauch '''$2''' d'Autorisatioun fir dës Aktiounen an Ärem Numm op alle Projete vun dësem Site ze maachen:\n\n$4",
+ "mwoauth-form-description-onewiki": "Bonjour $1,\n\nFir Är Ufro ofzeschléisse brauch '''$2''' d'Autorisatioun fir dës Aktiounen an Ärem Numm op ''$4'' ze maachen:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Bonjour $1,\n\nFir Är Ufro ofzeschléissen, brauch '''$2''' d'Autorisatioun fir dës Aktiounen an Ärem Numm op alle Projete vun dësem Site ze maachen. Et gi keng Ännerungen un Ärem Benotzerkont gemaach.",
+ "mwoauth-form-description-onewiki-nogrants": "Bonjour $1,\n\nFir Är Ufro ofzeschléissen, brauch '''$2''' d'Autorisatioun fir op Informatiounen op '''$4''' an Ärem Numm zouzegräifen. Et gi keng Ännerungen un Ärem Benotzerkont gemaach.",
+ "mwoauth-form-description-allwikis-privateinfo": "Bonjour $1,\n\nFir Är Ufro ofzeschléissen, brauch '''$2''' d'Autorisatioun fir op Informatiounen iwwer Iech, inklusiv Äre richtegen Numm an Är E-Mail-Adress, op alle Projete vun dësem Site, zouzegräifen. Et gi keng Ännerungen un Ärem Benotzerkont gemaach",
+ "mwoauth-form-button-approve": "Erlaben",
+ "mwoauth-form-button-cancel": "Ofbriechen",
+ "mwoauth-error": "Verbinndungsfeeler vun der Software",
+ "mwoauth-grants-heading": "Ugefroten Autorisatiounen:",
+ "grant-mwoauth-authonly": "Nëmmen Iwwerpréiwung vun der Benotzer-Identitéit, keng Méiglechkeet fir Säiten ze liesen oder aplaz vum Benotzer z'agéieren.",
+ "mwoauth-oauth-exception": "Am OAuth-Protokoll ass e Feeler geschitt: $1",
+ "mwoauth-api-module-disabled": "De Modul \"$1\" ass net iwwer OAuth disponibel.",
+ "echo-category-title-oauth-owner": "OAuth-Entwécklung",
+ "echo-pref-tooltip-oauth-owner": "Mech iwwer Evenementer a Relatioun mat OAuth-Appen, déi ech ugeluecht hunn, informéieren.",
+ "echo-category-title-oauth-admin": "OAuth Gestioun",
+ "echo-pref-tooltip-oauth-admin": "Mech informéiere iwwer Evenementer a Verbindung mam Nokucke vun OAuth-Applikatiounen.",
+ "notification-oauth-app-propose-title": "$1 huet déi nei OAuth-App $2 {{GENDER:$1|proposéiert}}.",
+ "notification-oauth-app-update-title": "$1 huet d'Auth App $2 {{GENDER:$1|aktualiséiert}}",
+ "notification-oauth-app-approve-title": "$1 huet {{GENDER:$3|Är}} OAuth-App($2) {{GENDER:$1|approuvéiert}}",
+ "notification-oauth-app-reject-title": "$1 huet {{GENDER:$3|Är}} OAuth-App ($2) {{GENDER:$1|refuséiert}}",
+ "notification-oauth-app-disable-title": "$1 huet {{GENDER:$3|Är}} OAuth App ($2) {{GENDER:$1|desaktivéiert}}",
+ "notification-oauth-app-reenable-title": "$1 huet {{GENDER:$3|Är}} OAuth-App ($2) {{GENDER:$1|reaktivéiert}}",
+ "notification-oauth-app-propose-subject": "$1 huet eng nei OAuth-App op {{SITENAME}} {{GENDER:$1|proposéiert}}",
+ "notification-oauth-app-update-subject": "$1 huet eng OAuth-App op {{SITENAME}} {{GENDER:$1|aktualiséiert}}",
+ "notification-oauth-app-approve-subject": "$1 huet {{GENDER:$3|Är}} OAuth-App op {{SITENAME}} {{GENDER:$1|approuvéiert}}",
+ "notification-oauth-app-reject-subject": "$1 huet {{GENDER:$3|Är}} OAuth-App op {{SITENAME}} {{GENDER:$1|refuséiert}}",
+ "notification-oauth-app-disable-subject": "$1 huet {{GENDER:$3|Är}} OAuth-App op {{SITENAME}} {{GENDER:$1|desaktivéiert}}",
+ "notification-oauth-app-reenable-subject": "$1 huet {{GENDER:$3|Är}} OAuth-App op {{SITENAME}} {{GENDER:$1|reeaktivéiert}}",
+ "notification-oauth-app-propose-primary-link": "App nokucken",
+ "notification-oauth-app-update-primary-link": "App nokucken",
+ "notification-oauth-app-approve-primary-link": "App weisen",
+ "notification-oauth-app-reject-primary-link": "App weisen",
+ "notification-oauth-app-disable-primary-link": "App weisen",
+ "notification-oauth-app-reenable-primary-link": "App weisen",
+ "notification-oauth-app-body": "Grond: $1",
+ "mwoauth-oauth2-granttype-auth-code": "Autorisatiouns-Code",
+ "mwoauthconsumer-consumer-view": "Dëse Konsument weisen",
+ "mwoauthconsumer-application-view": "Dës Applikatioun weisen"
+}
diff --git a/OAuth/i18n/lfn.json b/OAuth/i18n/lfn.json
new file mode 100644
index 00000000..5001c8a9
--- /dev/null
+++ b/OAuth/i18n/lfn.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mafcadio",
+ "Robin van der Vliet"
+ ]
+ },
+ "mwoauthconsumerregistration-description": "Descrive",
+ "mwoauthmanageconsumers-description": "Descrive",
+ "oauthlistconsumers": "Lista de apes de OAuth",
+ "mwoauthlistconsumers-description": "Descrive",
+ "oauthmanagemygrants": "Maneja apes liada",
+ "mwoauthmanagemygrants-description": "Descrive"
+}
diff --git a/OAuth/i18n/lij.json b/OAuth/i18n/lij.json
new file mode 100644
index 00000000..7ffef637
--- /dev/null
+++ b/OAuth/i18n/lij.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Giromin Cangiaxo"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Apricaçioin conligæ:",
+ "mwoauth-prefs-managegrantslink": "Gestisci $1 {{PLURAL:$1|apricaçion conligâ|apricaçioin conligæ}}",
+ "oauthlistconsumers": "Lista di appricaçioin OAuth",
+ "oauthmanagemygrants": "Gestion di apricaçioin conesse"
+}
diff --git a/OAuth/i18n/lki.json b/OAuth/i18n/lki.json
new file mode 100644
index 00000000..ddf7c5f8
--- /dev/null
+++ b/OAuth/i18n/lki.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hosseinblue",
+ "Lakzon"
+ ]
+ },
+ "mwoauth-verified": "این برنامه اجازهٔ دسترسی به مدیاویکی از طرف شما را دارد.\n\nبرای تکمیل این فرایند، مقدار تأیید را برای برنامه فراهم کنید: '''$1'''",
+ "mwoauth-consumer-description": "توضیحات نرم‌افزار:",
+ "mwoauthconsumerregistration-description": "توضیحۀل",
+ "mwoauthmanageconsumers-review": "دووارە دئین /مدیریەت",
+ "mwoauthlistconsumers-description": "توضیحةل"
+}
diff --git a/OAuth/i18n/lld.json b/OAuth/i18n/lld.json
new file mode 100644
index 00000000..071194cf
--- /dev/null
+++ b/OAuth/i18n/lld.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Starladin"
+ ]
+ },
+ "mwoauth-oauth2-granttype-auth-code": "Codesc d'autorisaziun",
+ "mwoauth-oauth2-granttype-refresh-token": "Ciaria danü le simbol",
+ "mwoauth-oauth2-granttype-client-credentials": "Dac dl cliënt",
+ "mwoauth-oauth2-invalid-access-token": "Simbol d'azes che ne vel nia"
+}
diff --git a/OAuth/i18n/lt.json b/OAuth/i18n/lt.json
new file mode 100644
index 00000000..f043728a
--- /dev/null
+++ b/OAuth/i18n/lt.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Eitvys200",
+ "Homo",
+ "Hugo.arg",
+ "Manvydasz"
+ ]
+ },
+ "mwoauth-desc": "Leidžia naudoti OAuth 1.0a API autorizacijai",
+ "mwoauth-prefs-managegrants": "Susieti programiniai įskiepiai:",
+ "mwoauth-prefs-managegrantslink": "Tvarkyti $1 {{PLURAL:$1|susietą programinį įskiepį|susietus programinius įskiepius|susietų programinių įskiepių}}",
+ "oauthmanagemygrants": "Tvarkyti susietas programas",
+ "mwoauth-form-description-allwikis-nogrants": "Sveiki $1,\n\nNorėdami įvykdyti jūsų prašymą, '''$2''' reikia leidimo pasiekti informaciją visuose šio puslapio projektuose jūsų vardu. Tai nepaveiks jūsų paskyros.",
+ "echo-pref-tooltip-oauth-owner": "Pranešti man apie įvykius, susijusius su OAuth paraiškomis, kurias aš sukūriau.",
+ "echo-pref-tooltip-oauth-admin": "Pranešti man apie įvykius, susijusius su OAuth paraiškų peržiūromis.",
+ "notification-oauth-app-body": "Priežastis: $1"
+}
diff --git a/OAuth/i18n/lv.json b/OAuth/i18n/lv.json
new file mode 100644
index 00000000..46eec524
--- /dev/null
+++ b/OAuth/i18n/lv.json
@@ -0,0 +1,47 @@
+{
+ "@metadata": {
+ "authors": [
+ "Edgars2007",
+ "Papuass",
+ "Silraks"
+ ]
+ },
+ "mwoauth-desc": "Ļauj izmantot OAuth 1.0a un OAuth 2.0 API autorizācijai",
+ "mwoauth-prefs-managegrants": "Piesaistītās lietotnes:",
+ "mwoauth-prefs-managegrantslink": "Pārvaldīt $1 {{PLURAL:$1|piesaistīto lietotni|piesaistītās lietotnes}}",
+ "mwoauth-consumer-description": "Lietotnes apraksts:",
+ "mwoauth-consumer-accesstoken": "Piekļuves pilnvara:",
+ "mwoauth-consumer-reason": "Iemesls:",
+ "mwoauthconsumerregistration-navigation": "Navigācija:",
+ "mwoauthconsumerregistration-user": "Izdevējs",
+ "mwoauthconsumerregistration-stage": "Statuss",
+ "mwoauthconsumerregistration-lastchange": "Pēdējā izmaiņa",
+ "mwoauthmanageconsumers-user": "Izdevējs",
+ "oauthlistconsumers": "OAuth lietotņu saraksts",
+ "mwoauthlistconsumers-legend": "Pārlūkot OAuth lietotnes",
+ "mwoauthlistconsumers-name": "Lietotnes nosaukums:",
+ "mwoauthlistconsumers-user": "Izdevējs",
+ "mwoauthlistconsumers-navigation": "Navigācija:",
+ "oauthmanagemygrants": "Pārvaldīt piesaistītās lietotnes",
+ "mwoauthmanagemygrants-navigation": "Navigācija:",
+ "mwoauthmanagemygrants-none": "Tavam kontam nav piesaistītu lietotņu.",
+ "mwoauthmanagemygrants-user": "Izdevējs:",
+ "mwoauthmanagemygrants-wikiallowed": "Atļauts projektā:",
+ "mwoauthmanagemygrants-confirm-legend": "Pārvaldīt piesaistīto lietotni",
+ "mwoauthserver-invalid-request-token": "Nederīgs pieprasījuma kods.",
+ "mwoauth-invalid-authorization-title": "OAuth autorizācijas kļūda",
+ "mwoauth-form-description-allwikis": "Sveicināts, $1,\n\nPieprasījuma pabeigšanai '''$2''' nepieciešama atļauja, lai tavā vārdā veiktu šādas darbības visos šīs vietnes projektos:\n\n$4",
+ "mwoauth-form-description-onewiki": "Sveicināts, $1,\n\nPieprasījuma pabeigšanai '''$2''' nepieciešama atļauja, lai tavā vārdā veiktu šādas darbības ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Sveicināts, $1,\n\nPieprasījuma pabeigšanai '''$2''' nepieciešama atļauja, lai tavā vārdā piekļūtu informācijai visos šīs vietnes projektos. Tavā kontā netiks veiktas nekādas izmaiņas.",
+ "mwoauth-form-description-onewiki-nogrants": "Sveicināts, $1,\n\nPieprasījuma pabeigšanai '''$2''' nepieciešama atļauja, lai tavā vārdā piekļūtu informācijai ''$4''. Tavā kontā netiks veiktas nekādas izmaiņas.",
+ "mwoauth-form-description-allwikis-privateinfo": "Sveicināts, $1,\n\nPieprasījuma pabeigšanai '''$2''' nepieciešama atļauja, lai piekļūtu informācijai par tevi, ieskaitot tavu īsto vārdu un e-pasta adresi, visos šīs vietnes projektos. Tavā kontā netiks veiktas nekādas izmaiņas.",
+ "mwoauth-form-description-onewiki-privateinfo": "Sveicināts, $1,\n\nPieprasījuma pabeigšanai '''$2''' nepieciešama atļauja, lai piekļūtu informācijai, ieskaitot tavu īsto vārdu un e-pasta adresi, ''$4''. Tavā kontā netiks veiktas nekādas izmaiņas.",
+ "mwoauth-form-button-approve": "Atļaut",
+ "mwoauth-form-button-cancel": "Atcelt",
+ "mwoauth-error": "Lietotnes savienojuma kļūda",
+ "mwoauth-grants-heading": "Pieprasītās atļaujas:",
+ "mwoauth-grants-nogrants": "Lietotne nav pieprasījusi nekādas atļaujas.",
+ "mwoauth-acceptance-cancelled": "Tu izvēlējies neļaut \"$1\" piekļūt tavam kontam. \"$1\" nestrādās, ja vien tai neatļausi piekļuvi. Tu vari doties atpakaļ uz \"$1\" vai [[Special:OAuthManageMyGrants|pārvaldīt]] savas pievienotās lietotnes.",
+ "notification-oauth-app-body": "Iemesls: $1",
+ "mwoauthconsumer-application-view": "Skatīt šo pieteikumu"
+}
diff --git a/OAuth/i18n/lzh.json b/OAuth/i18n/lzh.json
new file mode 100644
index 00000000..c1e7bd82
--- /dev/null
+++ b/OAuth/i18n/lzh.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "灰太狼Wolffy55"
+ ]
+ },
+ "mwoauth-tag-reserved": "箋始于<code> OAuth之CID:</ code>为过OAuth之用而留焉。"
+}
diff --git a/OAuth/i18n/min.json b/OAuth/i18n/min.json
new file mode 100644
index 00000000..c15d8e39
--- /dev/null
+++ b/OAuth/i18n/min.json
@@ -0,0 +1,30 @@
+{
+ "@metadata": {
+ "authors": [
+ "Iwan Novirion"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplikasi tahubuang:",
+ "mwoauth-prefs-managegrantslink": "Atua $1 {{PLURAL:$1|aplikasi}} tahubuang",
+ "mwoauth-consumer-name": "Namo aplikasi:",
+ "mwoauth-consumer-version": "Versi konsumen:",
+ "mwoauth-consumer-user": "Pangambang:",
+ "mwoauth-consumer-stage": "Status kini ko:",
+ "mwoauth-consumer-email": "Alamaik surel:",
+ "mwoauth-consumer-reason": "Alasan:",
+ "mwoauthconsumerregistration-navigation": "Navigasi:",
+ "mwoauthconsumerregistration-main": "Utamo",
+ "mwoauthconsumerregistration-name": "Konsumen",
+ "mwoauthconsumerregistration-user": "Panerbit",
+ "mwoauthconsumerregistration-description": "Katarangan",
+ "mwoauthconsumerregistration-email": "Kontak surel",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-manage": "atua",
+ "mwoauthmanageconsumers-main": "Utamo",
+ "mwoauthmanageconsumers-name": "Konsumen",
+ "mwoauthmanageconsumers-user": "Panerbit",
+ "mwoauthmanageconsumers-description": "Katarangan",
+ "mwoauthmanageconsumers-email": "Kontak surel",
+ "oauthmanagemygrants": "Atua aplikasi tahubuang",
+ "mwoauthmanagemygrants-confirm-legend": "Atua aplikasi tahubuang"
+}
diff --git a/OAuth/i18n/mk.json b/OAuth/i18n/mk.json
new file mode 100644
index 00000000..fefa94bb
--- /dev/null
+++ b/OAuth/i18n/mk.json
@@ -0,0 +1,329 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bjankuloski06",
+ "Macofe",
+ "Vlad5250"
+ ]
+ },
+ "mwoauth-desc": "Овозможува употреба на OAuth 1.0a и OAuth 2.0 за заверка на приложници",
+ "mwoauth-nosubpage-explanation": "OAuth е механизам кој им овозможува на надворешните прилози да распознаат корисник на {{SITENAME}} или да делуваат во негово име, откако ќе добијат дозвола од него.\n\nЗа оваа страница да работи, се бараат уште параметри. Ако сте тука преку надворешен прилог, тоа веројатно се случило поради грешка во прилогот; ќе треба да го контактирате авторот.",
+ "mwoauth-verified": "На извршникот не му е дозволен пристап до МедијаВики во ваше име.\n\nЗа да ја довршите постапката, на извршникот укажете му ја следнава контролна вредност: '''$1'''",
+ "mwoauth-db-readonly": "Базата на OAuth е привремено заклучена. Обидете се пак за некоја минута.",
+ "mwoauth-missing-field": "Недостасува вредност во полето „$1“",
+ "mwoauth-invalid-field": "Во полето „$1“ е зададена неважечка вредност",
+ "mwoauth-invalid-field-generic": "Укажана е неважечка вредност",
+ "mwoauth-field-hidden": "(оваа информација е скриена)",
+ "mwoauth-field-private": "(оваа информација е доверлива)",
+ "mwoauth-prefs-managegrants": "Поврзани извршници:",
+ "mwoauth-prefs-managegrantslink": "Раководење со {{PLURAL:$1|$1 поврзан извршник|$1 поврзани извршници|0=поврзани извршници}}",
+ "mwoauth-consumer-allwikis": "Сите проекти на ова мрежно место",
+ "mwoauth-consumer-key": "Потрошувачки клуч:",
+ "mwoauth-consumer-name": "Назив на извршникот:",
+ "mwoauth-consumer-version": "Потрошувачка верзија:",
+ "mwoauth-consumer-user": "Издавач:",
+ "mwoauth-consumer-stage": "Тековен статус:",
+ "mwoauth-consumer-email": "Е-пошта за контакт:",
+ "mwoauth-consumer-email-help": "Видливо само за оние што одобруваат нови потрошувачи",
+ "mwoauth-consumer-owner-only-label": "Само сопственик:",
+ "mwoauth-consumer-owner-only": "Овој потрошувач е само за употреба на $1.",
+ "mwoauth-consumer-owner-only-help": "Со оваа можност, потрошувачот ќе биде автоматски одобрен и прифатин за употреба од $1. Нема да може да го користи било кој друг корисник, и нема да работи вообичаениот тек на овластување. Дејствата на овој потрошувач нема да се означуваат.",
+ "mwoauth-consumer-description": "Опис на извршникот:",
+ "mwoauth-consumer-callbackurl": "URL-адреса за повикување на OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Овозможи му на потрошувачот да укаже повратен повик во барањата и погоре да користи URL за повратен повик како задолжителна претставка.",
+ "mwoauth-consumer-granttypes": "Видови доделувања што се бараат:",
+ "mwoauth-consumer-grantsneeded": "Применливи доделувања:",
+ "mwoauth-consumer-required-grant": "Применлив потрошувач",
+ "mwoauth-consumer-wiki": "Применлив проект:",
+ "mwoauth-consumer-wiki-thiswiki": "Тековен проект ($1)",
+ "mwoauth-consumer-restrictions": "Ограничувања на употребата:",
+ "mwoauth-consumer-restrictions-json": "Ограничувања на употребата (JSON):",
+ "mwoauth-consumer-rsakey": "Јавен RSA-клуч (незадол.):",
+ "mwoauth-consumer-rsakey-help": "Внесете јавен клуч за да го употребите потписниот метод RSA-SHA1. Оставете го празно за да се послужите со произволната тајна HMAC-SHA1. Ако не знаете кое да го употребите, оставете го ова празно.",
+ "mwoauth-consumer-secretkey": "Тајна потрошувачка шифра:",
+ "mwoauth-consumer-accesstoken": "Пристапна шифра:",
+ "mwoauth-consumer-reason": "Причина:",
+ "mwoauth-consumer-developer-agreement": "Поднесувајќи го овој извршник, прифаќате дека го задржуваме правото да го исклучиме, да Ве отстраниме Вашиот пристап или пристапот на извршникот до ова мрежно место и да поведеме други дејства што кои сметаме за неопходни доколку сметаме, по изклучиво свое наоѓање, дека Вие или Вашиот извршник кршите некои правило, напатствие и начело на мрежново место. Можеме да ги смениме овие Правила за извршници во било кое време без претходна најава, по исклучиво наше наоѓање, според она што го сметаме за потребно. Вашето понатамошно користење на OAuth ќе значи прифаќање на тие измени.",
+ "mwoauth-consumer-email-unconfirmed": "Вашата е-поштенска адреса сè уште не е потврдена.",
+ "mwoauth-consumer-email-mismatched": "Укажаната е-пошта мора да одговара на онаа во сметката.",
+ "mwoauth-consumer-alreadyexists": "Веќе постои потрошувач со ваква комбинација од име/верзија/издавач",
+ "mwoauth-consumer-alreadyexistsversion": "Веќе постои потрошувач со оваа комбинација на име/издавач со еднаква или повисока верзија („$1“)",
+ "mwoauth-consumer-not-accepted": "Не можам да ги изменам информациите за потрошувачко барање во исчекување",
+ "mwoauth-consumer-not-proposed": "Потрошувачот во моментов не е предложен",
+ "mwoauth-consumer-not-disabled": "Потрошувачот во моментов не е оневозможен",
+ "mwoauth-consumer-not-approved": "Потрошувачот не е одобрен (може да е оневозможен)",
+ "mwoauth-missing-consumer-key": "Нема укажано потрошувачки клуч.",
+ "mwoauth-invalid-consumer-key": "Не постои потрошувач со таков клуч.",
+ "mwoauth-invalid-access-token": "Не постои пристапна шифра со таков клуч.",
+ "mwoauth-invalid-access-wrongwiki": "Потрошувачот може да се користи само на проектот „$1“.",
+ "mwoauth-consumer-conflict": "Некои ги изменил атрибутети на овој потрошувач додека го разгледувавте. Обидете се повторно. Може да го погледате и дневникот на измени.",
+ "mwoauth-consumer-grantshelp": "Секое доделување дава пристап до список до наведени права што веќе ги има корисничката сметка. Повеќе ќе најдете на [[Special:ListGrants|табелата со доделувања]].",
+ "mwoauth-consumer-stage-proposed": "предложен",
+ "mwoauth-consumer-stage-rejected": "одбиен",
+ "mwoauth-consumer-stage-expired": "истечен",
+ "mwoauth-consumer-stage-approved": "одобрен",
+ "mwoauth-consumer-stage-disabled": "оневозможен",
+ "mwoauth-consumer-stage-suppressed": "притаен",
+ "oauthconsumerregistration": "Пријава на потрошувач на OAuth",
+ "mwoauthconsumerregistration-navigation": "Прегледник:",
+ "mwoauthconsumerregistration-propose": "Предложи нов потрошувач",
+ "mwoauthconsumerregistration-list": "Список на мои потрошувачи",
+ "mwoauthconsumerregistration-main": "Главна",
+ "mwoauthconsumerregistration-propose-text": "Програмерите треба да го користат образецов за предлагање на нов потрошувач на OAuth (повеќе ќе најдете во [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth документацијата на додатокот]). Откако ќе го поднесете образецот, ќе добиете шифра со која ќе се претставувате на МедијаВики. Администратор на OAuth ќе треба да ви ја одобри пријавата пред да можат да ја овластуваат корисниците.\n\nНеколку препораки и напомении:\n* Доделувајте што помалку одобренија. Одбегнувајте ги оние што не се потребни во моментов.\n* Верзиите се од обликот „главно.споредно.издание“ (последните две се незадолжителни) и зголемете и се зголемуваат со потребата за измени во доделувањата.\n* По можност, внесете јавен RSA-клуч (во форматот PEM); во спротивно (помалку безбедно) ќе ви зададеме таен клуч.\n* Можете да го ограничите потрошувачот на само еден проект на ова мрежно место, внесувајќи ја назнаката на проектот („*“ за сите проект).",
+ "mwoauthconsumerregistration-update-text": "Образецот подолу служи за менување на аспекти на потрошувачот на OAuth што се во ваша контрола.\n\nСите вредности тука ќе се презапишат врз претходните. Не оставајте празни полиња, освен ако не сакате да ги исчистите истите.",
+ "mwoauthconsumerregistration-maintext": "Оваа страница им овозможува на програмерите да предлагаат и подновуваат (менуваат) потрошувачки извршници за OAuth (погл. http://oauth.net) во регистарот на ова мрежно место.\n\nОд овде можете да: [[Special:OAuthConsumerRegistration/propose|предложите нов потрошувач]] или пак [[Special:OAuthConsumerRegistration/list|раководите со вашите постоечки потрошувачи]].\n\nПовеќе информации за OAuth ќе најдете во [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth документацијата на додатокот].",
+ "mwoauthconsumerregistration-propose-legend": "Нов кориснички извршник за OAuth",
+ "mwoauthconsumerregistration-update-legend": "Измена на кориснички извршник за OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Предложи потрошувач",
+ "mwoauthconsumerregistration-update-submit": "Измени потрошувач",
+ "mwoauthconsumerregistration-none": "Не контролирате ниеден потрошувач на OAuth.",
+ "mwoauthconsumerregistration-name": "Потрошувач",
+ "mwoauthconsumerregistration-user": "Издавач",
+ "mwoauthconsumerregistration-description": "Опис",
+ "mwoauthconsumerregistration-email": "Е-пошта за контакт",
+ "mwoauthconsumerregistration-consumerkey": "Потрошувачки клуч",
+ "mwoauthconsumerregistration-stage": "Статус",
+ "mwoauthconsumerregistration-lastchange": "Последна измена",
+ "mwoauthconsumerregistration-manage": "раководи",
+ "mwoauthconsumerregistration-resetsecretkey": "Дај нова вредност на тајниот клуч",
+ "mwoauthconsumerregistration-proposed": "Вашето потрошувачко барање за OAuth е примено.\n\nВашата потрошувачка шифра гласи '''$1''', а тајната шифра гласи '''$2'''. ''Зачувајте ги бидејќи може да ви затребаат во иднина.''",
+ "mwoauthconsumerregistration-created-owner-only": "Вашиот потрошувач на OAuth е создаден.\n\nВашите шифри се:\n; Потрошувачка шифра: $1\n; Потрошувачка тајна: $2\n; Пристапна шифра: $3\n; Пристапна тајна: $4\n<em>Зачувајте ги за идна консултација.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "Вашиот клиент на OAuth 2.0 е создаден.\n\nВашите шифри се:\n; Клуч на клиентскиот прилог: $1\n; Тајна на клиентскиот прилог: $2\n; Пристапна шифра: $3\n;<em>Зачувајте ги за да ви бидат при рака во иднина.</em>",
+ "mwoauthconsumerregistration-updated": "Вашиот потрошувачки регистар нa OAuth е изменет.",
+ "mwoauthconsumerregistration-secretreset": "Вашата тајна потрошувачка шифра гласи '''$1'''. ''Зачувајте ја бидејќи може да ви затреба во иднина.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Вашите потрошувачки шифри на OAuth се сменети. Еве ги новите:\n\n; Потрошувачка шифра: $1\n; Потрошувачка тајна: $2\n; Пристапна шифра: $3\n; Пристапна тајна: $4\n<em>Зачувајте ги за идна консултација.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Вашите потрошувачки шифри на OAuth 2.0 се пресоздадени. Новите шифри се:\n; Потрушувачка шифра: $1\n; Потрошувачка тајна: $2\n; Пристапна шифра: $3\n<em>Зачувајте ги за да ви бидат при рака во иднина.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Мора да ја потвтдите вашата е-пошта пред создавање на прилози со OAuth.\nЗадајте и потврдете ја е-поштата преку вашите [[Special:Preferences|кориснички нагодувања]].",
+ "oauthmanageconsumers": "Раководење со потрошувачи на OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Треба да сте најавени за да ја отворите страницата.",
+ "mwoauthmanageconsumers-type": "Редици:",
+ "mwoauthmanageconsumers-showproposed": "Предложени барања",
+ "mwoauthmanageconsumers-showrejected": "Одбиени барања",
+ "mwoauthmanageconsumers-showexpired": "Истечени барања",
+ "mwoauthmanageconsumers-linkproposed": "предложени барања",
+ "mwoauthmanageconsumers-linkrejected": "одбиени барања",
+ "mwoauthmanageconsumers-linkexpired": "истечени барања",
+ "mwoauthmanageconsumers-linkapproved": "одобрени барања",
+ "mwoauthmanageconsumers-linkdisabled": "оневозможени барања",
+ "mwoauthmanageconsumers-main": "Главна",
+ "mwoauthmanageconsumers-maintext": "Страницава е предвидена за работење со барања за кориснички извршници за OAuth (погл. http://oauth.net) и раководење со постоечките потрошувачи.",
+ "mwoauthmanageconsumers-queues": "Подолу изберете редица на потрочувачи за одобрување:",
+ "mwoauthmanageconsumers-q-proposed": "Редица на барања за предлагање на потрошувачи",
+ "mwoauthmanageconsumers-q-rejected": "Редица на одбиени потрошувачки барања",
+ "mwoauthmanageconsumers-q-expired": "Редица на истечени потрошувачки барања",
+ "mwoauthmanageconsumers-lists": "Подолу изберете потрошувачки статусен список:",
+ "mwoauthmanageconsumers-l-approved": "Список на моментално одобрени корисници",
+ "mwoauthmanageconsumers-l-disabled": "Список на моментално оневозможени потрошувачи",
+ "mwoauthmanageconsumers-none-proposed": "На списокот нема предложени потрошувачи.",
+ "mwoauthmanageconsumers-none-rejected": "На списокот нема предложени потрошувачи.",
+ "mwoauthmanageconsumers-none-expired": "На списокот нема предложени потрошувачи.",
+ "mwoauthmanageconsumers-none-approved": "Нема потрошувачи што одговараат на дадените услови.",
+ "mwoauthmanageconsumers-none-disabled": "Нема потрошувачи што одговараат на дадените услови.",
+ "mwoauthmanageconsumers-name": "Потрошувач",
+ "mwoauthmanageconsumers-user": "Издавач",
+ "mwoauthmanageconsumers-description": "Опис",
+ "mwoauthmanageconsumers-email": "Е-пошта за контакт",
+ "mwoauthmanageconsumers-consumerkey": "Потрошувачки клуч",
+ "mwoauthmanageconsumers-lastchange": "Последна измена",
+ "mwoauthmanageconsumers-review": "проверка/раководство",
+ "mwoauthmanageconsumers-confirm-text": "Образецов служи за одобрување, одбивање или преовозможување на корисникот.",
+ "mwoauthmanageconsumers-confirm-legend": "Раководење со потрошувач на OAuth",
+ "mwoauthmanageconsumers-action": "Статус на измената:",
+ "mwoauthmanageconsumers-approve": "Одобрен",
+ "mwoauthmanageconsumers-reject": "Одбиен",
+ "mwoauthmanageconsumers-rsuppress": "Одбиен и притаен",
+ "mwoauthmanageconsumers-disable": "Оневозможен",
+ "mwoauthmanageconsumers-dsuppress": "Оневозможен и притаен",
+ "mwoauthmanageconsumers-reenable": "Одобрено",
+ "mwoauthmanageconsumers-reason": "Причина:",
+ "mwoauthmanageconsumers-confirm-submit": "Измени потр. статус",
+ "mwoauthmanageconsumers-success-approved": "Барањето е одобрено.",
+ "mwoauthmanageconsumers-success-rejected": "Барањето е одбиено.",
+ "mwoauthmanageconsumers-success-disabled": "Потрошувачот е оневозможен.",
+ "mwoauthmanageconsumers-success-reanable": "Потрошувачот е преовозможен.",
+ "mwoauthmanageconsumers-search-name": "потрошувачи со ова име",
+ "mwoauthmanageconsumers-search-publisher": "потрошувачи од овој корисник",
+ "oauthlistconsumers": "Список на извршници на OAuth",
+ "mwoauthlistconsumers-legend": "Прелистај извршници на OAuth",
+ "mwoauthlistconsumers-view": "подробности",
+ "mwoauthlistconsumers-none": "Нема извршници што одговараат на дадените услови.",
+ "mwoauthlistconsumers-name": "Назив на извршникот",
+ "mwoauthlistconsumers-version": "Потрошувачка верзија",
+ "mwoauthlistconsumers-user": "Издавач",
+ "mwoauthlistconsumers-description": "Опис",
+ "mwoauthlistconsumers-wiki": "Применлив проект",
+ "mwoauthlistconsumers-callbackurl": "URL за повикување на OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Овозможи му на потрошувачот да укаже повратен повик во барањата и погоре да користи URL за повратен повик како задолжителна претставка.",
+ "mwoauthlistconsumers-grants": "Применливи доделувања",
+ "mwoauthlistconsumers-basicgrantsonly": "(само основен пристап)",
+ "mwoauthlistconsumers-status": "Статус",
+ "mwoauth-consumer-stage-any": "било кој",
+ "mwoauthlistconsumers-status-proposed": "предложен",
+ "mwoauthlistconsumers-status-approved": "одобрен",
+ "mwoauthlistconsumers-status-disabled": "оневозможен",
+ "mwoauthlistconsumers-status-rejected": "одбиен",
+ "mwoauthlistconsumers-status-expired": "истечен",
+ "mwoauthlistconsumers-navigation": "Прегледник:",
+ "mwoauthlistconsumers-update-link": "Поднови потрошувач",
+ "mwoauthlistconsumers-manage-link": "Управување со потрошувач",
+ "mwoauthlistconsumers-grants-link": "Управување со доделби",
+ "mwoauthlistconsumers-rclink": "Скорешни промени од овој прилог",
+ "oauthmanagemygrants": "Раководење со поврзани извршници",
+ "mwoauthmanagemygrants-text": "На страницава се наведени извршниците што можат да ја користат вашата сметка. Нивниот степен на пристап е ограничен од тоа што сте им дозволиле да прават кога сте им го одобриле пристапот. Ако на извршникот сте му дале посебно овластување за пристап на друг збратимен проект, тогаш подолу ќе ви се појават посебни поставки за секој одделен проект.\n\nПоврзаните извршници ја користат вашата сметка преку протокол на OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth?uselang=mk Дознајте повеќе за поврзаните извршници])</span>",
+ "mwoauthmanagemygrants-navigation": "Прегледник:",
+ "mwoauthmanagemygrants-showlist": "Список на поврзани извршници",
+ "mwoauthmanagemygrants-none": "Нема извршници поврзани со вашата сметка.",
+ "mwoauthmanagemygrants-user": "Издавач:",
+ "mwoauthmanagemygrants-description": "Опис",
+ "mwoauthmanagemygrants-wikiallowed": "Дозволен на проектот:",
+ "mwoauthmanagemygrants-grants": "Применливи доделувања",
+ "mwoauthmanagemygrants-grantsallowed": "Дозволени доделувања:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Дозволени применливи доделувања:",
+ "mwoauthmanagemygrants-review": "раковод. со пристап",
+ "mwoauthmanagemygrants-revoke": "одземи пристап",
+ "mwoauthmanagemygrants-grantaccept": "Доделено",
+ "mwoauthmanagemygrants-update-text": "Со овој образец можете да ги измените дозволите доделени на некој извршник за да делува во ваше име.",
+ "mwoauthmanagemygrants-revoke-text": "Со овој образец можете да му го одземете пристапот на некој извршник за да делува во ваше име.",
+ "mwoauthmanagemygrants-confirm-legend": "Раководење со поврзан извршник",
+ "mwoauthmanagemygrants-update": "Измени доделувања",
+ "mwoauthmanagemygrants-renounce": "Одземи дозвола",
+ "mwoauthmanagemygrants-action": "Смени статус:",
+ "mwoauthmanagemygrants-confirm-submit": "Измени статус на пристап. шифра",
+ "mwoauthmanagemygrants-success-update": "Вашите нагодувања за овој прилог се изменети.",
+ "mwoauthmanagemygrants-success-renounce": "Пристапот на прилогот до вашата сметка е отповикан.",
+ "mwoauthmanagemygrants-basic-tooltip": "Зошто не можам да го подновам ова доделување? Ова доделување му ги дава на поврзаниот извршник потребните дозволи за да работи како што треба. Ако не сакате да ги има тие права, треба да му го одземете пристапот.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Зошто не можам да го подновам доделувањево? Доколку не сакате овој сврзан извршник да го има ова право, треба да му го одземете пристапот.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Вашите}} уредувања од овој прилог",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Ваѓите}} дејства од овој прилог",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|предложи}} потрошувач на OAuth (потрошувачки клуч $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|измени}} потрошувач на OAuth (потрошувачки клуч $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|одобри}} потрошувач на OAuth со $3 (потрошувачки клуч $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|одби}} потрошувач на OAuth со $3 (потрошувачки клуч $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|оневозможи}} потрошувач на OAuth со $3 (потрошувачки клуч $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|преовозможи}} потрошувач на OAuth со $3 (потрошувачки клуч $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|создаде}} OAuth-потршувач наменет само за сопственикот (потрошувачки клуч $4)",
+ "log-action-filter-mwoauthconsumer": "Вид потрошувачко дејство со OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Потрошувачко одобрување со OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Создавање на потрошувачи со OAuth само за сопственикот",
+ "log-action-filter-mwoauthconsumer-disable": "Потрошувачко оневозможување со OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Потрошувачки предлог со OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Потрошувачко преовозможување со OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Потрошувачко одбивање со OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Потрошувачка поднова со OAuth",
+ "mwoauthconsumer-consumer-logpage": "Потрошувачки дневник за OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Дневник на одобрувања, одбивања и оневозможувања на зачленети потрошувачи на OAuth.",
+ "mwoauth-bad-request-missing-params": "За жал, се јави проблем при поставувањето на извршникот. Обратете се на <span class=\"plainlinks\">[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth местото за поддршка]</span> за да дознаете како да го решите.\n\n<span class=\"plainlinks mw-mwoautherror-details\">На OAuth му недостасуваат параметри, $1</span>",
+ "mwoauth-bad-request-invalid-action": "За жал, се јави проблем при поставувањето на извршнико. Обратете се кај неговиот автор за да го решите.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Непозната URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "За жал, се јави некаков проблем. Ќе треба да се [$1 обратите] кај авторот на извршникот за да го решите.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Непозната URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Не пронајдов одобрено доделување со таа повластена шифра",
+ "mwoauthdatastore-request-token-not-found": "За жал, се јави проблем при поврзувањето на извршникот.\nВратете се и пробајте одново, или пак обратете се кај неговиот автор.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Не ја најдов OAuth-шифрата, $1</span>",
+ "mwoauthdatastore-callback-not-found": "Не најдов URL за одѕив на OAuth во меѓускладот. Ова веројатно е грешка во барањата на извршникот кон опслужувачот.",
+ "mwoauthdatastore-request-token-already-used": "Барањето е веќе извршено и не може да се преподнесе.\nВратете се на извршникот и пробајте пак да се поврзете со вашата сметка, или пак обратете се кај творецот на извршникот.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-шифрата веќе се користи, $1</span>",
+ "mwoauthdatastore-bad-token": "Не пронајдов барање што одговара на бараното",
+ "mwoauthdatastore-bad-source-ip": "Барањето дојде од неважечка IP-адреса.",
+ "mwoauthdatastore-bad-verifier": "Укажаниот потврден код е неважечки",
+ "mwoauthdatastore-invalid-token-type": "Побараниот тип на шифра е неважечки.",
+ "mwoauthgrants-general-error": "Се појави грешка во барањето за OAuth",
+ "mwoauthserver-bad-consumer": "„$1“ не е одобрен како поврзан извршник. Ако ви треба помош, [$2 обратете се] кај неговиот автор.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Неодобрен поврзан извршник на OAuth, $3</span>",
+ "mwoauthserver-bad-consumer-key": "За жал, нешто не е во ред со прилогот.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Непознат OAuth-клуч, $1</span>",
+ "mwoauthserver-insufficient-rights": "На вашата сметка не ѝ е дозволено да користи Поврзани извршници. Обратете се кај администраторот на мрежното место за да дознаете зошто.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Недоволни кориснички права за OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Неважечкa шифра во барањето.",
+ "mwoauthserver-invalid-user": "За да користите поврзани извршници на ова мрежно место, ќе мора да имате сметка на сите проекти (обединета сметка). Обидете се повторно да го поврзете „$1“ кога ќе направите таква сметка.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Се бара обединета сметка, $2</span>",
+ "mwoauthserver-consumer-no-secret": "За жал, нешто не е во ред со поврзувањето со извршникот.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Потрошувачот нема таен клуч, $1</span>",
+ "mwoauthserver-consumer-owner-only": "„$1“ е поврзан прилог само за сопственкот. За да ја добиете пристапната шифра, погл. [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Потрошувачот е само за сопственикот, $3</span>",
+ "mwoauth-invalid-authorization-title": "Грешка со овластувањето во OAuth",
+ "mwoauth-invalid-authorization": "Овластителните заглавија во вашето барање се неисправни: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Овластителните заглавија во вашето барање се неисправни за $1",
+ "mwoauth-invalid-authorization-invalid-user": "Овластителните заглавија во вашето барање се однесуваат на корисникот што тука не постои",
+ "mwoauth-invalid-authorization-wrong-user": "Овластителните заглавија во вашето барање се однесуваат на друг корисник",
+ "mwoauth-invalid-authorization-not-approved": "Извршникот што сакате да го поврзете е исправно поставен. За помош, обратете се кај авторот на „$1“.",
+ "mwoauth-invalid-authorization-blocked-user": "Овластителните заглавија во вашето барање се однесуваат на корисник што е блокиран",
+ "mwoauth-form-description-allwikis": "Здраво $1,\n\nЗа да може да го исполни вашето барање, '''$2''' има потреба од дозвола да делува во ваше име на сите проекти на ова мрежно место:\n\n\n$4",
+ "mwoauth-form-description-onewiki": "Здраво $1,\n\nЗа да може да го исполни вашето барање, '''$2''' има потреба од дозвола да делува во ваше име на сите проекти на ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Здраво $1,\n\nЗа да може да го исполни вашето барање, '''$2''' има потреба од пристап до информации на сите проекти на ова мрежно место во ваше име. Нема да има измени во вашата сметка.",
+ "mwoauth-form-description-onewiki-nogrants": "Здраво $1,\n\nЗа да може да го исполни вашето барање, '''$2''' има потреба од дозвола за пристап до информации на „$4“ во ваше име. Нема да има измени во вашата сметка.",
+ "mwoauth-form-description-allwikis-privateinfo": "Здраво $1,\n\nЗа да го довршите барањето, '''$2''' има потреба од дозвола за пристап до вашите информации, вклучувајќи го вашето вистинско име и е-поштенска адреса, на сите проекти на мрежново место. Нема да се вршат никакви измени во вашата сметка.",
+ "mwoauth-form-description-onewiki-privateinfo": "Здраво $1,\n\nЗа да го довршите барањето, '''$2''' има потреба од дозвола за пристап до вашите информации, вклучувајќи го вашето вистинско име и е-поштенска адреса, на ''$4''. Нема да се вршат никакви измени во вашата сметка.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Здраво $1,\n\nЗа да го довршите барањето, '''$2''' има потреба од дозвола за пристап до вашите информации, вклучувајќи ја вашата е-поштенска адреса, на сите проекти на мрежново место. Нема да се вршат никакви измени во вашата сметка.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Здраво $1,\n\nЗа да го довршите барањето, '''$2''' има потреба од дозвола за пристап до вашите информации, вклучувајќи ја вашата е-поштенска адреса, на сите проекти на $4. Нема да се вршат никакви измени во вашата сметка.",
+ "mwoauth-form-button-approve": "Дозволи",
+ "mwoauth-form-button-cancel": "Откажи",
+ "mwoauth-error": "Грешка во поврзувањето со извршникот",
+ "mwoauth-grants-heading": "Побарани дозволи:",
+ "mwoauth-grants-nogrants": "Извршникот нема побарано ниедна дозвола.",
+ "mwoauth-acceptance-cancelled": "Одбравте на „$1“ да не му дозволите пристап на вашата сметка. „$1“ ќе работи само ако му дадете дозвола. Можете да се вратите на „$1“ или пак да направите [[Special:OAuthManageMyGrants|нагодувања]] на поврзаните извршници.",
+ "mwoauth-granttype-normal": "Побарајте овластување за поединечни дозволи.",
+ "grant-mwoauth-authonly": "Само за заверка на идентитетот на корисникот, без можност за читање страници и постапување во име на корисникот.",
+ "grant-mwoauth-authonlyprivate": "Само за заверка на идентитетот на корисникот, со пристап до вистинското име и е-пошта, без можност за читање страници и постапување во име на корисникот.",
+ "mwoauth-listgrants-extra-summary": "== Наменски доделувања за OAuth ==\n\nОвие дополнителни доделувања се применливи само на потрошувачи на OAuth.",
+ "mwoauth-oauth-exception": "Се појави грешка во протоколот на OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback мора да е поставено на „oob“ (строго со мали букви)",
+ "mwoauth-callback-not-oob-or-prefix": "Мора да е зададено oauth_callback и да биде наместено на „oob“ (разликува големи/мали букви), или пак поставениот повратен повик мора да да биде претставка на поставен повратен повик.",
+ "right-mwoauthproposeconsumer": "Предлагање на нови потрошувачи на OAuth",
+ "right-mwoauthupdateownconsumer": "Измена на потрошувачи на OAuth",
+ "right-mwoauthmanageconsumer": "Раководење со потрошувачи на OAuth",
+ "right-mwoauthsuppress": "Скривање на потрошувачи на OAuth",
+ "right-mwoauthviewsuppressed": "Преглед на скриените потрошувачи на OAuth",
+ "right-mwoauthviewprivate": "Преглед на лични податоци за OAuth",
+ "right-mwoauthmanagemygrants": "Раководење со доделувања за OAuth",
+ "action-mwoauthmanageconsumer": "раководење со потрошувачи на OAuth",
+ "action-mwoauthsuppress": "скривање на потрошувачи на OAuth",
+ "action-mwoauthmanagemygrants": "раководење со вашите доделувања за OAuth",
+ "action-mwoauthproposeconsumer": "предлагање на потрошувачи на OAuth",
+ "action-mwoauthupdateownconsumer": "измена на потрошувачи на OAuth",
+ "action-mwoauthviewprivate": "преглед на лични податоци за OAuth",
+ "action-mwoauthviewsuppressed": "преглед на скриени потрошувачи на OAuth",
+ "mwoauth-tag-reserved": "Ознаките што почнуваат со <code>OAuth CID:</code> се резервирани за употреба од OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Напомена:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> е побезбеден од ботовските лозинки и треба да се користи секогаш кога ботот го поддржува.",
+ "mwoauth-api-module-disabled": "Модулот „$1“ не е достапен со OAuth.",
+ "echo-category-title-oauth-owner": "Разработка на OAuth",
+ "echo-pref-tooltip-oauth-owner": "Извести ме за случувањата поврзани со прилозите со OAuth што ги имам создадено.",
+ "echo-category-title-oauth-admin": "Администратор на OAuth",
+ "echo-pref-tooltip-oauth-admin": "Известувај ме за случувања поврзани со проверките на прилози со OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|предложи}} нов прилог со OAuth: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|го поднови}} прилогот со OAuth: $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|го одобри}} {{GENDER:$3|вашиот}} прилог со OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|го одби}} {{GENDER:$3|вашиот}} прилог со OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|го оневозможи}} {{GENDER:$3|вашиот}} прилог со OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|повторно го овозможи}} {{GENDER:$3|вашиот}} прилог со OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|предложи}} нов прилог со OAuth на {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|поднови}} прилог со OAuth на {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|го одобри}} {{GENDER:$3|вашиот}} прилог со OAuth на {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|го одби}} {{GENDER:$3|вашиот}} прилог со OAuth на {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|го одби}} {{GENDER:$3|вашиот}} прилог со OAuth на {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|повторно го овозможи}} {{GENDER:$3|вашиот}} прилог со OAuth на {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Провери го прилогот",
+ "notification-oauth-app-update-primary-link": "Провери го прилогот",
+ "notification-oauth-app-approve-primary-link": "Погл. прилогот",
+ "notification-oauth-app-reject-primary-link": "Погл. прилогот",
+ "notification-oauth-app-disable-primary-link": "Погл. прилогот",
+ "notification-oauth-app-reenable-primary-link": "Погл. прилогот",
+ "notification-oauth-app-body": "Причина: $1",
+ "mwoauth-oauth-version": "Верзија на протоколот на OAuth",
+ "mwoauth-oauth2-is-confidential": "Клиентот е доверлив",
+ "mwoauth-oauth2-is-confidential-help": "Доверлив клиент е прилог кој е способен да држи клиентска лозинка во доверливост. Недоверливите клиенти не се толку безбедни",
+ "mwoauth-oauth2-granttypes": "Допуштени видови доделби на OAuth2",
+ "mwoauth-oauth2-granttype-auth-code": "Код за овластување",
+ "mwoauth-oauth2-granttype-refresh-token": "Превчитај шифра",
+ "mwoauth-oauth2-granttype-client-credentials": "Клиентски податоци",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Не можам да ја создадам пристапната шифра бидејќи корисникот не го одобри нејзиното доделување",
+ "mwoauth-oauth2-error-user-approval-deny": "Корисникот го одби барањето од клиентскиот прилог",
+ "mwoauth-oauth-unsupported-version": "Завршетокот не е допуштен за OAuth верзија $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "Делокругот „$1“ не е дозволен за овој прилог",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "На клиентите само за сопственици мора да им биде дозволено да користат client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Не можев да ја добијам пристапната шифра: $1",
+ "mwoauth-oauth2-error-server-error": "Овластувачкиот опслужувач наиде на неочекуван услов кој го пречи да го исполни барањето.",
+ "mwoauth-oauth2-error-invalid-request": "На барањето му недостасува задолжителен параметар, вклучува неважечка параметарска вредност, вклучува параметар повеќе од еднаш, или пак е поинаку недобро срочен.",
+ "mwoauth-oauth2-error-unauthorized-client": "Клиентот не е овластен да бара овластувачки код по овој пат.",
+ "mwoauth-oauth2-error-access-denied": "Барањето е одбиено од сопственикот на ресурсот или овластувачкиот опслужувач.",
+ "mwoauth-oauth2-error-unsupported-response-type": "Овластувачкиот опслужувач не поддржува добивање на овластувачки код по овој пат.",
+ "mwoauth-oauth2-error-invalid-scope": "Побараниот делокруг е неважечки, непознат или неисправно срочен.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "Овластувачкиот опслужувач тековно не е во можност да го изврши барањето поради привремено претоварување или одржување на истиот.",
+ "mwoauth-oauth2-error-invalid-client": "Заверката на клиентит не успеа (на пр., непознат клиент, не е вклучена заверка, или пак неподдржан начин на заверка)",
+ "mwoauth-oauth2-error-request-not-verified": "Се обидувам да добијам потврдено својство пред да го потврдам барањето",
+ "mwoauth-oauth2-invalid-access-token": "Неважечка пристапна шифра",
+ "mwoauth-consumer-access-no-user": "Потрошувачкото одобрение мора да биде врзано за важечки корисник. Укажан е корисник со назнака 0",
+ "mwoauth-login-required-reason": "Ќе треба да се најавите со вашата сметка на {{SITENAME}} за да ги овластите прилозите за пристап до неа.",
+ "mwoauthconsumer-consumer-view": "Погл. го потрошувачов",
+ "mwoauthconsumer-application-view": "Погр. го прилогов"
+}
diff --git a/OAuth/i18n/ml.json b/OAuth/i18n/ml.json
new file mode 100644
index 00000000..d1cfa209
--- /dev/null
+++ b/OAuth/i18n/ml.json
@@ -0,0 +1,105 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kavya Manohar",
+ "Praveenp",
+ "Raghith",
+ "Santhosh.thottingal"
+ ]
+ },
+ "mwoauth-desc": "ഒഓത് 1.0എ എ.പി.ഐ. അനുമതി ഉപയോഗിക്കാൻ അനുവദിക്കുന്നു",
+ "mwoauth-verified": "ഈ സൗകര്യം ഇപ്പോൾ താങ്കളുടെ പേരിൽ മീഡിയവിക്കി എടുക്കാൻ അനുംതി നൽകുന്നു.\n\nപ്രക്രിയ പൂർണ്ണമാക്കാൻ, ഈ സാധൂകരണ വില അപേക്ഷയിൽ നൽകുക: '''$1'''",
+ "mwoauth-db-readonly": "ഓഓത് ഡേറ്റാബേസ് താത്കാലികമായി പൂട്ടിയിരിക്കുന്നു. ദയവായി ഏതാനം മിനിറ്റുകൾക്ക് ശേഷം ശ്രമിക്കുക.",
+ "mwoauth-missing-field": "'''$1''' എന്ന മണ്ഡലത്തിൽ വില നൽകിയിട്ടില്ല",
+ "mwoauth-invalid-field": "''$1'' എന്ന മണ്ഡലത്തിൽ അസാധുവായ വിലയാണ് നൽകിയിരിക്കുന്നത്",
+ "mwoauth-invalid-field-generic": "അസാധുവായ വിലയാണ് നൽകിയത്",
+ "mwoauth-field-hidden": "(ഈ വിവരം മറയ്ക്കപ്പെട്ടിരിക്കുന്നു)",
+ "mwoauth-field-private": "(ഈ വിവരം സ്വകാര്യമാണ്)",
+ "mwoauth-prefs-managegrants": "ബന്ധപ്പെട്ടിരിക്കുന്ന ആപുകൾ:",
+ "mwoauth-prefs-managegrantslink": "ബന്ധപ്പെട്ട {{PLURAL:$1|ഒരു ആപ്ലിക്കേഷൻ|$1 ആപ്ലിക്കേഷനുകൾ|0=ആപ്ലിക്കേഷൻ}} കൈകാര്യം ചെയ്യുക",
+ "mwoauth-consumer-allwikis": "ഈ സൈറ്റിലെ എല്ലാ പദ്ധതികളും",
+ "mwoauth-consumer-key": "ഉപഭോക്തൃചാവി:",
+ "mwoauth-consumer-name": "അപേക്ഷകന്റെ/യുടെ പേര്:",
+ "mwoauth-consumer-version": "ഉപയോക്തൃപതിപ്പ്:",
+ "mwoauth-consumer-user": "പ്രസാധക(ൻ):",
+ "mwoauth-consumer-stage": "തത്സ്ഥിതി:",
+ "mwoauth-consumer-email": "ബന്ധപ്പെടാനുള്ള ഇമെയിൽ വിലാസം:",
+ "mwoauth-consumer-email-help": "പുതിയ ഉപഭോക്താക്കളെ അംഗീകരിക്കുന്നവർക്ക് മാത്രം ദൃശ്യം",
+ "mwoauth-consumer-owner-only-label": "ഉടമയ്ക്ക്-മാത്രം:",
+ "mwoauth-consumer-description": "ആപ്ലിക്കേഷൻ വിവരണം:",
+ "mwoauth-consumer-callbackurl": "ഒഓത് ''പിൻവിളി'' യു.ആർ.എൽ:",
+ "mwoauth-consumer-grantsneeded": "ബാധകമായ സഹായങ്ങൾ:",
+ "mwoauth-consumer-required-grant": "ഉപഭോക്താവിനു ബാധകം",
+ "mwoauth-consumer-wiki": "ബാധകമായ പദ്ധതി:",
+ "mwoauth-consumer-wiki-thiswiki": "ഇപ്പോഴത്തെ പദ്ധതി ($1)",
+ "mwoauth-consumer-restrictions": "ഉപയോഗത്തിന്റെ പരിമിതപ്പെടുത്തലുകൾ:",
+ "mwoauth-consumer-restrictions-json": "ഉപയോഗത്തിന്റെ പരിമിതപ്പെടുത്തലുകൾ (ജെസൺ):",
+ "mwoauth-consumer-rsakey": "പൊതു ആർ.എസ്.എ. ചാവി (ഐച്ഛികം):",
+ "mwoauth-consumer-secretkey": "ഉപഭോക്താവിനുള്ള രഹസ്യ ചീട്ട്:",
+ "mwoauth-consumer-accesstoken": "അഭിഗമ്യതാ ചീട്ട്:",
+ "mwoauth-consumer-reason": "കാരണം:",
+ "mwoauth-consumer-email-unconfirmed": "താങ്കളുടെ അംഗത്വ ഇമെയിൽ ഇതുവരെ സ്ഥിരീകരിച്ചിട്ടില്ല.",
+ "mwoauth-consumer-email-mismatched": "നൽകിയിരിക്കുന്ന ഇമെയിൽ വിലാസം താങ്കളുടെ അംഗത്വത്തിന്റേതുമായി ചേർച്ചയുള്ളതാവണം.",
+ "mwoauth-consumer-alreadyexists": "ഈ പേര്/പതിപ്പ്/പ്രസാധകർ മിശ്രം നിലവിലുള്ള ഒരു ഉപഭോക്താവ്",
+ "mwoauth-consumer-alreadyexistsversion": "ഇതിനു തുല്യമായതോ പുതിയതായതോ ആയ പതിപ്പിൽ (\"$1\") പേര്/പ്രസാധകർ മിശ്രം നിലവിലുള്ള ഒരു ഉപഭോക്താവ്",
+ "mwoauth-consumer-not-accepted": "പണികൾ അവശേഷിക്കുന്ന ഉപഭോക്തൃ അഭ്യർത്ഥന സംബന്ധിച്ച വിവരങ്ങൾ പുതുക്കാൻ കഴിയില്ല",
+ "mwoauth-consumer-not-proposed": "ഉപഭോക്താവ് ഇപ്പോൾ നിർദ്ദേശിച്ചിട്ടില്ല",
+ "mwoauth-consumer-not-disabled": "ഉപഭോക്താവ് ഇപ്പോൽ പ്രവർത്തനരഹിതമല്ല",
+ "mwoauth-consumer-not-approved": "ഉപഭോക്താവിനെ അംഗീകരിച്ചിട്ടില്ല (നിർജ്ജീവവുമായിരിക്കാം)",
+ "mwoauth-missing-consumer-key": "ഉപഭോക്തൃചാവിയൊന്നും നൽകിയിട്ടില്ല.",
+ "mwoauth-invalid-consumer-key": "നൽകിയിരിക്കുന്ന ചാവിക്ക് അനുയോജ്യമായ ഉപഭോക്താവ് ഇല്ല.",
+ "mwoauth-invalid-access-token": "നൽകിയിരിക്കുന്ന ചാവിക്ക് യോജിച്ച പ്രവേശന ചീട്ട് നിലവിലില്ല.",
+ "mwoauth-invalid-access-wrongwiki": "ഉപഭോക്തവിനെ \"$1\" പദ്ധതിയിൽ മാത്രമേ ഉപയോഗിക്കാനാവൂ.",
+ "mwoauth-consumer-conflict": "താങ്കൾ എടുത്തുകൊണ്ടിരിക്കെ ആരോ ഈ ഉപഭോക്താവിന്റെ നിർമ്മിതികളിൽ മാറ്റം വരുത്തിയിരിക്കുന്നു. ദയവായി വീണ്ടും ശ്രമിക്കുക. താങ്കൾക്ക് മാറ്റത്തിന്റെ രേഖയും പരിശോധിക്കാവുന്നതാണ്.",
+ "mwoauth-consumer-stage-proposed": "നാമനിർദ്ദേശം ചെയ്തു",
+ "mwoauth-consumer-stage-rejected": "നിരസിച്ചു",
+ "mwoauth-consumer-stage-expired": "കാലഹരണപ്പെട്ടു",
+ "mwoauth-consumer-stage-approved": "അംഗീകരിച്ചു",
+ "mwoauth-consumer-stage-disabled": "നിർജ്ജീവമാക്കപ്പെട്ടു",
+ "mwoauth-consumer-stage-suppressed": "ഒതുക്കി",
+ "mwoauthconsumerregistration-navigation": "വഴികാട്ടി:",
+ "mwoauthconsumerregistration-main": "മുഖ്യം",
+ "mwoauthconsumerregistration-user": "പ്രസാധകർ",
+ "mwoauthconsumerregistration-description": "വിവരണം",
+ "mwoauthconsumerregistration-email": "ബന്ധപ്പെടേണ്ട ഇമെയിൽ",
+ "mwoauthconsumerregistration-stage": "സ്ഥിതി",
+ "mwoauthconsumerregistration-lastchange": "അവസാന മാറ്റം",
+ "mwoauthconsumerregistration-manage": "കൈകാര്യം ചെയ്യുക",
+ "mwoauthmanageconsumers-showproposed": "നിർദ്ദേശിച്ച അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-showrejected": "നിരസിച്ച അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-showexpired": "കാലഹരണപ്പെട്ട അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-linkproposed": "നിർദ്ദേശിച്ച അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-linkrejected": "നിരസിച്ച അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-linkexpired": "കാലഹരണപ്പെട്ട അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-linkapproved": "അംഗീകരിച്ച അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-linkdisabled": "നിർജ്ജീവമാക്കിയ അഭ്യർത്ഥനകൾ",
+ "mwoauthmanageconsumers-main": "മുഖ്യം",
+ "mwoauthmanageconsumers-user": "പ്രസാധകർ",
+ "mwoauthmanageconsumers-description": "വിവരണം",
+ "mwoauthmanageconsumers-email": "ബന്ധപ്പെടേണ്ട ഇമെയിൽ",
+ "mwoauthmanageconsumers-lastchange": "അവസാന മാറ്റം",
+ "mwoauthmanageconsumers-review": "സംശോധനം/കൈകാര്യം",
+ "mwoauthmanageconsumers-action": "സ്ഥിതി മാറ്റുക:",
+ "mwoauthmanageconsumers-approve": "അനുമതി നൽകിയിട്ടുണ്ട്",
+ "mwoauthmanageconsumers-reject": "നിരസിച്ചു",
+ "mwoauthmanageconsumers-rsuppress": "നിരസിച്ചു, ഒതുക്കി",
+ "mwoauthmanageconsumers-disable": "നിർജ്ജീവമാക്കപ്പെട്ടിരിക്കുന്നു",
+ "oauthlistconsumers": "ഓഓത് ആപ്ലിക്കേഷനുകൾ പട്ടികയായി കാണിക്കുക",
+ "mwoauthlistconsumers-user": "പ്രസാധകർ",
+ "mwoauthlistconsumers-description": "വിവരണം",
+ "mwoauthlistconsumers-wiki": "ബാധകമായ പദ്ധതി",
+ "mwoauthlistconsumers-status": "സ്ഥിതി",
+ "oauthmanagemygrants": "ബന്ധപ്പെടുത്തിയിട്ടുള്ള ആപ്ലിക്കേഷനുകൾ കൈകാര്യം ചെയ്യുക",
+ "mwoauthmanagemygrants-user": "പ്രസാധക(ൻ):",
+ "mwoauth-form-description-allwikis": "നമസ്കാരം $1,\n\nതാങ്കളുടെ അഭ്യർത്ഥന പൂർത്തിയാക്കുന്നതിനായി, ഈ സൈറ്റിലെ എല്ലാ പദ്ധതികളിലും ഇനി പറയുന്ന പ്രവൃത്തികൾ ചെയ്യാനുള്ള അനുമതി '''$2''' ആപ്ലിക്കേഷന് ആവശ്യമാണ്:\n\n$4",
+ "mwoauth-form-description-onewiki": "നമസ്കാരം $1,\n\nതാങ്കളുടെ അഭ്യർത്ഥന പൂർത്തിയാക്കുന്നതിനായി, ഇനി പറയുന്ന പ്രവൃത്തികൾ ''$4'' വിക്കിയിൽ താങ്കളെ പ്രതിനിധീകരിച്ച് ചെയ്യാനുള്ള അനുമതി '''$2''' ആപ്ലിക്കേഷന് ആവശ്യമാണ്:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "നമസ്കാരം $1,\n\nതാങ്കളുടെ അഭ്യർത്ഥന പൂർത്തിയാക്കുന്നതിനായി, താങ്കൾക്ക് വേണ്ടി ഈ സൈറ്റിലെ എല്ലാ പദ്ധതികളിലേയും വിവരങ്ങൾ എടുക്കാനുള്ള അനുമതി '''$2''' ആപ്ലിക്കേഷന് ആവശ്യമാണ്. താങ്കളുടെ അംഗത്വത്തിൽ യാതൊരു മാറ്റങ്ങളും വരുത്തുന്നതല്ല.",
+ "mwoauth-form-description-onewiki-nogrants": "നമസ്കാരം $1,\n\nതാങ്കളുടെ അഭ്യർത്ഥന പൂർത്തിയാക്കുന്നതിനായി, ''$4'' വിക്കിയിൽ താങ്കളെ പ്രതിനിധീകരിച്ച് വിവരങ്ങൾ എടുക്കാനുള്ള അനുമതി '''$2''' ആപ്ലിക്കേഷന് ആവശ്യമാണ്. താങ്കളുടെ അംഗത്വം ഉപയോഗിച്ച് മാറ്റങ്ങളൊന്നും വരുത്തുന്നതല്ല.",
+ "mwoauth-form-button-approve": "അനുവദിക്കുക",
+ "mwoauth-form-button-cancel": "റദ്ദാക്കുക",
+ "mwoauth-error": "ആപ്ലിക്കേഷൻ ബന്ധപ്പെടൽ പിഴവ്",
+ "mwoauth-grants-heading": "ആവശ്യപ്പെട്ട അനുമതികൾ:",
+ "grant-mwoauth-authonly": "ഉപയോക്താവിന്റെ അനന്യത പരിശോധന മാത്രം, താളുകൾ വായിക്കാനോ ഉപയോക്താവിന് വേണ്ടി പ്രവർത്തിക്കാനോ കഴിയില്ല.",
+ "grant-mwoauth-authonlyprivate": "യഥാർത്ഥനാമവും ഇമെയിൽ വിലാസവും അടക്കം ഉപയോക്താവിന്റെ അനന്യത പരിശോധന മാത്രം, താളുകൾ വായിക്കാനോ ഉപയോക്താവിന് വേണ്ടി പ്രവർത്തിക്കാനോ കഴിയില്ല.",
+ "mwoauth-listgrants-extra-summary": "== ഓഓത്-അധിഷ്ഠിത അനുമതികൾ ==\n\nഈ അധിക അനുമതികൾ ഓഓത് ഉപഭോക്താക്കൾക്ക് മാത്രം ബാധകമായവയാണ്."
+}
diff --git a/OAuth/i18n/mn.json b/OAuth/i18n/mn.json
new file mode 100644
index 00000000..ae8556c2
--- /dev/null
+++ b/OAuth/i18n/mn.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Wisdom"
+ ]
+ },
+ "mwoauthdatastore-bad-source-ip": "Хандалт буруу IP хаягаас ирсэн байна."
+}
diff --git a/OAuth/i18n/mr.json b/OAuth/i18n/mr.json
new file mode 100644
index 00000000..aefa4da7
--- /dev/null
+++ b/OAuth/i18n/mr.json
@@ -0,0 +1,155 @@
+{
+ "@metadata": {
+ "authors": [
+ "V.narsikar",
+ "संतोष दहिवळ"
+ ]
+ },
+ "mwoauth-verified": "या अनुप्रयुक्तिस आपल्यातर्फे मिडियाविकिवर पोहोचाची परवानगी नाही.\nही प्रक्रिया पूर्ण करण्यास,अनुप्रयुक्तिस ही पडताळा किंमत पुरवा:'''$1'''",
+ "mwoauth-db-readonly": "OAuthची डाटाबेस सध्या तात्पुरती ताळाबंद अवस्थेत आहे. काही मिनीटांनंतर पुन्हा प्रयत्न करा.",
+ "mwoauth-missing-field": "\"$1\" क्षेत्राची किंमत टाकली नाही",
+ "mwoauth-invalid-field": "\"$1\" क्षेत्रात अवैध किंमत टाकण्यात आली आहे",
+ "mwoauth-invalid-field-generic": "अवैध किंमत पुरविल्या गेली",
+ "mwoauth-field-hidden": "(ही माहिती लपविलेली आहे)",
+ "mwoauth-field-private": "(ही माहिती खाजगी आहे)",
+ "mwoauth-prefs-managegrants": "जोडलेले अॅप्स:",
+ "mwoauth-prefs-managegrantslink": "जोडलेल्या $1 {{PLURAL:$1|अनुप्रयुक्तिचे|अनुप्रयुक्तिंचे}} व्यवस्थापन करा",
+ "mwoauth-consumer-allwikis": "या संकेतस्थळावरील सर्व प्रकल्प",
+ "mwoauth-consumer-key": "ग्राहक कळ(किल्ली):",
+ "mwoauth-consumer-name": "अनुप्रयुक्तिचे नाव:",
+ "mwoauth-consumer-version": "ग्राहकाची आवृत्ती:",
+ "mwoauth-consumer-user": "प्रकाशक:",
+ "mwoauth-consumer-stage": "सद्य स्थिती:",
+ "mwoauth-consumer-email": "संपर्कासाठीचा विपत्रपत्ता:",
+ "mwoauth-consumer-owner-only-label": "मालक-फक्त:",
+ "mwoauth-consumer-description": "अनुप्रयुक्तिचे वर्णन:",
+ "mwoauth-consumer-granttypes": "मागणी केलेल्या अनुदानांचे प्रकार:",
+ "mwoauth-consumer-grantsneeded": "लागू असणारी अनुदाने:",
+ "mwoauth-consumer-required-grant": "ग्राहकांना लागू",
+ "mwoauth-consumer-wiki": "लागू असणारा प्रकल्प:",
+ "mwoauth-consumer-wiki-thiswiki": "सध्याचा प्रकल्प ($1)",
+ "mwoauth-consumer-restrictions": "वापराच्या मर्यादा:",
+ "mwoauth-consumer-restrictions-json": "वापराच्या मर्यादा (JSON):",
+ "mwoauth-consumer-rsakey": "पब्लिक आरएसए कळ:",
+ "mwoauth-consumer-secretkey": "ग्राहकाचे गुप्त टोकन:",
+ "mwoauth-consumer-accesstoken": "पोहोच बिल्ला:",
+ "mwoauth-consumer-reason": "कारण:",
+ "mwoauth-consumer-developer-agreement": "ही अनुप्रयुक्ति सादर करण्याने,आपण हे मान्य करता कि आपली अनुप्रयुक्ति अक्षम करण्याचा, आपणास हटविण्याचा किंवा आपणास किंवा आपल्या अनुप्रयुक्तिस या संकेतस्थळावर असलेल्या पोहोचेस अटकाव करण्याचा किंवा कोणताही,आम्हास योग्य वाटेल तो इतर कार्यवाहीचा मार्ग,केवळ आमच्याच निवाड्यानुसार, कि आपण व आपली अनुप्रयुक्ति एखादी नीती,मार्गदर्शन व या संकेतस्थळाची मार्गदर्शक तत्वे झुगारत आहात,तो अधिकार आम्ही राखीव ठेवला आहे. आम्ही ही अनुप्रयुक्ति नीती, पूर्वसूचनेशिवाय कधीही, आमच्याच निर्णयस्वातंत्र्यानुसार,आम्हास आवश्यक वाटेल तेंव्हा बदलू शकतो.आपला OAuth चा नेहमीचा सुरु असलेला वापर त्या बदलांना स्वीकृती दर्शवितो.",
+ "mwoauth-consumer-email-unconfirmed": "तुमचा विपत्र (ई-मेल) पत्ता अद्याप प्रमाणित झाला नाही.",
+ "mwoauth-consumer-email-mismatched": "आपल्या खात्याचा विपत्रपत्ता दिलेल्या विपत्रपत्त्याशी जुळावयास हवा.",
+ "mwoauth-consumer-alreadyexists": "या नावाचे/आवृत्तीचे/प्रकाशकाचे एकत्रीकरण पूर्वीच अस्तित्वात आहे",
+ "mwoauth-consumer-alreadyexistsversion": "या नावाचे/आवृत्तीचे/प्रकाशकाचे एकत्रीकरण त्या-सम वा उच्चतर आवृत्तीत पूर्वीच अस्तित्वात आहे(\"$1\")",
+ "mwoauth-consumer-not-accepted": "ग्राहकाच्या प्रलंबित विनंतीची माहिती अद्यतन करु शकत नाही",
+ "mwoauth-consumer-not-proposed": "हा ग्राहक सध्या प्रस्तावित नाही",
+ "mwoauth-consumer-not-disabled": "हा ग्राहक सध्या अक्षम केल्या गेला नाही",
+ "mwoauth-consumer-not-approved": "हा ग्राहक मंजूर नाही (तो अक्षम असण्याची शक्यता आहे)",
+ "mwoauth-missing-consumer-key": "ग्राहक कळ पुरविल्या गेली नाही",
+ "mwoauth-invalid-consumer-key": "दिलेल्या कळ असलेला कोणताच ग्राहक अस्तित्वात नाही",
+ "mwoauth-consumer-stage-proposed": "प्रस्तावित",
+ "mwoauth-consumer-stage-rejected": "अमान्य केलेल्या",
+ "mwoauth-consumer-stage-expired": "मुदतबाह्य",
+ "mwoauth-consumer-stage-approved": "प्रमाणित",
+ "mwoauth-consumer-stage-disabled": "अक्षम केलेल्या",
+ "mwoauth-consumer-stage-suppressed": "दमन(सप्रेस) केलेल्या",
+ "mwoauthconsumerregistration-navigation": "सुचालन:",
+ "mwoauthconsumerregistration-propose": "नविन ग्राहक प्रस्तावित करा",
+ "mwoauthconsumerregistration-list": "माझी ग्राहक यादी",
+ "mwoauthconsumerregistration-main": "मुख्य",
+ "mwoauthconsumerregistration-name": "ग्राहक",
+ "mwoauthconsumerregistration-user": "प्रकाशक",
+ "mwoauthconsumerregistration-description": "वर्णन",
+ "mwoauthconsumerregistration-email": "संपर्कासाठी विपत्रपत्ता",
+ "mwoauthconsumerregistration-consumerkey": "ग्राहक कळ(किल्ली):",
+ "mwoauthconsumerregistration-stage": "स्थिती",
+ "mwoauthconsumerregistration-lastchange": "मागील बदल",
+ "mwoauthconsumerregistration-manage": "व्यवस्थापन करा",
+ "mwoauthmanageconsumers-type": "रांगा:",
+ "mwoauthmanageconsumers-showproposed": "प्रस्तावित विनंत्या",
+ "mwoauthmanageconsumers-showrejected": "नामंजूर विनंत्या",
+ "mwoauthmanageconsumers-showexpired": "मुदतबाह्य विनंत्या",
+ "mwoauthmanageconsumers-linkproposed": "प्रस्तावित विनंत्या",
+ "mwoauthmanageconsumers-linkrejected": "नाकारलेल्या विनंत्या",
+ "mwoauthmanageconsumers-linkexpired": "समाप्त विनंत्या",
+ "mwoauthmanageconsumers-linkapproved": "मंजूर विनंत्या",
+ "mwoauthmanageconsumers-main": "मुख्य",
+ "mwoauthmanageconsumers-l-approved": "सध्या मंजूर ग्राहकांची यादी",
+ "mwoauthmanageconsumers-l-disabled": "सध्या अक्षम असलेल्या ग्राहकांची यादी",
+ "mwoauthmanageconsumers-none-proposed": "या यादित कोणताच ग्राहक प्रस्तावित नाही.",
+ "mwoauthmanageconsumers-none-rejected": "या यादित कोणताच ग्राहक प्रस्तावित नाही.",
+ "mwoauthmanageconsumers-none-expired": "या यादित कोणताच ग्राहक प्रस्तावित नाही.",
+ "mwoauthmanageconsumers-none-approved": "या निकषाशी कोणताच ग्राहक जुळत नाही.",
+ "mwoauthmanageconsumers-none-disabled": "या निकषाशी कोणताच ग्राहक जुळत नाही.",
+ "mwoauthmanageconsumers-name": "ग्राहक",
+ "mwoauthmanageconsumers-user": "प्रकाशक",
+ "mwoauthmanageconsumers-description": "वर्णन",
+ "mwoauthmanageconsumers-email": "संपर्काचा विपत्रपत्ता",
+ "mwoauthmanageconsumers-consumerkey": "ग्राहक कळ(किल्ली)",
+ "mwoauthmanageconsumers-lastchange": "मागील बदल",
+ "mwoauthmanageconsumers-review": "पुनरावलोकन/व्यवस्थापन करा",
+ "mwoauthmanageconsumers-action": "स्थिती बदला:",
+ "mwoauthmanageconsumers-approve": "मंजूर",
+ "mwoauthmanageconsumers-reject": "नामंजूर",
+ "mwoauthmanageconsumers-rsuppress": "नामंजूर व दमन केले",
+ "mwoauthmanageconsumers-disable": "अक्षम",
+ "mwoauthmanageconsumers-dsuppress": "अक्षम व दमन केले",
+ "mwoauthmanageconsumers-reenable": "मंजूर",
+ "mwoauthmanageconsumers-reason": "कारण:",
+ "mwoauthmanageconsumers-confirm-submit": "ग्राहकाची स्थिती अद्यतन करा",
+ "mwoauthmanageconsumers-success-approved": "विनंती मान्य करण्यात आली.",
+ "mwoauthmanageconsumers-success-rejected": "विनंती नामंजूर करण्यात आली.",
+ "mwoauthmanageconsumers-success-disabled": "ग्राहक अक्षम केल्या गेला आहे.",
+ "mwoauthmanageconsumers-success-reanable": "ग्राहक पुन्हा सक्षम केल्या गेला आहे.",
+ "mwoauthmanageconsumers-search-name": "या नावाचे ग्राहक",
+ "mwoauthmanageconsumers-search-publisher": "या सदस्याचे ग्राहक",
+ "oauthlistconsumers": "ओऑथ(OAuth) अनुप्रयुक्तिंची यादी",
+ "mwoauthlistconsumers-legend": "ओऑथ (OAuth) अनुप्रयुक्ति न्याहाळा",
+ "mwoauthlistconsumers-view": "तपशील",
+ "mwoauthlistconsumers-none": "या निकषाशी जुळणारी कोणतीही अनुप्रयुक्ति सापडली नाही.",
+ "mwoauthlistconsumers-name": "अनुप्रयुक्तिचे नाव",
+ "mwoauthlistconsumers-user": "प्रकाशक",
+ "mwoauthlistconsumers-description": "वर्णन",
+ "mwoauthlistconsumers-wiki": "लागू असणार प्रकल्प",
+ "mwoauthlistconsumers-grants": "लागू असणारी अनुदाने",
+ "mwoauthlistconsumers-status": "स्थिती",
+ "mwoauth-consumer-stage-any": "कोणतेही",
+ "mwoauthlistconsumers-status-proposed": "प्रस्तावित",
+ "mwoauthlistconsumers-status-approved": "मंजूर",
+ "mwoauthlistconsumers-status-disabled": "अक्षम केले",
+ "mwoauthlistconsumers-status-rejected": "नामंजूर केले",
+ "mwoauthlistconsumers-status-expired": "मुदतबाह्य झाले",
+ "oauthmanagemygrants": "जुळलेल्या अनुदानांचे व्यवस्थापन करा",
+ "mwoauthmanagemygrants-text": "हे पान,आपले खाते वापरु शकत असलेल्या अनुप्रयुक्तिची यादी तयार करते.अश्या कोणत्याही अनुप्रयुक्तिस,त्याच्या पोहोचेस असलेला आवाका हा, जेंव्हा आपण त्याला आपल्या वतीने काम करण्यास अधिकृत करता,या आपण त्यास दिलेल्या परवानगीपर्यंतच मर्यादित असतो.जर आपण वेगळ्या तऱ्हेने एखाद्या अनुप्रयुक्तिस आपल्या वतीने एखाद्या बंधू-प्रकल्पावर काम करण्यास अधिकृत करता,तेंव्हा आपण अश्या सर्व प्रकल्पांची वेगवेगळी रचना खाली बघु शकाल.\n\nजुळलेली अनुप्रयुक्ति ही, ओऑथ(OAuth) हा शिष्टाचार वापरुन,आपल्या खात्यापर्यंच पोहोचु शकते.<span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth जुळलेल्या अनुप्रयुक्तिविषयी अधिक जाणून घ्या])</span>",
+ "mwoauthmanagemygrants-navigation": "सुचालन:",
+ "mwoauthmanagemygrants-showlist": "जुळलेल्या अनुप्रयुक्त्यांची यादी",
+ "mwoauthmanagemygrants-none": "आपल्या खात्याशी जुळलेल्या कोणत्याच अनुप्रयुक्ति नाहीत.",
+ "mwoauthmanagemygrants-user": "प्रकाशक:",
+ "mwoauthmanagemygrants-description": "वर्णन",
+ "mwoauthmanagemygrants-wikiallowed": "या प्रकल्पावर परवानगी दिली:",
+ "mwoauthmanagemygrants-grants": "लागू असणारी अनुदाने",
+ "mwoauthmanagemygrants-grantsallowed": "परवानगी दिलेली अनुदाने",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "लागू अनुदानांना दिलेली परवनगी:",
+ "mwoauthmanagemygrants-grantaccept": "मंजूर",
+ "mwoauthmanagemygrants-confirm-legend": "जुळलेल्या अनुप्रयुक्तिचे व्यवस्थापन करा",
+ "mwoauthmanagemygrants-update": "अनुदानांना अद्यतन करा",
+ "mwoauthmanagemygrants-renounce": "अधिकार नाकारा",
+ "mwoauthmanagemygrants-action": "स्थिती बदला:",
+ "mwoauthmanagemygrants-success-update": "आपला या अनुप्रयुक्तिसाठीचा पसंतीक्रम अद्यतन करण्यात आलेला आहे.",
+ "mwoauthdatastore-bad-source-ip": "ही विनंती अवैध अंकपत्त्याद्वारे आली आहे.",
+ "mwoauthdatastore-bad-verifier": "पुरविलेला पडताळणी संकेत वैध नाही.",
+ "mwoauthdatastore-invalid-token-type": "विनंती केलेला टोकन प्रकार अवैध आहे.",
+ "mwoauthserver-invalid-request-token": "आपल्या विनंतीत अवैध टोकन आहे.",
+ "mwoauth-form-description-allwikis": "नमस्कार $1,\n\nआपली विनंती पूर्ण करण्यास,'''$2'''ला या संकेतस्थळाच्या सर्व प्रकल्पांवर आपल्या वतीने खालील क्रिया करण्यास परवानगी हवी आहे:\n\n\n$4",
+ "mwoauth-form-description-onewiki": "नमस्कार $1,\n\nआपली विनंती पूर्ण करण्यास,'''$2'''ला आपल्या वतीने ''$4'' वर खालील क्रिया करण्यास परवानगी हवी आहे:\n\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "नमस्कार $1,\n\nआपली विनंती पूर्ण करण्यास,'''$2'''ला या संकेतस्थळाच्या सर्व प्रकल्पांवर आपल्या वतीने माहितीस पोहोच करण्यास परवानगी हवी आहे. आपल्या खात्याचे काहीही बदलणार नाही.",
+ "mwoauth-form-description-onewiki-nogrants": "नमस्कार $1,\n\nआपली विनंती पूर्ण करण्यास,'''$2'''ला आपल्या वतीने''$4'' वर माहितीस पोहोच करण्यास परवानगी हवी आहे.आपल्या खात्याचे काहीही बदलणार नाही.\n\n\n$4",
+ "mwoauth-form-button-approve": "परवनगी द्या",
+ "mwoauth-form-button-cancel": "रद्द करा",
+ "mwoauth-error": "अनुप्रयुक्ति जुळण्याबाबतची त्रुटी",
+ "mwoauth-grants-heading": "विनंती केलेल्या परवानग्या:",
+ "mwoauth-grants-nogrants": "या अनुप्रयुक्तिने काहीच परवानग्या मागीतल्या नाहीत.",
+ "mwoauth-listgrants-extra-summary": "== ओऑथ-विशिष्ट अनुदाने ==\n\nही अतिरिक्त अनुदाने ओऑथ ग्राहकांना लागू आहेत.",
+ "echo-pref-tooltip-oauth-owner": "मी तयार केलेल्या ओऑथ अनुदानांच्या संबंधित उपक्रमांबद्दल मला सूचित करा.",
+ "echo-pref-tooltip-oauth-admin": "मी तयार केलेल्या ओऑथ अनुदानांच्या पुनरावलोकनाशी संबंधित उपक्रमांबद्दल मला सूचित करा.",
+ "notification-oauth-app-disable-primary-link": "ऐप बघा",
+ "notification-oauth-app-body": "कारण: $1"
+}
diff --git a/OAuth/i18n/ms.json b/OAuth/i18n/ms.json
new file mode 100644
index 00000000..ba331674
--- /dev/null
+++ b/OAuth/i18n/ms.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia"
+ ]
+ },
+ "mwoauth-desc": "Kelulusan OAuth 1.0a API",
+ "mwoauth-verified": "Aplikasi ini kini dibenarkan untuk mengakses MediaWiki bagi pihak anda.\n\nUntuk melengkapkan proses ini, berikan nilai penentusahan ini kepada aplikasi: '''$1'''",
+ "mwoauth-missing-field": "Nilai tertinggal untuk ruangan \"$1\"",
+ "mwoauth-invalid-field": "Nilai yang diberikan tidak sah untuk ruangan \"$1\"",
+ "mwoauth-invalid-field-generic": "Nilai yang diberikan tidak sah",
+ "mwoauth-field-hidden": "(maklumat ini tersembunyi)",
+ "mwoauth-field-private": "(maklumat ini adalah peribadi)",
+ "mwoauth-tag-reserved": "Teg-teg yang berawalan <code>OAuth CID:</code> adalah untuk kegunaan OAuth sahaja."
+}
diff --git a/OAuth/i18n/mwl.json b/OAuth/i18n/mwl.json
new file mode 100644
index 00000000..a1b69b48
--- /dev/null
+++ b/OAuth/i18n/mwl.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Athena in Wonderland",
+ "MokaAkashiyaPT"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplicaçones lhigadas:",
+ "mwoauth-prefs-managegrantslink": "Gerir $1 {{PLURAL:$1|aplicaçon lhigada|aplicaçones lhigadas}}",
+ "mwoauth-consumer-stage-disabled": "zatibado",
+ "mwoauthmanageconsumers-disable": "Zatibado",
+ "oauthlistconsumers": "Aplicaçones OAuth",
+ "mwoauthlistconsumers-status-disabled": "zatibado",
+ "oauthmanagemygrants": "Admenistrar aplicaçones lhigadas"
+}
diff --git a/OAuth/i18n/my.json b/OAuth/i18n/my.json
new file mode 100644
index 00000000..c32c8c57
--- /dev/null
+++ b/OAuth/i18n/my.json
@@ -0,0 +1,79 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dr Lotus Black",
+ "Ninjastrikers"
+ ]
+ },
+ "mwoauth-field-hidden": "(ဤသတင်းအချက်အလက်သည် ဖုံးကွယ်ထားသည်)",
+ "mwoauth-field-private": "(ဤသတင်းအချက်အလက်သည် ပုဂ္ဂလိကဖြစ်သည်)",
+ "mwoauth-prefs-managegrants": "ဆက်သွယ်ထားသော အက်ပ်များ -",
+ "mwoauth-prefs-managegrantslink": "ချိတ်ဆက်ထားသော {{PLURAL:$1|အပလေကေးရှင်း|အပလေကေးရှင်းများ}} $1 ခုကို စီမံခန့်ခွဲရန်",
+ "mwoauth-consumer-allwikis": "ဤဆိုဒ်ပေါ်ရှိ ပရောဂျက်အားလုံး",
+ "mwoauth-consumer-user": "ထုတ်ဝေသူ:",
+ "mwoauth-consumer-stage": "လက်ရှိအခြေနေ:",
+ "mwoauth-consumer-wiki-thiswiki": "လက်ရှိ ပရောဂျက် ($1)",
+ "mwoauth-consumer-reason": "အ​ကြောင်း​ပြ​ချက်:",
+ "mwoauth-consumer-stage-proposed": "အဆိုပြုထားသော",
+ "mwoauth-consumer-stage-rejected": "ငြင်းပယ်ခဲ့သည်",
+ "mwoauth-consumer-stage-expired": "သက်တမ်းကုန်လွန်သော",
+ "mwoauth-consumer-stage-approved": "အတည်ပြုထားသော",
+ "mwoauth-consumer-stage-disabled": "ပိတ်ထားသော",
+ "mwoauth-consumer-stage-suppressed": "ဖျောက်ထားသော",
+ "mwoauthconsumerregistration-navigation": "အ​ညွှန်း​:",
+ "mwoauthconsumerregistration-main": "ပင်မ",
+ "mwoauthconsumerregistration-user": "ထုတ်ဝေသူ",
+ "mwoauthconsumerregistration-description": "ဖော်ပြချက်",
+ "mwoauthconsumerregistration-email": "ဆက်သွယ်ရန် အီးမေးလ်",
+ "mwoauthconsumerregistration-stage": "အခြေအနေ",
+ "mwoauthconsumerregistration-lastchange": "နောက်ဆုံး ပြောင်းလဲမှု",
+ "mwoauthmanageconsumers-notloggedin": "ဤစာမျက်နှာသို့ ဝင်ရောက်ခွင့်ရှိရန် အကောင့်ထဲသို့ဝင်ရပါမည်။",
+ "mwoauthmanageconsumers-showproposed": "အဆိုပြုထားသော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-showrejected": "ငြင်းပယ်လိုက်သော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-showexpired": "သက်တမ်းကျော်လွန်သော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-linkproposed": "အဆိုပြုထားသော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-linkrejected": "ငြင်းပယ်လိုက်သော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-linkexpired": "သက်တမ်းကျော်လွန်သော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-linkapproved": "အတည်ပြုလိုက်သော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-linkdisabled": "ပိတ်ထားသော တောင်းဆိုချက်များ",
+ "mwoauthmanageconsumers-main": "ပင်မ",
+ "mwoauthmanageconsumers-user": "ထုတ်ဝေသူ",
+ "mwoauthmanageconsumers-description": "ဖော်ပြချက်",
+ "mwoauthmanageconsumers-email": "ဆက်သွယ်ရန် အီးမေးလ်",
+ "mwoauthmanageconsumers-lastchange": "နောက်ဆုံး ပြောင်းလဲမှု",
+ "mwoauthmanageconsumers-approve": "အတည်ပြုထားသည်",
+ "mwoauthmanageconsumers-reject": "ငြင်းပယ်ခဲ့သည်",
+ "mwoauthmanageconsumers-disable": "ပိတ်ထားသည်",
+ "mwoauthmanageconsumers-reenable": "အတည်ပြုထားသည်",
+ "mwoauthmanageconsumers-reason": "အ​ကြောင်း​ပြ​ချက်:",
+ "mwoauthlistconsumers-user": "ထုတ်ဝေသူ",
+ "mwoauthlistconsumers-description": "ဖော်ပြချက်",
+ "mwoauthlistconsumers-basicgrantsonly": "(အခြေခံ ဝင်ကြည့်ခွင့်သာ)",
+ "mwoauthlistconsumers-status": "အခြေအနေ",
+ "mwoauth-consumer-stage-any": "နှစ်သက်ရာ",
+ "mwoauthlistconsumers-status-proposed": "အဆိုပြုထားသော",
+ "mwoauthlistconsumers-status-approved": "အတည်ပြုခဲ့သည်",
+ "mwoauthlistconsumers-status-disabled": "ပိတ်ထားသော",
+ "mwoauthlistconsumers-status-rejected": "ငြင်းပယ်ခဲ့သည်",
+ "mwoauthlistconsumers-status-expired": "သက်တမ်းကုန်လွန်သော",
+ "oauthmanagemygrants": "ချိတ်ဆက်ထားသော အပလေကေးရှင်းများကို စီမံခန့်ခွဲရန်",
+ "mwoauthmanagemygrants-navigation": "အ​ညွှန်း​:",
+ "mwoauthmanagemygrants-showlist": "ချိတ်ဆက်ထားသော အပလေကေးရှင်းစာရင်း",
+ "mwoauthmanagemygrants-user": "ထုတ်ဝေသူ:",
+ "mwoauthmanagemygrants-description": "ဖော်ပြချက်",
+ "mwoauthmanagemygrants-wikiallowed": "ပရောဂျက်ပေါ်ရှိ ခွင့်ပြုထားသော:",
+ "mwoauthmanagemygrants-grantaccept": "အပ်နှင်းပြီး",
+ "mwoauthmanagemygrants-confirm-legend": "ချိတ်ဆက်ထားသော အပလေကေးရှင်းကို စီမံခန့်ခွဲရန်",
+ "mwoauthmanagemygrants-editslink": "ဤအပလေကေးရှင်းဖြင့် {{GENDER:$1|သင်၏}}တည်းဖြတ်မှုများ",
+ "mwoauthmanagemygrants-actionslink": "ဤအပလေကေးရှင်းဖြင့် {{GENDER:$1|သင်၏}}ဆောင်ရွက်ချက်များ",
+ "mwoauthdatastore-request-token-not-found": "စိတ်မကောင်းပါ၊ ဤအပလေကေးရှင်းသို့ ချိတ်ဆက်ရာ၌ တစ်ခုခု မှားယွင်းသွားခဲ့ပါသည်။ နောက်ပြန်သွားပြီး သင်၏အကောင့်ကို ထပ်မံချိတ်ဆက်ရန် ကြိုးစားပါ၊ သို့မဟုတ် အပလေကေးရှင်းပိုင်ရှင်ကို ဆက်သွယ်ပါ။ \n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth token မတွေ့ပါ၊ $1</span>",
+ "mwoauth-form-description-allwikis": "မင်္ဂလာပါ $1၊\n\n\nသင်၏တောင်းဆိုချက်ကို အပြီးသတ်ရန်အတွက် '''$2''' အနေဖြင့် ဤဆိုဒ်၏ ပရောဂျက်အားလုံးတွင် သင့်ကိုယ်စား အောက်ပါလုပ်ဆောင်ချက်များကို ဆောင်ရွက်ရန် ခွင့်ပြုချက်လိုအပ်ပါသည်။ \n\n\n$4",
+ "mwoauth-form-description-onewiki": "မင်္ဂလာပါ $1၊\n\n\nသင်၏တောင်းဆိုချက်ကို အပြီးသတ်ရန်အတွက် '''$2''' အနေဖြင့် ''$4'' ပေါ်တွင် သင့်ကိုယ်စား အောက်ပါလုပ်ဆောင်ချက်များကို ဆောင်ရွက်ရန် ခွင့်ပြုချက်လိုအပ်ပါသည်။ \n\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "မင်္ဂလာပါ $1၊\n\n\nသင်၏တောင်းဆိုချက်ကို အပြီးသတ်ရန်အတွက် '''$2''' အနေဖြင့် ဤဆိုဒ်၏ ပရောဂျက်အားလုံးတွင် သင့်ကိုယ်စား သတင်းအချက်အလက်ကို ဝင်ကြည့်ရန် ခွင့်ပြုချက်လိုအပ်ပါသည်။ သင်၏အကောင့်နှင့်ပတ်သက်၍ မည်သည့်ပြောင်းလဲမှုကိုမှ လုပ်ဆောင်မည်မဟုတ်ပါ။",
+ "mwoauth-form-description-onewiki-nogrants": "မင်္ဂလာပါ $1၊\n\n\nသင်၏တောင်းဆိုချက်ကို အပြီးသတ်ရန်အတွက် '''$2''' အနေဖြင့် ''$4'' တွင် သင့်ကိုယ်စား သတင်းအချက်အလက်ကို ဝင်ကြည့်ရန် ခွင့်ပြုချက်လိုအပ်ပါသည်။ သင်၏အကောင့်နှင့်ပတ်သက်၍ မည်သည့်ပြောင်းလဲမှုကိုမှ လုပ်ဆောင်မည်မဟုတ်ပါ။",
+ "mwoauth-form-description-allwikis-privateinfo": "မင်္ဂလာပါ $1၊\n\n\nသင်၏တောင်းဆိုချက်ကို အပြီးသတ်ရန်အတွက် '''$2''' အနေဖြင့် ဤဆိုဒ်၏ပရောဂျက်များအားလုံးတွင် သင့်ကိုယ်စား သင်၏နာမည်အရင်း၊ အီးမေးလိပ်စာအပါအဝင် သင့်အကြောင်း သတင်းအချက်အလက်ကို ဝင်ကြည့်ရန် ခွင့်ပြုချက်လိုအပ်ပါသည်။ သင်၏အကောင့်နှင့်ပတ်သက်၍ မည်သည့်ပြောင်းလဲမှုကိုမှ လုပ်ဆောင်မည်မဟုတ်ပါ။",
+ "mwoauth-form-description-onewiki-privateinfo": "မင်္ဂလာပါ $1၊\n\n\nသင်၏တောင်းဆိုချက်ကို အပြီးသတ်ရန်အတွက် '''$2''' အနေဖြင့် ''$4'' တွင် သင့်ကိုယ်စား သင်၏နာမည်အရင်း၊ အီးမေးလိပ်စာအပါအဝင် သင့်အကြောင်း သတင်းအချက်အလက်ကို ဝင်ကြည့်ရန် ခွင့်ပြုချက်လိုအပ်ပါသည်။ သင်၏အကောင့်နှင့်ပတ်သက်၍ မည်သည့်ပြောင်းလဲမှုကိုမှ လုပ်ဆောင်မည်မဟုတ်ပါ။",
+ "mwoauth-form-button-approve": "ခွင့်ပြုရန်",
+ "mwoauth-form-button-cancel": "မလုပ်တော့ပါ",
+ "notification-oauth-app-body": "အကြောင်းပြချက်: $1"
+}
diff --git a/OAuth/i18n/nah.json b/OAuth/i18n/nah.json
new file mode 100644
index 00000000..b04db54e
--- /dev/null
+++ b/OAuth/i18n/nah.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Akapochtli"
+ ]
+ },
+ "mwoauth-form-button-cancel": "Moxitiniz"
+}
diff --git a/OAuth/i18n/nap.json b/OAuth/i18n/nap.json
new file mode 100644
index 00000000..c9a293b8
--- /dev/null
+++ b/OAuth/i18n/nap.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "C.R."
+ ]
+ },
+ "mwoauthdatastore-bad-source-ip": "'A richiesta è venuta 'a n'innerizzo IP nun buono."
+}
diff --git a/OAuth/i18n/nb.json b/OAuth/i18n/nb.json
new file mode 100644
index 00000000..91d2324e
--- /dev/null
+++ b/OAuth/i18n/nb.json
@@ -0,0 +1,325 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cocu",
+ "Danmichaelo",
+ "Jon Harald Søby"
+ ]
+ },
+ "mwoauth-desc": "Muliggjør bruk av OAuth 1.0a og OAuth 2.0 for API-autorisering",
+ "mwoauth-nosubpage-explanation": "OAuth er en mekanisme som lar eksterne applikasjoner identifisere en {{SITENAME}}-bruker eller handle på deres vegne, etter at tillatelse er gitt av brukeren.\n\nFor at denne siden skal gjøre noe må flere parametere angis. Hvis du ble sendt hit av en ekstern applikasjon var det trolig på grunn av en feil i applikasjonen; du burde kontakte opphavspersonen dens.",
+ "mwoauth-verified": "Applikasjonen har nå tilgang til MediaWiki på dine vegne.\n\nFor å fullføre prosessen, vennligst angi verifikasjonsverdien til applikasjonen: '''$1'''",
+ "mwoauth-db-readonly": "OAuth-databasen er midlertidig låst. Vennligst forsøk igjen om et par minutter.",
+ "mwoauth-missing-field": "Mangler verdi for «$1»-feltet",
+ "mwoauth-invalid-field": "Ugyldig verdi angitt for «$1»-feltet",
+ "mwoauth-invalid-field-generic": "Ugyldig verdi angitt",
+ "mwoauth-field-hidden": "(denne informasjonen er skjult)",
+ "mwoauth-field-private": "(denne informasjonen er privat)",
+ "mwoauth-prefs-managegrants": "Tilkoblede apper:",
+ "mwoauth-prefs-managegrantslink": "Håndter {{PLURAL:$1|tilkoblet app|$1 tilkoblede apper|0=tilkoblede apper}}",
+ "mwoauth-consumer-allwikis": "Ikke begrenset til prosjekt",
+ "mwoauth-consumer-key": "Konsumentnøkkel:",
+ "mwoauth-consumer-name": "Applikasjonsnavn:",
+ "mwoauth-consumer-version": "Versjon:",
+ "mwoauth-consumer-user": "Utgiver:",
+ "mwoauth-consumer-stage": "Status:",
+ "mwoauth-consumer-email": "E-postadresse",
+ "mwoauth-consumer-email-help": "Bare synlig for de som godkjenner nye konsumenter",
+ "mwoauth-consumer-owner-only-label": "Kun eieren:",
+ "mwoauth-consumer-owner-only": "Denne konsumenten er kun for bruk av $1.",
+ "mwoauth-consumer-owner-only-help": "Å velge dette alternativet gjør at konsumenten godkjennes automatisk og godtas for bruk av $1. Det vil ikke kunne brukes av andre brukere, og den vanlige autoriseringsflyten vil ikke fungere. Handlinger tatt med denne konsumenten blir ikke tagget.",
+ "mwoauth-consumer-description": "Beskrivelse av appen:",
+ "mwoauth-consumer-callbackurl": "OAuth «callback»-URL:",
+ "mwoauth-consumer-callbackisprefix": "La konsument angi et callback i forespørsler og bruke «callback»-URL-en ovenfor som påkrevd prefiks.",
+ "mwoauth-consumer-granttypes": "Typer tildelinger som det bes om:",
+ "mwoauth-consumer-grantsneeded": "Tilgjengelige tildelinger:",
+ "mwoauth-consumer-required-grant": "Tilgjengelig for konsumentappen",
+ "mwoauth-consumer-wiki": "Begrens til prosjekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Gjeldende prosjekt ($1)",
+ "mwoauth-consumer-restrictions": "Bruksbegrensninger:",
+ "mwoauth-consumer-restrictions-json": "Bruksbegrensninger (JSON):",
+ "mwoauth-consumer-rsakey": "Offentlig RSA-nøkkel (valgfritt):",
+ "mwoauth-consumer-rsakey-help": "Skriv inn en offentlig nøkkel som skal bruke RSA-SHA-1-signaturmetoden. La denne være tom for å bruke HMAC-SHA1 med en tilfeldig hemmelighet. Hvis du er usikker, la den være tom.",
+ "mwoauth-consumer-secretkey": "Konsumentens hemmelige nøkkel:",
+ "mwoauth-consumer-accesstoken": "Tilgangstoken:",
+ "mwoauth-consumer-reason": "Årsak:",
+ "mwoauth-consumer-developer-agreement": "Ved å sende inn denne applikasjonen anerkjenner du at vi reserverer retten til å slå av applikasjonen, fjerne eller begrense din og din applikasjons tilgang til nettstedet, og vedta øvrige tiltak vi anser som nødvendige hvis vi mener at du eller din applikasjon bryter regler, retningslinjer eller prinsipper for nettstedet. Vi kan endre applikasjonsreglene når som helst uten forvarsel, på våre egne premisser når vi finner det nødvendig. Din fortsatte bruk av OAuth impliserer godtagelse av disse endringene.",
+ "mwoauth-consumer-email-unconfirmed": "E-postadressen din har ikke blitt bekreftet enda.",
+ "mwoauth-consumer-email-mismatched": "Den oppgitte e-postadressen må stemme med den som er koblet til kontoen din.",
+ "mwoauth-consumer-alreadyexists": "Det eksisterer allerede en konsument med denne kombinasjonen av navn/versjon/utgiver.",
+ "mwoauth-consumer-alreadyexistsversion": "Det eksisterer allerede en konsument med denne kombinasjonen av navn/utgiver, med en lik eller høyere versjon («$1»)",
+ "mwoauth-consumer-not-accepted": "Kan ikke oppdatere informasjon om en pågående konsumentsøknad",
+ "mwoauth-consumer-not-proposed": "Konsumenten er ikke foreslått",
+ "mwoauth-consumer-not-disabled": "Denne konsumenten er ikke deaktivert på det nåværende tidspunkt",
+ "mwoauth-consumer-not-approved": "Denne konsumenten er godkjent (den kan ha vært deaktivert)",
+ "mwoauth-missing-consumer-key": "Ingen konsumentnøkkel ble gitt.",
+ "mwoauth-invalid-consumer-key": "Det eksisterer ingen konsument med den gitte nøkkelen.",
+ "mwoauth-invalid-access-token": "Det eksisterer ingen tilgangstoken med den gitte nøkkelen.",
+ "mwoauth-invalid-access-wrongwiki": "Konsumenten kan kun brukes på prosjektet «$1».",
+ "mwoauth-consumer-conflict": "Noen endret attributtene til denne konsumenten mens du så på den. Vennligst prøv igjen. Se evt. i endringsloggen.",
+ "mwoauth-consumer-grantshelp": "Hver tildeling gir tilgang til en eller flere brukerrettigheter som brukeren allerede har. Se [[Special:ListGrants|tabell over tildelinger]] for mer informasjon.",
+ "mwoauth-consumer-stage-proposed": "foreslått",
+ "mwoauth-consumer-stage-rejected": "avslått",
+ "mwoauth-consumer-stage-expired": "utgått",
+ "mwoauth-consumer-stage-approved": "godkjent",
+ "mwoauth-consumer-stage-disabled": "deaktivert",
+ "mwoauth-consumer-stage-suppressed": "underslått",
+ "oauthconsumerregistration": "OAuth-konsumentregistrering",
+ "mwoauthconsumerregistration-navigation": "Navigasjon:",
+ "mwoauthconsumerregistration-propose": "Foreslå ny konsument",
+ "mwoauthconsumerregistration-list": "Min konsumentliste",
+ "mwoauthconsumerregistration-main": "Hovedside",
+ "mwoauthconsumerregistration-propose-text": "Utviklere kan bruke skjemaet under for å søke om en ny OAuth-konsument (se [//www.mediawiki.org/wiki/Extension:OAuth dokumentasjonen for MediaWiki-tillegget] for flere detaljer). Etter at skjemaet er sendt inn, vil du få et token som applikasjonen kan bruke for å identifisere seg for MediaWiki. En OAuth-administrator må godkjenne søknaden før applikasjonen kan brukes av andre brukere.\n\nNoen anbefalinger:\n* Prøv å bruke så få rettighetstildelinger som mulig. Unngå tildelinger som strengt tatt ikke er nødvendige.\n* Versjonsnumre følger formen «major.minor.release» (de siste to er valgfrie) og øker når endringer i tildelinger er nødvendige.\n* Du kan begrense applikasjonen til et bestemt prosjekt.",
+ "mwoauthconsumerregistration-update-text": "Bruk skjemaet under for å oppdatere innstillinger for en OAuth-konsument du kontrollerer.",
+ "mwoauthconsumerregistration-maintext": "Denne siden er for å la utviklere foreslå og oppdatere OAuth-konsumentapplikasjoner i registeret.\n\nHerfra kan du\n* [[Special:OAuthConsumerRegistration/propose|Søke om et token for en ny konsumentapplikasjon]].\n* [[Special:OAuthConsumerRegistration/list|Håndtere dine eksisterende konsumentapplikasjoner]].\n\nFor mer informasjon om OAuth, se [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth dokumentasjonen for MediaWiki-tillegget].",
+ "mwoauthconsumerregistration-propose-legend": "Ny OAuth-konsumentapplikasjon",
+ "mwoauthconsumerregistration-update-legend": "Oppdater OAuth-konsumentapplikasjon",
+ "mwoauthconsumerregistration-propose-submit": "Foreslå konsument",
+ "mwoauthconsumerregistration-update-submit": "Oppdater konsument",
+ "mwoauthconsumerregistration-none": "Du kontrollerer ingen OAuth-konsumenter.",
+ "mwoauthconsumerregistration-name": "Konsument",
+ "mwoauthconsumerregistration-user": "Utgiver",
+ "mwoauthconsumerregistration-description": "Beskrivelse",
+ "mwoauthconsumerregistration-email": "Kontakt-epost",
+ "mwoauthconsumerregistration-consumerkey": "Konsumentnøkkel",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Siste endring",
+ "mwoauthconsumerregistration-manage": "håndter",
+ "mwoauthconsumerregistration-resetsecretkey": "Tilbakestill den hemmelige nøkkelen til en ny verdi",
+ "mwoauthconsumerregistration-proposed": "Forespørselen din om en OAuth-konsument har blitt levert.\n\nDu har fått tildelt et konsumenttoken '''$1''' og et hemmelig token '''$2'''. ''Vennligst ta vare på disse til fremtidig bruk.''",
+ "mwoauthconsumerregistration-created-owner-only": "OAuth-konsumenten din har blitt opprettet.\n\nNøklene dine er:\n;Konsumentnøkkel: $1\n;Konsumenthemmelighet: $2\n;Tilgangsnøkkel: $3\n;Tilgangshemmelighet: $4\n<em>Ta vare på disse for fremtidig bruk.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "OAuth 2.0-klienten din har blitt opprettet.\n\nNøklene dine er:\n;Klientens applikasjonsnøkkel: $1\n;Klientens applikasjonshemmelighet: $2\n;Tilgangsnøkkel: $3\n;<em>Ta vare på disse for mulig framtidig bruk.</em>",
+ "mwoauthconsumerregistration-updated": "Ditt OAuth-konsumentregister ble oppdatert.",
+ "mwoauthconsumerregistration-secretreset": "Du har fått tildelt et hemmelig konsumenttoken '''$1'''. ''Vennligst ta vare på denne til fremtidig bruk.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Dine OAuth-konsumentnøkler har blitt endret. De nye nøklene er:\n;Konsumentnøkkel: $1\n;Konsumenthemmelighet: $2\n;Tilgangsnøkkel: $3\n;Tilgangshemmelighet: $4\n<em>Ta vare på disse for fremtidig bruk.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Dine konsumentnøkler for OAuth 2.0 har blitt tilbakestilt. De nye nøklene er:\n;Konsumentnøkkel: $1\n;Konsumenthemmelighet: $2\n;Tilgangsnøkkel: $3\n<em>Ta vare på disse til framtidig bruk.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Du må bekrefte epostadressa di før du oppretter OAuth-applikasjoner.\nSett og valider epostadressa di via [[Special:Preferences|innstillingene dine]].",
+ "oauthmanageconsumers": "Håndter OAuth-konsumenter",
+ "mwoauthmanageconsumers-notloggedin": "Du må være innlogget for å vise denne siden.",
+ "mwoauthmanageconsumers-type": "Køer:",
+ "mwoauthmanageconsumers-showproposed": "Ubehandlede søknader",
+ "mwoauthmanageconsumers-showrejected": "Avslåtte søknader",
+ "mwoauthmanageconsumers-showexpired": "Utgåtte søknader",
+ "mwoauthmanageconsumers-linkproposed": "foreslåtte forespørsler",
+ "mwoauthmanageconsumers-linkrejected": "avviste forespørsler",
+ "mwoauthmanageconsumers-linkexpired": "utgåtte forespørsler",
+ "mwoauthmanageconsumers-linkapproved": "godkjente forespørsler",
+ "mwoauthmanageconsumers-linkdisabled": "deaktiverte forespørsler",
+ "mwoauthmanageconsumers-main": "Hovedside",
+ "mwoauthmanageconsumers-maintext": "Denne siden er til for å behandle OAuth-konsumentapplikasjonforespørsler (se http://oauth.net) og håndtere etablerte OAuth-konsumenter.",
+ "mwoauthmanageconsumers-queues": "Velg en konsumentsøknadskø under:",
+ "mwoauthmanageconsumers-q-proposed": "Kø av ubehandlede konsumentsøknader",
+ "mwoauthmanageconsumers-q-rejected": "Kø av avslåtte konsumentsøknader",
+ "mwoauthmanageconsumers-q-expired": "Kø av utgåtte konsumentsøknader",
+ "mwoauthmanageconsumers-lists": "Velg en konsumentstatusliste under:",
+ "mwoauthmanageconsumers-l-approved": "Liste over godkjente konsumenter",
+ "mwoauthmanageconsumers-l-disabled": "Liste over deaktiverte konsumenter",
+ "mwoauthmanageconsumers-none-proposed": "Ingen foreslåtte konsumenter i denne listen.",
+ "mwoauthmanageconsumers-none-rejected": "Ingen foreslåtte konsumenter i denne listen.",
+ "mwoauthmanageconsumers-none-expired": "Ingen foreslåtte konsumenter i denne listen.",
+ "mwoauthmanageconsumers-none-approved": "Ingen konsumenter oppfyller disse kriteriene.",
+ "mwoauthmanageconsumers-none-disabled": "Ingen konsumenter oppfyller disse kriteriene.",
+ "mwoauthmanageconsumers-name": "Kunde",
+ "mwoauthmanageconsumers-user": "Utgiver",
+ "mwoauthmanageconsumers-description": "Beskrivelse",
+ "mwoauthmanageconsumers-email": "E-postadresse",
+ "mwoauthmanageconsumers-consumerkey": "Kundenøkkel",
+ "mwoauthmanageconsumers-lastchange": "Siste endring",
+ "mwoauthmanageconsumers-review": "behandle/håndter",
+ "mwoauthmanageconsumers-confirm-text": "Bruk dette skjemaet for å godkjenne, avslå, deaktivere eller gjenaktivere denne konsumenten.",
+ "mwoauthmanageconsumers-confirm-legend": "Håndter OAuth-konsument",
+ "mwoauthmanageconsumers-action": "Endre status:",
+ "mwoauthmanageconsumers-approve": "Godkjent",
+ "mwoauthmanageconsumers-reject": "Avslått",
+ "mwoauthmanageconsumers-rsuppress": "Avslått eller undertrykket",
+ "mwoauthmanageconsumers-disable": "Deaktivert",
+ "mwoauthmanageconsumers-dsuppress": "Deaktivert og undertrykket",
+ "mwoauthmanageconsumers-reenable": "Godkjent",
+ "mwoauthmanageconsumers-reason": "Årsak:",
+ "mwoauthmanageconsumers-confirm-submit": "Oppdater kundestatus",
+ "mwoauthmanageconsumers-success-approved": "Søknaden har blitt godkjent.",
+ "mwoauthmanageconsumers-success-rejected": "Forespørselen har blitt avslått.",
+ "mwoauthmanageconsumers-success-disabled": "Kunden har blitt deaktivert.",
+ "mwoauthmanageconsumers-success-reanable": "Kunden har blitt reaktivert.",
+ "mwoauthmanageconsumers-search-name": "kunder med dette navn",
+ "mwoauthmanageconsumers-search-publisher": "kunder for denne brukeren",
+ "oauthlistconsumers": "Liste over OAuth-applikasjoner",
+ "mwoauthlistconsumers-legend": "Bla i OAuth-applikasjoner",
+ "mwoauthlistconsumers-view": "detaljer",
+ "mwoauthlistconsumers-none": "Ingen applikasjoner oppfyller disse kriteriene.",
+ "mwoauthlistconsumers-name": "Applikasjonsnavn",
+ "mwoauthlistconsumers-version": "Kundeversjon",
+ "mwoauthlistconsumers-user": "Utgiver",
+ "mwoauthlistconsumers-description": "Beskrivelse",
+ "mwoauthlistconsumers-wiki": "Begrenset til prosjekt",
+ "mwoauthlistconsumers-callbackurl": "OAuth «callback-URL»",
+ "mwoauthlistconsumers-callbackisprefix": "La konsument angi et callback i forespørsler og bruke «callback»-URL-en ovenfor som påkrevd prefiks.",
+ "mwoauthlistconsumers-grants": "Tilgjengelige tildelinger:",
+ "mwoauthlistconsumers-basicgrantsonly": "(kun grunnleggende tilgang)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "alle",
+ "mwoauthlistconsumers-status-proposed": "foreslått",
+ "mwoauthlistconsumers-status-approved": "godkjent",
+ "mwoauthlistconsumers-status-disabled": "deaktivert",
+ "mwoauthlistconsumers-status-rejected": "avslått",
+ "mwoauthlistconsumers-status-expired": "utgått",
+ "mwoauthlistconsumers-navigation": "Navigasjon:",
+ "mwoauthlistconsumers-update-link": "Oppdater konsument",
+ "mwoauthlistconsumers-manage-link": "Behandle konsument",
+ "mwoauthlistconsumers-grants-link": "Behandle tildelinger",
+ "mwoauthlistconsumers-rclink": "Siste endringer av denne applikasjonen",
+ "oauthmanagemygrants": "Behandle tilkoblede applikasjoner",
+ "mwoauthmanagemygrants-text": "Denne siden lister opp alle applikasjoner som kan bruke kontoen din. Hver applikasjons tilgang er begrenset til de rettigheter du har godkjent. Hvis du uavhengig har godkjent en applikasjon for bruk på flere søsterprosjekter, vil du se uavhengige konfigurasjonsmuligheter for hvert prosjekt under.\n\nApplikasjonene har tilgang til kontoen din over OAuth-protokollen. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Lær mer om tilkoblede applikasjoner])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigasjon:",
+ "mwoauthmanagemygrants-showlist": "Liste over tilkoblede applikasjoner",
+ "mwoauthmanagemygrants-none": "Det er ingen applikasjoner tilknyttet kontoen din.",
+ "mwoauthmanagemygrants-user": "Utgiver:",
+ "mwoauthmanagemygrants-description": "Beskrivelse",
+ "mwoauthmanagemygrants-wikiallowed": "Tillatt på prosjektet:",
+ "mwoauthmanagemygrants-grants": "Tilgjengelige tildelinger",
+ "mwoauthmanagemygrants-grantsallowed": "Tildelinger tillatt",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Aktuelle tillatte tildelinger:",
+ "mwoauthmanagemygrants-review": "håndter tilgang",
+ "mwoauthmanagemygrants-revoke": "tilbakekall tilgang",
+ "mwoauthmanagemygrants-grantaccept": "Bevilget",
+ "mwoauthmanagemygrants-update-text": "Bruk skjemaet under for å tilpasse hvilke typer tilgang du vil tildele applikasjon for å handle på dine vegne.",
+ "mwoauthmanagemygrants-revoke-text": "Bruk skjemaet under for å tilbakekalle en applikasjons tilgang til å handle på dine vegne.",
+ "mwoauthmanagemygrants-confirm-legend": "Håndter tilkoblet applikasjon",
+ "mwoauthmanagemygrants-update": "Oppdater tildelinger",
+ "mwoauthmanagemygrants-renounce": "Avautorisér",
+ "mwoauthmanagemygrants-action": "Endre status:",
+ "mwoauthmanagemygrants-confirm-submit": "Oppdater status for tilgangstoken",
+ "mwoauthmanagemygrants-success-update": "Innstillingene dine for denne applikasjonen har blitt oppdatert.",
+ "mwoauthmanagemygrants-success-renounce": "Applikasjonens tilgang til din konto har blitt tilbakekalt.",
+ "mwoauthmanagemygrants-basic-tooltip": "Hvorfor kan jeg ikke oppdatere denne tildelingen? Denne tildelingen gir det tilknyttede applikasjonen din grunnleggende tillatelser som kreves for å fungere ordentlig. Hvis du ikke ønsker at applikasjonen skal ha disse rettighetene bør du tilbakekalle tilgangen til applikasjonen.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Hvorfor kan jeg ikke oppdatere denne tildelingen? Hvis du ikke ønsker at den tilkoblede applikasjonen skal ha denne rettigheten bør du fjerne tilgangen til applikasjonen.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Dine}} redigeringer via denne applikasjonen",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Dine}} handlinger via denne applikasjonen",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|foreslo}} en OAuth-konsument (konsumentnøkkel $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|oppdaterte}} en OAuth-konsument (konsumentnøkkel $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|godkjente}} en OAuth-konsument fra $3 (konsumentnøkkel $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|avslo}} en OAuth-konsument fra $3 (konsumentnøkkel $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|deaktiverte}} en OAuth-konsument fra $3 (konsumentnøkkel $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|re-aktiverte}} en OAuth-konsument fra $3 (konsumentnøkkel $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|opprettet}} en OAuth-konsument kun for eieren (konsumentnøkkel $4)",
+ "log-action-filter-mwoauthconsumer": "Type OAUth-konsumenthandling:",
+ "log-action-filter-mwoauthconsumer-approve": "Godkjenning av OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Opprettelse av OAuth-konsument kun for eieren",
+ "log-action-filter-mwoauthconsumer-disable": "Deaktivering av OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-propose": "Forslag om OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-reenable": "Reaktivering av OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-reject": "Avslag på OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-update": "Oppdatering av OAuth-konsument",
+ "mwoauthconsumer-consumer-logpage": "OAuth-konsumentlogg",
+ "mwoauthconsumer-consumer-logpagetext": "Logg over godkjennelser, avslag og deaktiveringer for registrerte OAuth-konsumenter.",
+ "mwoauth-bad-request-missing-params": "Beklager, noe gikk galt under konfigurasjonen av den tilkoblede applikasjonen. Kontakt applikasjonens utvikler.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Manglende OAuth-parametere, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Beklager, noe gikk galt, du må kontakte utvikleren av applikasjonen for å få hjelp med dette.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ukjent URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Beklager, noe gikk galt. Du må [$1 kontakte] utvikleren av applikasjonen for hjelp til å løse dette.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ukjent URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Ingen godkjente tildelinger ble funnet for dette autorisasjonstokenet.",
+ "mwoauthdatastore-request-token-not-found": "Beklager, noe gikk galt ved tilkobling av denne applikasjonen.\nGå tilbake og prøv å koble til kontoen igjen, eller kontakt applikasjonsutvikleren.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-token ble ikke funnet, $1</span>",
+ "mwoauthdatastore-callback-not-found": "OAuth-callback-URL ikke funnet i mellomlageret. Dette er trolig en feil i hvordan applikasjon gjør forespørsler mot tjeneren.",
+ "mwoauthdatastore-request-token-already-used": "Denne forespørselen har allerede blitt fullført og kan ikke utføres igjen.\nGå tilbake til applikasjonen og prøv å koble til kontoen din igjen, eller kontakt opphavspersonen til applikasjonen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-nøkkel allerede brukt, $1</span>",
+ "mwoauthdatastore-bad-token": "Det ble ikke funnet noe token som matchet forespørselen din.",
+ "mwoauthdatastore-bad-source-ip": "Forespørselen kom fra en ugyldig IP-adresse.",
+ "mwoauthdatastore-bad-verifier": "Den angitte verifikasjonskoden var ikke gyldig.",
+ "mwoauthdatastore-invalid-token-type": "Den forespurte tokentypen er ugyldig.",
+ "mwoauthgrants-general-error": "Det var en feil i OAuth-forespørselen.",
+ "mwoauthserver-bad-consumer": "«$1» er ikke godkjent som en Tilkoblet App. [$2 Kontakt] apputvikleren for hjelp.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Tilkoblet OAuth-app er ikke godkjent, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Beklager, noe gikk galt under tilkoblingen til denne applikasjonen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ukjent OAuth-nøkkel, $1</span>",
+ "mwoauthserver-insufficient-rights": "Kontoen din har ikke tilgang til å bruke tilkoblede applikasjoner, kontakt din sideadministrator for å finne ut hvorfor.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Utilstrekkelige OAuth-brukerrettigheter, $1</span>",
+ "mwoauthserver-invalid-request-token": "Ugyldig token i forespørselen din.",
+ "mwoauthserver-invalid-user": "For å bruke tilkoblede applikasjoner på denne siden, må du ha en konto som fungerer på alle prosjektene (''unified login''). Når du har fått en slik konto kan du prøve å koble til «$1» igjen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Trenger ''unified login'', $2</span>",
+ "mwoauthserver-consumer-no-secret": "Beklager, noe gikk galt under tilkoblingen av denne applikasjonen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konsumenten har ingen hemmelig nøkkel, $1</span>",
+ "mwoauthserver-consumer-owner-only": "«$1» er en app som er tilkoblet kun for eieren. For å hente tilgangsnøkkelen, se [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konsumenten er kun for eieren, $3</span>",
+ "mwoauth-invalid-authorization-title": "OAuth-autoriseringsfeil",
+ "mwoauth-invalid-authorization": "Autorisasjonsheaderne i forespørselen er ikke gyldige: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Autorisasjonsheaderne i forespørselen er ikke gyldige for $1",
+ "mwoauth-invalid-authorization-invalid-user": "Autorisasjonsheaderne i forespørselen tilhører en bruker som ikke eksisterer her",
+ "mwoauth-invalid-authorization-wrong-user": "Autorisasjonsheaderne i forespørselen tilhører en annen bruker",
+ "mwoauth-invalid-authorization-not-approved": "Appen du prøver å koble til ser ikke ut til å være satt opp riktig. Kontakt utvikleren av «$1» for hjelp.",
+ "mwoauth-invalid-authorization-blocked-user": "Autorisasjonsheaderne i forespørselen tilhører en bruker som er blokkert.",
+ "mwoauth-form-description-allwikis": "Hei $1,\n\nFor å fullføre forespørselen trenger '''$2''' tillatelse til å utføre følgende handlinger på dine vegne på alle prosjekter på dette nettstedet:\n\n$4",
+ "mwoauth-form-description-onewiki": "Hei $1,\n\nFor å fullføre forespørselen trenger '''$2''' tillatelse til å utføre følgende handlinger på dine vegne på ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hei $1,\n\nFor å fullføre forespørselen trenger '''$2''' tillatelse til å hente informasjon på dine vegne fra alle prosjekter på dette nettstedet. Ingen endringer vil bli gjort med din konto.",
+ "mwoauth-form-description-onewiki-nogrants": "Hei $1,\n\nFor å fullføre forespørselen trenger '''$2''' tillatelse til å hente informasjon fra ''$4'' på dine vegne. Ingen endringer vil bli gjort med din konto.",
+ "mwoauth-form-description-allwikis-privateinfo": "Hei $1,\n\nFor å fullføre forespørselen din trenger '''$2''' tilgang til informasjon om deg, inkludert virkelig navn og epostadresse, på alle prosjekter på dette nettstedet. Ingen endringer blir gjort med kontoen din.",
+ "mwoauth-form-description-onewiki-privateinfo": "Hei $1,\n\nFor å fullføre forespørselen din trenger '''$2''' tilgang til informasjon, inkludert ditt virkelige navn og epostadresse, på ''$4''. Ingen endringer blir gjort med kontoen din.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Hei $1,\n\nFor å fullføre forespørselen din trenger '''$2''' tilgang til informasjon om deg, inkludert din epostadresse, på alle prosjekter på dette nettstedet. Ingen endringer blir gjort med kontoen din.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Hei $1,\n\nFor å fullføre forespørselen din trenger '''$2''' tilgang til informasjon, inkludert din epostadresse, på ''$4''. Ingen endringer blir gjort med kontoen din.",
+ "mwoauth-form-button-approve": "Godkjenn",
+ "mwoauth-form-button-cancel": "Avbryt",
+ "mwoauth-error": "Tilkoblingsfeil for applikasjonen",
+ "mwoauth-grants-heading": "Ønskede rettigheter:",
+ "mwoauth-grants-nogrants": "Applikasjonen har ikke bedt om noen rettigheter.",
+ "mwoauth-acceptance-cancelled": "Du har valgt å ikke gi «$1» tilgang til kontoen din. «$1» vil ikke fungere uten denne tilgangen. Du kan velge å gå tilbake til «$1» eller [[Special:OAuthManageMyGrants|håndtere]] dine tilkoblede apper.",
+ "mwoauth-granttype-normal": "Be om autorisering for bestemte tilganger.",
+ "grant-mwoauth-authonly": "Kun brukerverifisering, ingen mulighet til å lese sider eller handle på brukerens vegne.",
+ "grant-mwoauth-authonlyprivate": "Identitetsverifikasjon med tilgang til virkelig navn og epostadresse, kan ikke lese sider eller handle på brukerens vegne.",
+ "mwoauth-listgrants-extra-summary": "== OAuth-spesifikke tildelinger ==\n\nDisse ekstra tildelingene er tilgjengelige for OAuth-konsumenter.",
+ "mwoauth-oauth-exception": "Det oppsto en feil i OAuth-protokollen: $1",
+ "mwoauth-callback-not-oob": "oauth_callback må angis, og må settes lik «oob» (skiftsensitiv)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback må være satt, og må være satt til «oob» (skill mellom store og små bokstaver), eller det konfigurerte callbacket må være et prefiks til det gitte callbacket.",
+ "right-mwoauthproposeconsumer": "Foreslå nye OAuth-konsumenter",
+ "right-mwoauthupdateownconsumer": "Oppdatere OAuth-konsumenter du kontrollerer",
+ "right-mwoauthmanageconsumer": "Håndtere OAuth-konsumenter",
+ "right-mwoauthsuppress": "Underslå OAuth-konsumenter",
+ "right-mwoauthviewsuppressed": "Vise underslåtte OAuth-konsumenter",
+ "right-mwoauthviewprivate": "Vise private OAuth-data",
+ "right-mwoauthmanagemygrants": "Håndtere OAuth-tildelinger",
+ "action-mwoauthmanageconsumer": "håndter OAuth-konsumenter",
+ "action-mwoauthsuppress": "undertrykke OAuth-konsumenter",
+ "action-mwoauthmanagemygrants": "håndter dine OAuth-tildelinger",
+ "action-mwoauthproposeconsumer": "foreslå nye OAuth-konsumenter",
+ "action-mwoauthupdateownconsumer": "oppdater OAuth-konsumenter du kontrollerer",
+ "action-mwoauthviewprivate": "vise private OAuth-data",
+ "action-mwoauthviewsuppressed": "vis underslåtte OAuth-konsumenter",
+ "mwoauth-tag-reserved": "Tagger som begynner med <code>OAuth CID:</code> er reservert for bruk av OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Merk:</strong> <span class=\"plainlinks\">[$1 Oauth]</span> er tryggere en botpassord og foretrekkes der botten støtter det.",
+ "mwoauth-api-module-disabled": "Modulen «$1» er ikke tilgjengelig med OAuth.",
+ "echo-category-title-oauth-owner": "OAuth-utvikling",
+ "echo-pref-tooltip-oauth-owner": "Varsle meg om hendelser relatert til OAuth-applikasjoner jeg har opprettet.",
+ "echo-category-title-oauth-admin": "OAuth-admin",
+ "echo-pref-tooltip-oauth-admin": "Varsle meg om hendelser i forbindelse med gjennomgang av OAuth-applikasjoner.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|foreslo}} en ny OAuth-app: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|oppdaterte}} OAuth-appen $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|godkjente}} OAuth-appen {{GENDER:$3|din}} ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|avviste}} OAuth-appen {{GENDER:$3|din}} ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|deaktiverte}} OAuth-appen {{GENDER:$3|din}} ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reaktiverte}} OAuth-appen {{GENDER:$3|din}} ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|foreslo}} en ny OAuth-app på {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|oppdaterte}} en OAuth-app på {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|godkjente}} OAuth-appen {{GENDER:$3|din}} på {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|avviste}} OAuth-appen {{GENDER:$3|din}} på {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|deaktiverte}} OAuth-appen {{GENDER:$3|din}} på {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reaktiverte}} OAuth-appen {{GENDER:$3|din}} på {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Gjennomgå app",
+ "notification-oauth-app-update-primary-link": "Gjennomgå app",
+ "notification-oauth-app-approve-primary-link": "Vis app",
+ "notification-oauth-app-reject-primary-link": "Vis app",
+ "notification-oauth-app-disable-primary-link": "Vis app",
+ "notification-oauth-app-reenable-primary-link": "Vis app",
+ "notification-oauth-app-body": "Årsak: $1",
+ "mwoauth-oauth-version": "OAuth-protokollversjon",
+ "mwoauth-oauth2-is-confidential": "Klienten er konfidensiell",
+ "mwoauth-oauth2-is-confidential-help": "En konfidensiell klient er en applikasjon som kan holde klientpassordet konfidensielt for verden. Ikke-konfidensielle klienter er mindre sikre.",
+ "mwoauth-oauth2-granttypes": "Tillatte OAuth2-tildelingstyper",
+ "mwoauth-oauth2-granttype-auth-code": "Autoriseringskode",
+ "mwoauth-oauth2-granttype-refresh-token": "Oppdater nøkkel",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Kan ikke opprette tilgangsnøkkel, brukeren godtok ikke å utdele denne tilgangsnøkkelen",
+ "mwoauth-oauth2-error-user-approval-deny": "Brukeren har avvist forespørselen fra klientapplikasjonen",
+ "mwoauth-oauth-unsupported-version": "Dette endepunktet er ikke tillatt for OAuth versjon $1",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Klienter for kun eiere må kunne bruke client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Kunne ikke hente tilgangsnøkkel: $1",
+ "mwoauth-oauth2-error-invalid-request": "Forespørselen mangler en påkrevd parameter, inkluderer en ugyldig parameterverdi, inkluderer en parameter flere ganger, eller er misformet på annet vis.",
+ "mwoauth-oauth2-error-unauthorized-client": "Klienten er ikke autorisert til å be om en autoriseringskode med denne metoden.",
+ "mwoauth-oauth2-error-access-denied": "Ressurseieren eller autoriseringstjeneren avviste forespørselen.",
+ "mwoauth-oauth2-error-unsupported-response-type": "Autoriseringstjeneren støtter ikke henting av autoriseringskode med denne metoden.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "Autoriseringstjeneren er for tiden ikke i stand til å behandle forespørselen på grunn av overbelastning eller vedlikehold av tjeneren.",
+ "mwoauth-oauth2-error-invalid-client": "Klientautentisering feilet (f.eks. ukjent klient, ingen klientautentisering inkludert eller ustøttet autentiseringsmetode)",
+ "mwoauth-oauth2-error-request-not-verified": "Prøvde å hente godkjent egenskap før forespørselen ble bekreftet",
+ "mwoauth-oauth2-invalid-access-token": "Ugyldig tilgangsnøkkel",
+ "mwoauth-consumer-access-no-user": "Konsumentgodkjenning må være knyttet til en gyldig bruker, bruker med ID 0 gitt",
+ "mwoauth-login-required-reason": "Du må logge inn med kontoen din på {{SITENAME}} for å autorisere tilgang til applikasjoner.",
+ "mwoauthconsumer-consumer-view": "Vis denne konsumenten",
+ "mwoauthconsumer-application-view": "Vis denne applikasjonen"
+}
diff --git a/OAuth/i18n/nds-nl.json b/OAuth/i18n/nds-nl.json
new file mode 100644
index 00000000..ef8e9f1b
--- /dev/null
+++ b/OAuth/i18n/nds-nl.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Servien"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Koppelde topassingen:",
+ "mwoauth-prefs-managegrantslink": "$1 koppelde {{PLURAL:$1|topassing|topassingen|0=topassingen}} beheyren",
+ "mwoauthconsumerregistration-navigation": "Navigasie:",
+ "mwoauthconsumerregistration-main": "Startsyde",
+ "mwoauthconsumerregistration-description": "Beskryving",
+ "mwoauthconsumerregistration-stage": "Staotus",
+ "mwoauthmanageconsumers-description": "Beskryving",
+ "oauthlistconsumers": "Lyste mid OAuth-topassingen",
+ "mwoauthlistconsumers-description": "Beskryving",
+ "mwoauthlistconsumers-grants": "Tostemmingen",
+ "oauthmanagemygrants": "Koppelde topassingen beheyren",
+ "mwoauthmanagemygrants-description": "Beskryving",
+ "mwoauthserver-invalid-user": "Um gebruuk te kunnen maken van ekoppelde toepassigen op disse webstee, mu'j n gebruker hebben op alle projekten. A'j n gebruker hebben op alle projekten, ku'j \"$1\" opniej proberen te koppelen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Samenevoegd anmelden noodzakelik, [https://www.mediawiki.org/wiki/Help:OAuth/Errors#E008 E008]</span>",
+ "mwoauth-acceptance-cancelled": "Je hebben ekeuzen \"$1\" gien toegang te geven tot joew gebruker. \"$1\" warkt niet, behalve a'j de toepassige toegang geven. Je kunnen weerummegaon naor \"$1\" of joew ekoppelde toepassigen [[Special:OAuthManageMyGrants|beheren]]."
+}
diff --git a/OAuth/i18n/ne.json b/OAuth/i18n/ne.json
new file mode 100644
index 00000000..fdd51c24
--- /dev/null
+++ b/OAuth/i18n/ne.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "NehalDaveND",
+ "Nirjal stha",
+ "पर्वत सुबेदी",
+ "राम प्रसाद जोशी",
+ "सरोज कुमार ढकाल"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "जडान गरिएका अनुप्रयाेगहरू",
+ "mwoauth-consumer-granttypes": "निवेदन गरिएको अनुदानको प्रकारः",
+ "mwoauth-consumer-reason": "कारण:",
+ "mwoauth-consumer-email-unconfirmed": "तपाईंको खातामा इमेल ठेगाना प्रमाणित भएको छैन ।",
+ "mwoauthconsumerregistration-main": "मुख्य",
+ "mwoauthconsumerregistration-stage": "स्थिति",
+ "mwoauthconsumerregistration-manage": "व्यवस्थापन गर्नुहाेस्",
+ "mwoauthmanageconsumers-main": "मुख्य",
+ "mwoauthmanageconsumers-description": "विवरण",
+ "mwoauthmanageconsumers-reason": "कारण:",
+ "mwoauthlistconsumers-view": "विवरणहरू",
+ "mwoauthlistconsumers-status": "स्थिति",
+ "mwoauthlistconsumers-navigation": "पथ प्रर्दशन",
+ "mwoauthlistconsumers-update-link": "अद्यावधिक उपभोक्ता",
+ "mwoauthmanagemygrants-description": "विवरण",
+ "mwoauth-form-button-cancel": "रद्द गर्ने",
+ "mwoauth-granttype-normal": "विशेष अनुमति अधिकारको लागि निवेदन गर्नुहोस् ।"
+}
diff --git a/OAuth/i18n/nl.json b/OAuth/i18n/nl.json
new file mode 100644
index 00000000..a634312e
--- /dev/null
+++ b/OAuth/i18n/nl.json
@@ -0,0 +1,280 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arent",
+ "Elroy",
+ "Hansmuller",
+ "Kippenvlees1",
+ "Macofe",
+ "Mainframe98",
+ "Mar(c)",
+ "Mathonius",
+ "McDutchie",
+ "MrLeopold",
+ "PiefPafPier",
+ "Robin0van0der0vliet",
+ "Romaine",
+ "SPQRobin",
+ "Servien",
+ "Siebrand",
+ "Sjoerddebruin",
+ "Southparkfan"
+ ]
+ },
+ "mwoauth-desc": "Maakt het mogelijk OAuth 1.0a en OAuth 2.0 te gebruiken voor API-toestemming",
+ "mwoauth-verified": "De toepassing heeft nu namens u toegang tot MediaWiki.\n\nGeef deze controlewaarde op in de toepassing om het proces te voltooien: '''$1'''",
+ "mwoauth-db-readonly": "De OAuth-database is tijdelijk vergrendeld. Probeer het over enkele minuten opnieuw.",
+ "mwoauth-missing-field": "Waarde voor het veld \"$1\" ontbreekt",
+ "mwoauth-invalid-field": "Er is een ongeldige waarde opgegeven voor het veld \"$1\"",
+ "mwoauth-invalid-field-generic": "Er is een ongeldige waarde opgegeven",
+ "mwoauth-field-hidden": "(deze gegevens zijn verborgen)",
+ "mwoauth-field-private": "(deze gegevens zijn persoonlijk)",
+ "mwoauth-prefs-managegrants": "Gekoppelde apps:",
+ "mwoauth-prefs-managegrantslink": "$1 gekoppelde {{PLURAL:$1|toepassing|toepassingen|0=toepassingen}} beheren",
+ "mwoauth-consumer-allwikis": "Alle projecten op deze site",
+ "mwoauth-consumer-key": "Consumersleutel:",
+ "mwoauth-consumer-name": "Naam toepassing:",
+ "mwoauth-consumer-version": "Consumerversie:",
+ "mwoauth-consumer-user": "Uitgever:",
+ "mwoauth-consumer-stage": "Huidige status:",
+ "mwoauth-consumer-email": "E-mailadres voor contact:",
+ "mwoauth-consumer-owner-only-label": "Alleen eigenaar toestaan:",
+ "mwoauth-consumer-description": "Toepassingsbeschrijving:",
+ "mwoauth-consumer-callbackurl": "URL voor OAuth-\"callback\":",
+ "mwoauth-consumer-grantsneeded": "Van toepassing zijnde rechten:",
+ "mwoauth-consumer-required-grant": "Van toepassing op consumer",
+ "mwoauth-consumer-wiki": "Van toepassing op project:",
+ "mwoauth-consumer-wiki-thiswiki": "Huidig project ($1)",
+ "mwoauth-consumer-restrictions": "Gebruiksbeperkingen:",
+ "mwoauth-consumer-restrictions-json": "Gebruiksbeperkingen (JSON):",
+ "mwoauth-consumer-rsakey": "Openbare RSA-sleutel (optioneel):",
+ "mwoauth-consumer-secretkey": "Geheim token consumer:",
+ "mwoauth-consumer-accesstoken": "Toegangstoken:",
+ "mwoauth-consumer-reason": "Reden:",
+ "mwoauth-consumer-email-unconfirmed": "Het e-mailadres van uw account is nog niet bevestigd.",
+ "mwoauth-consumer-email-mismatched": "Het opgegeven e-mailadres moet overeenkomen met dat van uw account.",
+ "mwoauth-consumer-alreadyexists": "Er bestaat al een toepassing met deze combinatie van naam, versie en uitgever",
+ "mwoauth-consumer-alreadyexistsversion": "Er bestaat al een toepassing met deze combinatie van naam en uitgever met een versie die gelijk of hoger is (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Het was niet mogelijk om gegevens van een openstaand toepassingsverzoek bij te werken",
+ "mwoauth-consumer-not-proposed": "De toepassing wordt op dit moment niet voorgesteld",
+ "mwoauth-consumer-not-disabled": "De toepassing is op dit moment niet uitgeschakeld",
+ "mwoauth-consumer-not-approved": "De toepassing is niet goedgekeurd (deze kan uitgeschakeld zijn)",
+ "mwoauth-missing-consumer-key": "Er is geen toepassingssleutel opgegeven.",
+ "mwoauth-invalid-consumer-key": "Er bestaat geen toepassing met deze sleutel.",
+ "mwoauth-invalid-access-token": "Er bestaat geen toegangstoken met de opgegeven sleutel.",
+ "mwoauth-invalid-access-wrongwiki": "De toepassing kan alleen gebruikt worden op het project \"$1\".",
+ "mwoauth-consumer-conflict": "Iemand heeft de eigenschappen van deze toepassing aangepast terwijl u deze aan het bekijken was. U kunt het wijzigingenlogboek bekijken.",
+ "mwoauth-consumer-grantshelp": "Iedere toestemming geeft toegang tot de opgegeven gebruikersrechten die een gebruiker al heeft. Zie de [[Special:ListGrants|tabel met toestemmingen]] voor meer informatie.",
+ "mwoauth-consumer-stage-proposed": "voorgesteld",
+ "mwoauth-consumer-stage-rejected": "afgewezen",
+ "mwoauth-consumer-stage-expired": "vervallen",
+ "mwoauth-consumer-stage-approved": "goedgekeurd",
+ "mwoauth-consumer-stage-disabled": "uitgeschakeld",
+ "mwoauth-consumer-stage-suppressed": "onderdrukt",
+ "oauthconsumerregistration": "Registratie van OAuth-toepassingen",
+ "mwoauthconsumerregistration-navigation": "Navigatie:",
+ "mwoauthconsumerregistration-propose": "Nieuwe consumer voorstellen",
+ "mwoauthconsumerregistration-list": "Uw consumerlijst",
+ "mwoauthconsumerregistration-main": "Startpagina",
+ "mwoauthconsumerregistration-propose-text": "Ontwikkelaars moeten onderstaand formulier gebruiken om een nieuwe OAuth-toepassing voor te stellen (zie de [//www.mediawiki.org/wiki/Extension:OAuth documentatie van de uitbreiding] voor meer details). Na het indienen van dit formulier ontvangt u een token dat uw toepassing gaat gebruiken om zichzelf te identificeren bij MediaWiki. Een OAuth-beheerder moet uw toepassing goedkeuren voor het door andere gebruikers kan worden toegestaan.\n\nEen paar aanbevelingen en opmerkingen:\n* Probeer zo min mogelijk bevoegdheden te gebruiken. Vermijd bevoegdheden die niet echt nodig zijn.\n* Versies zijn van de vorm \"groot.klein.release\" (de laatste twee elementen zijn optioneel) en moeten oplopen als er wijzigingen voor bevoegdheden nodig zijn.\n* Verschaf indien mogelijk een publieke RSA-sleutel (in PEM-formaat); als dat niet mogelijk is, zal een (minder veilig) geheim token moeten worden gebruikt.\n* U kunt een project-ID gebruiken om de toepassing te beperken tot één enkel project op deze site (gebruik \"*\" voor alle projecten).",
+ "mwoauthconsumerregistration-update-text": "Gebruik onderstaand formulier om bepaalde aspecten van de OAuth-toepassing die u beheert bij te werken.\n\nAlle waarden hier overschrijven eerdere waarden. Laat velden niet leeg, tenzij u inderdaad waarden wilt verwijderen.",
+ "mwoauthconsumerregistration-maintext": "Op deze pagina kunnen ontwikkelaars OAuth-toepassingen voorstellen en bijwerken in het register van deze site.\n\nVanaf hier kunt u:\n* [[Special:OAuthConsumerRegistration/propose|een token aanvragen voor een nieuwe toepassing]];\n* [[Special:OAuthConsumerRegistration/list|uw lijst met bestaande toepassingen beheren]].\n\nVoor meer informatie over OAuth kunt u de [https://www.mediawiki.org/wiki/Extension:OAuth uitbreidingsdocumentatie] raadplegen.",
+ "mwoauthconsumerregistration-propose-legend": "Nieuwe OAuth-consumertoepassing",
+ "mwoauthconsumerregistration-update-legend": "OAuth-consumertoepassing bijwerken",
+ "mwoauthconsumerregistration-propose-submit": "Consumer voorstellen",
+ "mwoauthconsumerregistration-update-submit": "Consumer bijwerken",
+ "mwoauthconsumerregistration-none": "U hebt geen controle over OAuth-applicaties.",
+ "mwoauthconsumerregistration-name": "Consumer",
+ "mwoauthconsumerregistration-user": "Uitgever",
+ "mwoauthconsumerregistration-description": "Beschrijving",
+ "mwoauthconsumerregistration-email": "E-mailadres voor contact",
+ "mwoauthconsumerregistration-consumerkey": "Consumersleutel",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Laatste wijziging",
+ "mwoauthconsumerregistration-manage": "beheren",
+ "mwoauthconsumerregistration-resetsecretkey": "Geheime sleutel op een nieuwe waarde instellen",
+ "mwoauthconsumerregistration-proposed": "Uw OAuth-applicatieverzoek is geregistreerd.\n\nU hebt het applicatietoken <strong>$1</strong> toegewezen gekregen en een geheim token <strong>$2</strong>. <em>Bewaar deze gegevens zorgvuldig.</em>",
+ "mwoauthconsumerregistration-updated": "Uw OAuth-applicatieregister is bijgewerkt.",
+ "mwoauthconsumerregistration-secretreset": "U hebt het geheime applicatietoken <strong>$1</strong>toegewezen gekregen. <em>Bewaar deze gegevens zorgvuldig.</em>",
+ "oauthmanageconsumers": "OAuth-toepassingen beheren",
+ "mwoauthmanageconsumers-notloggedin": "U moet aangemeld zijn om toegang te krijgen tot deze pagina.",
+ "mwoauthmanageconsumers-type": "Wachtrijen:",
+ "mwoauthmanageconsumers-showproposed": "Voorgestelde verzoeken",
+ "mwoauthmanageconsumers-showrejected": "Afgewezen verzoeken",
+ "mwoauthmanageconsumers-showexpired": "Verlopen aanvragen",
+ "mwoauthmanageconsumers-linkproposed": "voorgestelde verzoeken",
+ "mwoauthmanageconsumers-linkrejected": "afgewezen verzoeken",
+ "mwoauthmanageconsumers-linkexpired": "verlopen verzoeken",
+ "mwoauthmanageconsumers-linkapproved": "toegekende verzoeken",
+ "mwoauthmanageconsumers-linkdisabled": "uitgeschakelde verzoeken",
+ "mwoauthmanageconsumers-main": "Startpagina",
+ "mwoauthmanageconsumers-maintext": "Deze pagina is bedoeld voor het afhandelen van OAuth-applicatieverzoeken en het beheren van geregistreerde OAuth-applicaties. Zie http://oauth.net voor meer informatie over OAuth.",
+ "mwoauthmanageconsumers-queues": "Kies hieronder een wachtrij voor applicatiebevestiging:",
+ "mwoauthmanageconsumers-q-proposed": "Wachtrij met voorgestelde toepassingsverzoeken",
+ "mwoauthmanageconsumers-q-rejected": "Wachtrij met afgewezen toepassingsverzoeken",
+ "mwoauthmanageconsumers-q-expired": "Wachtrij met verlopen toepassingsverzoeken",
+ "mwoauthmanageconsumers-lists": "Selecteer een consumerstatus uit de onderstaande lijst:",
+ "mwoauthmanageconsumers-l-approved": "Wachtrij met goedgekeurde toepassingen",
+ "mwoauthmanageconsumers-l-disabled": "Lijst met uitgeschakelde toepassingen",
+ "mwoauthmanageconsumers-none-proposed": "Geen voorgestelde toepassingen.",
+ "mwoauthmanageconsumers-none-rejected": "Geen voorgestelde toepassingen.",
+ "mwoauthmanageconsumers-none-expired": "Geen voorgestelde consumers in deze lijst.",
+ "mwoauthmanageconsumers-none-approved": "Er zijn geen consumers die aan deze voorwaarden voldoen.",
+ "mwoauthmanageconsumers-none-disabled": "Er zijn geen toepassingen die aan de criteria voldoen.",
+ "mwoauthmanageconsumers-name": "Consumer",
+ "mwoauthmanageconsumers-user": "Uitgever",
+ "mwoauthmanageconsumers-description": "Beschrijving",
+ "mwoauthmanageconsumers-email": "E-mailadres voor contact",
+ "mwoauthmanageconsumers-consumerkey": "Consumersleutel",
+ "mwoauthmanageconsumers-lastchange": "Laatste wijziging",
+ "mwoauthmanageconsumers-review": "controleren en beheren",
+ "mwoauthmanageconsumers-confirm-text": "Gebruik dit formulier om deze consumer goed te keuren, af te keuren of opnieuw in te schakelen.",
+ "mwoauthmanageconsumers-confirm-legend": "OAuth-consumer beheren",
+ "mwoauthmanageconsumers-action": "Status wijzigen:",
+ "mwoauthmanageconsumers-approve": "Goedgekeurd",
+ "mwoauthmanageconsumers-reject": "Afgewezen",
+ "mwoauthmanageconsumers-rsuppress": "Afgewezen en onderdrukt",
+ "mwoauthmanageconsumers-disable": "Uitgeschakeld",
+ "mwoauthmanageconsumers-dsuppress": "Uitgeschakeld en onderdrukt",
+ "mwoauthmanageconsumers-reenable": "Goedgekeurd",
+ "mwoauthmanageconsumers-reason": "Reden:",
+ "mwoauthmanageconsumers-confirm-submit": "Consumerstatus bijwerken",
+ "mwoauthmanageconsumers-success-approved": "Het verzoek is goedgekeurd.",
+ "mwoauthmanageconsumers-success-rejected": "Het verzoek is afgewezen.",
+ "mwoauthmanageconsumers-success-disabled": "De consumer is uitgeschakeld.",
+ "mwoauthmanageconsumers-success-reanable": "De consumer is opnieuw ingeschakeld.",
+ "mwoauthmanageconsumers-search-name": "toepassingen met deze naam",
+ "mwoauthmanageconsumers-search-publisher": "toepassingen van deze gebruiker",
+ "oauthlistconsumers": "Lijst met OAuth-toepassingen",
+ "mwoauthlistconsumers-legend": "OAuth-toepassingen bekijken",
+ "mwoauthlistconsumers-view": "details",
+ "mwoauthlistconsumers-none": "Er zijn geen toepassingen die aan de criteria voldoen.",
+ "mwoauthlistconsumers-name": "Naam toepassing",
+ "mwoauthlistconsumers-version": "Versie toepassing",
+ "mwoauthlistconsumers-user": "Uitgever",
+ "mwoauthlistconsumers-description": "Beschrijving",
+ "mwoauthlistconsumers-wiki": "Van toepassing op project",
+ "mwoauthlistconsumers-callbackurl": "OAuth callback-URL",
+ "mwoauthlistconsumers-grants": "Toestemmingen",
+ "mwoauthlistconsumers-basicgrantsonly": "(alleen basistoegang)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "alle",
+ "mwoauthlistconsumers-status-proposed": "voorgesteld",
+ "mwoauthlistconsumers-status-approved": "goedgekeurd",
+ "mwoauthlistconsumers-status-disabled": "uitgeschakeld",
+ "mwoauthlistconsumers-status-rejected": "afgewezen",
+ "mwoauthlistconsumers-status-expired": "verlopen",
+ "mwoauthlistconsumers-navigation": "Navigatie:",
+ "mwoauthlistconsumers-rclink": "Recente wijzigingen door deze applicatie",
+ "oauthmanagemygrants": "Gekoppelde toepassingen beheren",
+ "mwoauthmanagemygrants-text": "Op deze pagina worden alle toepassingen weergegeven die toegang hebben tot uw gebruikersaccount. Iedere toepassing kan alleen dat doen waar u deze voor hebt gemachtigd. Als u een toepassing afzonderlijk toegang hebt gegeven tot uw gebruikersaccount op zusterprojecten, dan ziet u hieronder afzonderlijke instellingen voor elk van die projecten.\n\nGekoppelde toepassingen hebben toegang tot uw account via het protocol OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Meer over gekoppelde toepassingen])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigatie:",
+ "mwoauthmanagemygrants-showlist": "Lijst met gekoppelde toepassingen",
+ "mwoauthmanagemygrants-none": "Er zijn geen toepassingen aan uw account gekoppeld.",
+ "mwoauthmanagemygrants-user": "Uitgever:",
+ "mwoauthmanagemygrants-description": "Beschrijving",
+ "mwoauthmanagemygrants-wikiallowed": "Toegestaan op project:",
+ "mwoauthmanagemygrants-grants": "Van toepassing zijnde rechten",
+ "mwoauthmanagemygrants-grantsallowed": "Toegestane rechten:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Van toepassing zijnde rechten toegestaan:",
+ "mwoauthmanagemygrants-review": "toegang beheren",
+ "mwoauthmanagemygrants-revoke": "toegang intrekken",
+ "mwoauthmanagemygrants-grantaccept": "Toegestaan",
+ "mwoauthmanagemygrants-update-text": "Gebruik het onderstaande formulier om de rechten te wijzigen die worden gegeven aan een toepassing om namens u te handelen.",
+ "mwoauthmanagemygrants-revoke-text": "Gebruik het onderstaande formulier om de toestemming van een toepassing om namens u te handelen in te trekken.",
+ "mwoauthmanagemygrants-confirm-legend": "Gekoppelde toepassing beheren",
+ "mwoauthmanagemygrants-update": "Toegang bijwerken",
+ "mwoauthmanagemygrants-renounce": "Machtiging intrekken",
+ "mwoauthmanagemygrants-action": "Statuswijziging:",
+ "mwoauthmanagemygrants-confirm-submit": "Toegangstokenstatus bijwerken",
+ "mwoauthmanagemygrants-success-update": "Uw voorkeuren voor deze toepassing zijn bijgewerkt.",
+ "mwoauthmanagemygrants-success-renounce": "De toegang tot uw account is voor deze toepassing ingetrokken.",
+ "mwoauthmanagemygrants-basic-tooltip": "Waarom kan ik deze toestemming niet bijwerken? Deze toestemming geeft uw gekoppelde toepassing standaard rechten die nodig zijn om goed te kunnen werken. Als u niet wilt dat deze gekoppelde toepassing deze rechten heeft, trek dan de toegang van de toepassing in.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Uw}} bewerkingen gemaakt door deze applicatie",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Uw}} handelingen uitgevoerd door deze applicatie",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|heeft}} een OAuth-toepassing (toepassingssleutel $4) voorgesteld",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|heeft}} een OAuth-toepassing (toepassingssleutel $4) bijgewerkt",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|heeft}} een OAuth-toepassing van $3 (toepassingssleutel $4) goedgekeurd",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|heeft}} een OAuth-toepassing van $3 (toepassingssleutel $4) afgewezen",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|heeft}} een OAuth-toepassing van $3 (toepassingssleutel $4) uitgeschakeld",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|heeft}} een OAuth-toepassing van $3 (toepassingssleutel $4) opnieuw ingeschakeld",
+ "mwoauthconsumer-consumer-logpage": "OAuth-consumerlogboek",
+ "mwoauthconsumer-consumer-logpagetext": "Logboek met goedkeuringen, afwijzingen en uitschakelingen van geregistreerde OAuth-consumers.",
+ "mwoauth-bad-request-missing-params": "Er is helaas iets misgegaan tijdens het instellen van deze gekoppelde toepassing. Neem contact op met de ontwikkelaar.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Parameters voor OAuth ontbreken, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Er is helaas iets misgegaan. Neem contact op met de maker van de toepassing om dit probleem op te lossen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Onbekende URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Er is helaas iets misgegaan. [$1 Neem contact op] met de maker van de toepassing om dit probleem op te lossen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Onbekende URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Er is geen goedgekeurde toestemming gevonden voor dit autorisatietoken.",
+ "mwoauthdatastore-request-token-not-found": "Er is helaas iets misgegaan tijdens het koppelen van deze toepassing.\nGa terug en probeer uw account opnieuw te koppelen, of neem contact op met de maker van de toepassing.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-token niet gevonden, $1</span>",
+ "mwoauthdatastore-bad-token": "Er is geen token gevonden dat overeenkomt met uw verzoek.",
+ "mwoauthdatastore-bad-source-ip": "Het verzoek kwam van een ongeldig IP-adres.",
+ "mwoauthdatastore-bad-verifier": "De opgegeven verificatiecode is niet geldig.",
+ "mwoauthdatastore-invalid-token-type": "Het type van het aangevraagde token is ongeldig.",
+ "mwoauthgrants-general-error": "Er is een fout opgetreden in uw OAth-verzoek.",
+ "mwoauthserver-bad-consumer": "\"$1\" is niet toegestaan als gekoppelde toepassing. [$2 Neem contact op] met de maker van de toepassing.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Gekoppelde OAuth-toepassing niet toegestaan, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Er is helaas iets misgegaan tijdens het koppelen van deze toepassing.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Onbekende OAuth-sleutel, $1</span>",
+ "mwoauthserver-insufficient-rights": "Uw account mag geen toepassingen koppelen. Neem contact op met de beheerder als u wilt weten waarom dit zo is.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Onvoldoende gebruikersrechten voor OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Ongeldig token in uw verzoek.",
+ "mwoauthserver-invalid-user": "Om gebruik te kunnen maken van gekoppelde toepassingen op deze site, moet u een account hebben voor alle projecten. Wanneer u een account op alle projecten hebt, kunt u \"$1\" opnieuw proberen te koppelen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Samengevoegd aanmelden noodzakelijk, $2</span>",
+ "mwoauth-invalid-authorization-title": "OAuth-autorisatiefout",
+ "mwoauth-invalid-authorization": "De autorisatieheaders in uw verzoek zijn niet geldig: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "De autorisatieheaders in uw verzoek zijn niet geldig voor $1",
+ "mwoauth-invalid-authorization-invalid-user": "De autorisatieheaders in uw verzoek zijn voor een gebruiker die hier niet bestaat",
+ "mwoauth-invalid-authorization-wrong-user": "De autorisatieheaders in uw verzoek zijn voor een andere gebruiker",
+ "mwoauth-invalid-authorization-not-approved": "De toepassing die u probeert te koppelen lijkt onjuist te zijn opgezet. Neem voor hulp contact op met de maker van \"$1\".",
+ "mwoauth-invalid-authorization-blocked-user": "De autorisatieheaders in uw verzoek zijn voor een gebruiker die is geblokkeerd",
+ "mwoauth-form-description-allwikis": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u de volgende acties uit te voeren op alle projecten van deze site:\n\n$4",
+ "mwoauth-form-description-onewiki": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u de volgende acties uit te voeren op ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u toegang te hebben tot informatie op alle projecten van deze site. Er zullen met uw account geen wijzigingen gedaan worden.",
+ "mwoauth-form-description-onewiki-nogrants": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u toegang te hebben tot informatie op ''$4''. Er zullen met uw account geen wijzigingen gedaan worden.",
+ "mwoauth-form-description-allwikis-privateinfo": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u toegang te hebben tot informatie over u, met inbegrip van uw echte naam en e-mailadres, op alle projecten van deze site. Er zullen met uw account geen wijzigingen gedaan worden.",
+ "mwoauth-form-description-onewiki-privateinfo": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u toegang te hebben tot informatie over u, met inbegrip van uw echte naam en e-mailadres, op ''$4''. Er zullen met uw account geen wijzigingen gedaan worden.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u toegang te hebben tot informatie over u, met inbegrip van uw e-mailadres, op alle projecten van deze site. Er zullen met uw account geen wijzigingen gedaan worden.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Hallo $1,\n\nOm uw verzoek te voltooien, heeft '''$2''' toestemming nodig om namens u toegang te hebben tot informatie over u, met inbegrip van e-mailadres, op ''$4''. Er zullen met uw account geen wijzigingen gedaan worden.",
+ "mwoauth-form-button-approve": "Toestaan",
+ "mwoauth-form-button-cancel": "Annuleren",
+ "mwoauth-error": "Fout bij koppelen toepassing",
+ "mwoauth-grants-heading": "Aangevraagde rechten:",
+ "mwoauth-grants-nogrants": "De toepassing heeft geen rechten aangevraagd.",
+ "mwoauth-acceptance-cancelled": "U hebt gekozen \"$1\" geen toegang te geven tot uw account. \"$1\" werkt niet, tenzij u de toepassing toegang geeft. U kunt teruggaan naar \"$1\" of uw gekoppelde toepassingen [[Special:OAuthManageMyGrants|beheren]].",
+ "grant-mwoauth-authonly": "Alleen verificatie van gebruikersidentiteit, geen mogelijkheid om pagina's te lezen of namens een gebruiker te handelen.",
+ "mwoauth-oauth-exception": "Er is een fout opgetreden in het OAuth-protocol: $1",
+ "mwoauth-callback-not-oob": "oauth_callback moet worden ingesteld, en moet worden ingesteld op \"oob\" (hoofdlettergevoelig)",
+ "right-mwoauthproposeconsumer": "Nieuwe OAuth-consumers voorstellen",
+ "right-mwoauthupdateownconsumer": "OAuth-consumers die u beheert bijwerken",
+ "right-mwoauthmanageconsumer": "OAuth-consumers beheren",
+ "right-mwoauthsuppress": "OAuth-consumers onderdrukken",
+ "right-mwoauthviewsuppressed": "Onderdrukte OAuth-consumers bekijken",
+ "right-mwoauthviewprivate": "Beschermde OAuth-gegevens bekijken",
+ "right-mwoauthmanagemygrants": "OAuth-bevoegdheden beheren",
+ "action-mwoauthmanageconsumer": "OAuth-consumers te beheren",
+ "action-mwoauthmanagemygrants": "uw OAuth-bevoegdheden te beheren",
+ "action-mwoauthproposeconsumer": "nieuwe OAuth-consumers voor te stellen",
+ "action-mwoauthupdateownconsumer": "OAuth-consumers die u beheert bij te werken",
+ "action-mwoauthviewsuppressed": "onderdrukte OAuth-consumers te bekijken",
+ "mwoauth-botpasswords-note": "<strong>Opmerking:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> is veiliger dan botwachtwoorden en verdient de voorkeur wanneer de bot dit ondersteunt.",
+ "mwoauth-api-module-disabled": "De \"$1\" module is niet beschikbaar met OAuth.",
+ "echo-category-title-oauth-owner": "OAuth-ontwikkeling",
+ "echo-pref-tooltip-oauth-owner": "Meld mij over gebeurtenissen rond OAuth-applicaties die ik heb aangemaakt.",
+ "echo-category-title-oauth-admin": "OAuth-beheer",
+ "echo-pref-tooltip-oauth-admin": "Meld mij gebeurtenissen aangaande het beoordelen van OAuth-applicaties.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|stelde}} een nieuwe OAuth-applicatie voor: $2",
+ "notification-oauth-app-update-title": "$1 heeft de OAuth-applicatie $2 {{GENDER:$1|bijgewerkt}}",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie ($2) goedgekeurd",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie ($2) afgewezen",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie ($2) uitgeschakeld",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie ($2) opnieuw ingeschakeld",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|stelde}} een nieuwe OAuth-applicatie op {{SITENAME}} voor",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|heeft}} een OAuth-applicatie op {{SITENAME}} bijgewerkt",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie op {{SITENAME}} goedgekeurd",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie op {{SITENAME}} afgewezen",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie op {{SITENAME}} uitgeschakeld",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|heeft}} {{GENDER:$3|uw}} OAuth-applicatie op {{SITENAME}} opnieuw ingeschakeld",
+ "notification-oauth-app-propose-primary-link": "Herzie applicatie",
+ "notification-oauth-app-update-primary-link": "Herzie applicatie",
+ "notification-oauth-app-approve-primary-link": "Bekijk applicatie",
+ "notification-oauth-app-reject-primary-link": "Bekijk applicatie",
+ "notification-oauth-app-disable-primary-link": "Bekijk applicatie",
+ "notification-oauth-app-reenable-primary-link": "Bekijk applicatie",
+ "notification-oauth-app-body": "Reden: $1",
+ "mwoauth-oauth2-granttype-client-credentials": "Aanmeldgegevens klant"
+}
diff --git a/OAuth/i18n/nn.json b/OAuth/i18n/nn.json
new file mode 100644
index 00000000..59783dfb
--- /dev/null
+++ b/OAuth/i18n/nn.json
@@ -0,0 +1,43 @@
+{
+ "@metadata": {
+ "authors": [
+ "Njardarlogar"
+ ]
+ },
+ "mwoauth-desc": "Gjer det mogeleg å nytta OAuth 1.0a for å gje API-løyve",
+ "mwoauth-prefs-managegrants": "Tilkopla småprogram:",
+ "mwoauth-prefs-managegrantslink": "Handsam {{PLURAL:$1|eitt tilkopla småprogram|$1 tilkopla småprogram}}",
+ "mwoauth-consumer-allwikis": "Alle prosjekta på denne nettstaden",
+ "mwoauth-consumer-name": "Programnamn:",
+ "mwoauth-consumer-user": "Utgjevar:",
+ "mwoauth-consumer-description": "Programskildring:",
+ "mwoauth-consumer-stage-proposed": "framlagd",
+ "mwoauth-consumer-stage-approved": "godkjent",
+ "mwoauthconsumerregistration-user": "Utgjevar",
+ "mwoauthconsumerregistration-manage": "handsam",
+ "mwoauthmanageconsumers-user": "Utgjevar",
+ "oauthlistconsumers": "List opp OAuth-småprogram",
+ "mwoauthlistconsumers-legend": "Bla gjennom OAuth-småprogram",
+ "mwoauthlistconsumers-view": "detaljar",
+ "mwoauthlistconsumers-name": "Programnamn:",
+ "mwoauthlistconsumers-user": "Utgjevar",
+ "mwoauthlistconsumers-description": "Skildring",
+ "mwoauthlistconsumers-wiki": "For prosjekt",
+ "mwoauthlistconsumers-grants": "For løyve",
+ "oauthmanagemygrants": "Handsam tilkopla småprogram",
+ "mwoauthmanagemygrants-showlist": "Liste over tilkopla småprogram",
+ "mwoauthmanagemygrants-none": "Det finst ingen småprogram knytte til kontoen din.",
+ "mwoauthmanagemygrants-user": "Utgjevar:",
+ "mwoauthmanagemygrants-description": "Skildring",
+ "mwoauthmanagemygrants-wikiallowed": "Tillate på prosjekt:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Mogelege løyve:",
+ "mwoauthmanagemygrants-review": "handsam tilgang",
+ "mwoauthmanagemygrants-revoke": "ta frå tilgang",
+ "mwoauthmanagemygrants-grantaccept": "Gjeve",
+ "mwoauthmanagemygrants-update-text": "Nytt skjemaet under for å endra løyva gjevne til eit småprogram for å handla på dine vegner.",
+ "mwoauthmanagemygrants-confirm-legend": "Handsam tilkopla småprogram",
+ "mwoauthmanagemygrants-update": "Oppdater løyve",
+ "mwoauth-form-description-allwikis": "Hei $1,\n\nfor å fullføra førespurnaden treng '''$2''' løyve til å utføra desse handlingane på dine vegner på alle prosjekta på denne nettstaden:\n\n$4",
+ "mwoauth-form-description-onewiki": "Hei $1,\n\nfor å fullføra førespurnaden treng '''$2''' løyve til å utføra desse handlingane på dine vegner på ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hei $1,\n\nFor å fullføra førespurnaden din treng '''$2''' løyve til å henta informasjon på dine vegner frå alle prosjekt på denne nettstaden. Ingen endringar vil gjerast med kontoen din."
+}
diff --git a/OAuth/i18n/oc.json b/OAuth/i18n/oc.json
new file mode 100644
index 00000000..e07c2766
--- /dev/null
+++ b/OAuth/i18n/oc.json
@@ -0,0 +1,123 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cedric31",
+ "Quentí"
+ ]
+ },
+ "mwoauth-desc": "Autoriza l’utilizacion de OAuth 1.0a per l’autentificacion de l’API",
+ "mwoauth-verified": "Ara, l’aplicacion pòt accedir a MediaWiki en vòstre nom.\n\nPer acabar lo processus, provesissètz aquesta valor de verificacion a l’aplicacion : ''' $1 '''",
+ "mwoauth-missing-field": "Valor mancanta pel camp « $1 »",
+ "mwoauth-invalid-field": "Valor invalida provesida pel camp « $1 »",
+ "mwoauth-invalid-field-generic": "Valor invalida provesida",
+ "mwoauth-field-hidden": "(aquesta informacion es amagada)",
+ "mwoauth-field-private": "(aquesta informacion es privada)",
+ "mwoauth-prefs-managegrants": "Aplicacions connectadas :",
+ "mwoauth-prefs-managegrantslink": "Gerir $1 {{PLURAL:$1|aplicacion connectada|aplicacions connectadas}}",
+ "mwoauth-consumer-allwikis": "Totes los projèctes sus aqueste site",
+ "mwoauth-consumer-key": "Clau del consomator :",
+ "mwoauth-consumer-name": "Nom de l'aplicacion :",
+ "mwoauth-consumer-version": "Version del consomator :",
+ "mwoauth-consumer-user": "Editor :",
+ "mwoauth-consumer-stage": "Estatut actual :",
+ "mwoauth-consumer-email": "Adreça de corrièr electronic de contacte :",
+ "mwoauth-consumer-description": "Descripcion de l'aplicacion :",
+ "mwoauth-consumer-callbackurl": "URl « de rapèl » per OAuth :",
+ "mwoauth-consumer-grantsneeded": "Dreits aplicables :",
+ "mwoauth-consumer-required-grant": "Aplicable al consomator",
+ "mwoauth-consumer-wiki": "Projècte aplicable :",
+ "mwoauth-consumer-wiki-thiswiki": "Projècte actual ($1)",
+ "mwoauth-consumer-restrictions": "Limitacions d’utilizacion :",
+ "mwoauth-consumer-restrictions-json": "Limitacions d’utilizacion (JSON) :",
+ "mwoauth-consumer-rsakey": "Clau RSA publica (facultatiu) :",
+ "mwoauth-consumer-secretkey": "Geton secret del consomator :",
+ "mwoauth-consumer-accesstoken": "Geton d’accès :",
+ "mwoauth-consumer-reason": "Motiu :",
+ "mwoauth-consumer-email-unconfirmed": "Vòstra adreça de corrièr electronic del compte es pas encara estada confirmada.",
+ "mwoauth-consumer-email-mismatched": "L’adreça de corrièr electronic provesida deu correspondre a la de vòstre compte.",
+ "mwoauth-consumer-alreadyexists": "Un consomator amb aquesta combinason de nom/version/editor existís ja",
+ "mwoauth-consumer-alreadyexistsversion": "Un consomator amb aquesta combinason de nom/editor existís ja amb una version egala o superiora (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Impossible de metre a jorn las informacions per una demanda de consomator en cors",
+ "mwoauth-consumer-not-proposed": "Lo consomator es pas prepausat actualament",
+ "mwoauth-consumer-not-disabled": "Lo consomator es pas desactivat pel moment",
+ "mwoauth-consumer-not-approved": "Lo consomator es pas aprovat (benlèu qu'es estat desactivat)",
+ "mwoauth-missing-consumer-key": "Cap de clau de consomator es pas estada provesida.",
+ "mwoauth-invalid-consumer-key": "Cap de consomator existís pas amb la clau provesida.",
+ "mwoauth-invalid-access-token": "Cap de geton d’accès existís pas per la clau provesida",
+ "mwoauth-invalid-access-wrongwiki": "Lo consomator pòt pas èsser utilizat que sul projècte « $1 ».",
+ "mwoauth-consumer-conflict": "Qualqu’un a modificat los atributs d'aqueste consomator pendent que lo consultavatz. Tornatz ensajar. Podètz tanben verificar lo jornal de las modificacions.",
+ "mwoauth-consumer-stage-proposed": "prepausat",
+ "mwoauth-consumer-stage-rejected": "regetat",
+ "mwoauth-consumer-stage-expired": "expirat",
+ "mwoauth-consumer-stage-approved": "aprovat",
+ "mwoauth-consumer-stage-disabled": "desactivat",
+ "mwoauth-consumer-stage-suppressed": "suprimit",
+ "oauthconsumerregistration": "Inscripcion de consomidor OAuth",
+ "mwoauthconsumerregistration-navigation": "Navigacion :",
+ "mwoauthconsumerregistration-propose": "Prepausar un novèl consomator",
+ "mwoauthconsumerregistration-list": "Ma lista de consomators",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-submit": "Prepausar un consomator",
+ "mwoauthconsumerregistration-update-submit": "Metre a jorn un consomator",
+ "mwoauthconsumerregistration-none": "Contrarotlatz pas cap de consomator OAuth.",
+ "mwoauthconsumerregistration-name": "Consomator",
+ "mwoauthconsumerregistration-user": "Editor",
+ "mwoauthconsumerregistration-description": "Descripcion",
+ "mwoauthconsumerregistration-email": "Corrièr electronic de contacte",
+ "mwoauthconsumerregistration-consumerkey": "Clau del consomator",
+ "mwoauthconsumerregistration-stage": "Estat",
+ "mwoauthconsumerregistration-lastchange": "Darrièr cambiament",
+ "mwoauthconsumerregistration-manage": "gerir",
+ "oauthmanageconsumers": "Gerir los consomidors OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Vos cal èsser connectat per accedir a aquesta pagina.",
+ "mwoauthmanageconsumers-type": "Filas d'espèra :",
+ "mwoauthmanageconsumers-showproposed": "Requèstas prepausadas",
+ "mwoauthmanageconsumers-showrejected": "Requèstas regetadas",
+ "mwoauthmanageconsumers-showexpired": "Requèstas expiradas",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-l-approved": "Lista dels consumidors aprovats actualament",
+ "mwoauthmanageconsumers-l-disabled": "Lista dels consumidors desactivats actualament",
+ "mwoauthmanageconsumers-name": "Consomator",
+ "mwoauthmanageconsumers-user": "Editor",
+ "mwoauthmanageconsumers-description": "Descripcion",
+ "mwoauthmanageconsumers-email": "Corrièr electronic de contacte",
+ "mwoauthmanageconsumers-consumerkey": "Clau del consomator",
+ "mwoauthmanageconsumers-lastchange": "Darrièr cambiament",
+ "mwoauthmanageconsumers-review": "reveire/gerir",
+ "mwoauthmanageconsumers-approve": "Aprovat",
+ "mwoauthmanageconsumers-reject": "Regetat",
+ "mwoauthmanageconsumers-disable": "Desactivat",
+ "mwoauthmanageconsumers-reenable": "Aprovat",
+ "mwoauthmanageconsumers-reason": "Motiu :",
+ "oauthlistconsumers": "Listar las aplicacions OAuth",
+ "mwoauthlistconsumers-view": "detalhs",
+ "mwoauthlistconsumers-status": "Estat",
+ "mwoauth-consumer-stage-any": "totes",
+ "mwoauthlistconsumers-status-proposed": "prepausat",
+ "mwoauthlistconsumers-status-approved": "aprovat",
+ "mwoauthlistconsumers-status-disabled": "desactivat",
+ "mwoauthlistconsumers-status-rejected": "regetat",
+ "mwoauthlistconsumers-status-expired": "expirat",
+ "oauthmanagemygrants": "Gerir las aplicacions connectadas",
+ "mwoauthmanagemygrants-navigation": "Navigacion :",
+ "mwoauthmanagemygrants-user": "Editor :",
+ "mwoauthmanagemygrants-description": "Descripcion",
+ "mwoauthmanagemygrants-wikiallowed": "Autorizat sul projècte :",
+ "mwoauthmanagemygrants-grantaccept": "Acordat",
+ "mwoauthmanagemygrants-revoke-text": "Utilizar lo formulari çaijós per revocar lo dreit, per una aplicacion, d’agir en vòstre nom.",
+ "mwoauthmanagemygrants-confirm-legend": "Gerir las aplicacions connectadas",
+ "mwoauth-error": "Error de connexion de l’aplicacion",
+ "mwoauth-grants-nogrants": "L’aplicacion a pas demandat cap de dreit.",
+ "right-mwoauthproposeconsumer": "prepausar de consomidors OAuth novèls",
+ "right-mwoauthupdateownconsumer": "Metre a jorn los consomidors OAuth",
+ "right-mwoauthmanagemygrants": "Gerir los dreits OAuth",
+ "action-mwoauthmanagemygrants": "Gerir vòstres dreits OAuth",
+ "action-mwoauthproposeconsumer": "prepausar de consomidors OAuth novèls",
+ "action-mwoauthupdateownconsumer": "Metre a jorn los consomidors OAuth",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|a aprovat}} {{GENDER:$3|vòstra}} aplicacion OAuth sus {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|a regetat}} {{GENDER:$3|vòstra}} aplicacion OAuth sus {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|a desactivat}} {{GENDER:$3|vòstra}} aplicacion OAuth sus {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|a reactivat}} {{GENDER:$3|vòstra}} aplicacion OAuth sus {{SITENAME}}",
+ "mwoauthconsumer-consumer-view": "Veire aqueste client",
+ "mwoauthconsumer-application-view": "Veire aquesta aplicacion"
+}
diff --git a/OAuth/i18n/olo.json b/OAuth/i18n/olo.json
new file mode 100644
index 00000000..bc5cb41e
--- /dev/null
+++ b/OAuth/i18n/olo.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mashoi7"
+ ]
+ },
+ "mwoauthconsumerregistration-navigation": "Navigacii:",
+ "mwoauthmanagemygrants-navigation": "Navigacii:"
+}
diff --git a/OAuth/i18n/pam.json b/OAuth/i18n/pam.json
new file mode 100644
index 00000000..15dac1b6
--- /dev/null
+++ b/OAuth/i18n/pam.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Leeheonjin"
+ ]
+ },
+ "mwoauth-grant-createeditmovepage": "Alilan deng bulung (pages)"
+}
diff --git a/OAuth/i18n/pcd.json b/OAuth/i18n/pcd.json
new file mode 100644
index 00000000..66a3ce38
--- /dev/null
+++ b/OAuth/i18n/pcd.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Geoleplubo"
+ ]
+ },
+ "mwoauth-prefs-managegrantslink": "Gérer {{PLURAL:$1|$1 aplitchacion connectée|$1 aplitchacions connectées|0=chés aplitchacions connectées}}",
+ "oauthlistconsumers": "Lisse éd chés aplitchacions OAuth",
+ "oauthmanagemygrants": "Gérer chés aplitchacions connectées",
+ "mwoauthmanagemygrants-confirm-legend": "Gérer chés aplitchacions connectées"
+}
diff --git a/OAuth/i18n/pl.json b/OAuth/i18n/pl.json
new file mode 100644
index 00000000..b99da681
--- /dev/null
+++ b/OAuth/i18n/pl.json
@@ -0,0 +1,331 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alan ffm",
+ "Chrumps",
+ "Peter Bowman",
+ "Rail",
+ "Railfail536",
+ "Rzuwig",
+ "Sp5uhe",
+ "Ty221",
+ "Vlad5250",
+ "Vuh",
+ "WTM"
+ ]
+ },
+ "mwoauth-desc": "Umożliwia korzystanie z OAuth 1.0a i OAuth 2.0 w celu autoryzacji API",
+ "mwoauth-nosubpage-explanation": "OAuth jest mechanizmem pozwalającym zewnętrznym aplikacjom na zidentyfikowanie użytkownika {{GRAMMAR:D.lp|{{SITENAME}}}} lub działanie w jego imieniu po otyrzymnaniu uprawnień od użytkownika.\n\nDo działąnia tej strony potrzeba więcej parametrów. Jeśli {{GENDER:|trafiłeś|trafiłaś}} tutaj z zewnętrznej aplikacji, prawdopodobnie było to spowodowane błędem w tej aplikacji; zaleca się kontakt z jej autorem.",
+ "mwoauth-verified": "Aplikacja ma teraz dostęp do MediaWiki w twoim imieniu.\n\nAby ukończyć proces wprowadź ten kod weryfikacyjny w aplikacji: '''$1'''",
+ "mwoauth-db-readonly": "Bazy danych OAuth jest tymczasowo zablokowana. Proszę spróbować ponownie za kilka minut.",
+ "mwoauth-missing-field": "Brak wartości dla pola „$1”",
+ "mwoauth-invalid-field": "W polu „$1” podano nieprawidłową wartość",
+ "mwoauth-invalid-field-generic": "Podano nieprawidłową wartość",
+ "mwoauth-field-hidden": "(ta informacja jest ukryta)",
+ "mwoauth-field-private": "(ta informacja jest prywatna)",
+ "mwoauth-prefs-managegrants": "Włączone aplikacje:",
+ "mwoauth-prefs-managegrantslink": "Zarządzaj {{PLURAL:$1|włączoną aplikacją|$1 włączonymi aplikacjami}}",
+ "mwoauth-consumer-allwikis": "Wszystkie projekty na tej witrynie",
+ "mwoauth-consumer-key": "Klucz konsumenta:",
+ "mwoauth-consumer-name": "Nazwa aplikacji:",
+ "mwoauth-consumer-version": "Wersja konsumenta:",
+ "mwoauth-consumer-user": "Wydawca:",
+ "mwoauth-consumer-stage": "Aktualny status:",
+ "mwoauth-consumer-email": "Kontaktowy adres e-mail:",
+ "mwoauth-consumer-email-help": "Widoczne tylko dla osób zatwierdzających nowych konsumentów",
+ "mwoauth-consumer-owner-only-label": "Tylko właściciel:",
+ "mwoauth-consumer-owner-only": "Ten konsument będzie wykorzystywany tylko przez $1.",
+ "mwoauth-consumer-owner-only-help": "Wybranie tej opcji sprawi, że konsument zostanie automatycznie zatwierdzony i akceptowany do użytku przez $1. Nie będzie mógł go używać żaden inny użytkownik, a standardowy przepływ autoryzacji nie będzie działał. Akcje wykonane za pośrednictwem tego konsumenta nie będą tagowane.",
+ "mwoauth-consumer-description": "Opis aplikacji:",
+ "mwoauth-consumer-callbackurl": "Adres URL wywołania zwrotnego OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Zezwól konsumentowi na ustalenie wywołania zwrotnego w żądaniu i użyj adresu URL wywołania zwrotnego podanego powyżej jako wymaganego przedrostka.",
+ "mwoauth-consumer-granttypes": "Rodzaj żądanych dostępów:",
+ "mwoauth-consumer-grantsneeded": "Dotyczy uprawnień:",
+ "mwoauth-consumer-required-grant": "Dotyczy konsumenta",
+ "mwoauth-consumer-wiki": "Dotyczy projektu:",
+ "mwoauth-consumer-wiki-thiswiki": "Obecny projekt ($1)",
+ "mwoauth-consumer-restrictions": "Ograniczenia użytkowania:",
+ "mwoauth-consumer-restrictions-json": "Ograniczenia użycia (JSON):",
+ "mwoauth-consumer-rsakey": "Klucz publiczny RSA (opcjonalnie):",
+ "mwoauth-consumer-rsakey-help": "Wprowadź publiczny klucz, aby użyć metody podpisywania RSA-SHA1. Pozostawienie tego pola poskutkuje użyciem HMAC-SHA1 z losowym kluczem bezpieczeństwa. Jeżeli nie jesteś pewien, co wybrać, pozostaw to pole puste.",
+ "mwoauth-consumer-secretkey": "Tajny klucz konsumenta:",
+ "mwoauth-consumer-accesstoken": "Token dostępu:",
+ "mwoauth-consumer-reason": "Powód:",
+ "mwoauth-consumer-developer-agreement": "Przesyłając tę aplikację potwierdzasz, że zastrzegamy sobie prawo do wyłączenia twojej aplikacji, odebrania bądź ograniczenia Twojego oraz aplikacji dostępu do tej strony oraz do podjęcia wszelkich innych działań, które uznamy za właściwe, jeżeli uznamy według naszego osądu, że Twoja aplikacja narusza nasze zasady, wytyczne oraz przewodnie reguły tej strony. Możemy zmienić nasze Wytyczne Aplikacji w każdym momencie bez wcześniejszego powiadamiania o tym i według własnego uznania, gdy uznamy to za konieczne. Dalsze używanie OAuth oznacza akceptację tych zmian.",
+ "mwoauth-consumer-email-unconfirmed": "Adres email twojego konta nie został jeszcze zweryfikowany.",
+ "mwoauth-consumer-email-mismatched": "Wprowadzony adres email musi pasować do adresu twojego konta.",
+ "mwoauth-consumer-alreadyexists": "Konsument z taką kombinacją nazwy/wersji/wydawcy już istnieje",
+ "mwoauth-consumer-alreadyexistsversion": "Konsument z taką kombinacją nazwy/wydawcy już istnieje z identyczną lub wyższą wersją („$1”)",
+ "mwoauth-consumer-not-accepted": "Nie można zaktualizować informacji dla oczekującej propozycji nowego konsumenta",
+ "mwoauth-consumer-not-proposed": "Konsument nie jest aktualnie zaproponowany",
+ "mwoauth-consumer-not-disabled": "Konsument nie jest aktualnie wyłączony",
+ "mwoauth-consumer-not-approved": "Konsument nie został zatwierdzony (mógł zostać wyłączony)",
+ "mwoauth-missing-consumer-key": "Nie podano klucza konsumenta.",
+ "mwoauth-invalid-consumer-key": "Konsument o podanym kluczu nie istnieje.",
+ "mwoauth-invalid-access-token": "Token dostępu o podanym kluczu nie istnieje.",
+ "mwoauth-invalid-access-wrongwiki": "Konsument może być użyty tylko w projekcie „$1”.",
+ "mwoauth-consumer-conflict": "Ktoś zmienił atrybuty tego konsumenta, gdy je przeglądałeś. Spróbuj ponownie. Możesz chcieć sprawdzić rejestr zmian.",
+ "mwoauth-consumer-grantshelp": "Nadawane uprawnienia upoważniają do działań, które możesz już obecnie wykonywać. Zobacz [[Special:ListGrants|tabelę uprawnień]], aby uzyskać więcej informacji.",
+ "mwoauth-consumer-stage-proposed": "proponowane",
+ "mwoauth-consumer-stage-rejected": "odrzucone",
+ "mwoauth-consumer-stage-expired": "przeterminowane",
+ "mwoauth-consumer-stage-approved": "zatwierdzone",
+ "mwoauth-consumer-stage-disabled": "wyłączone",
+ "mwoauth-consumer-stage-suppressed": "ukryte",
+ "oauthconsumerregistration": "Rejestracja konsumenta OAuth",
+ "mwoauthconsumerregistration-navigation": "Nawigacja:",
+ "mwoauthconsumerregistration-propose": "Zaproponuj nowego konsumenta",
+ "mwoauthconsumerregistration-list": "Lista moich konsumentów",
+ "mwoauthconsumerregistration-main": "Główna",
+ "mwoauthconsumerregistration-propose-text": "Deweloperzy powinni używać poniższego formularza w celu zaproponowania nowego konsumenta OAuth (zobacz [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth dokumentację rozszerzenia] po więcej szczegółów). Po przesłaniu tego formularza otrzymasz token, którego używała będzie Twoja aplikacja w celu identyfikacji w MediaWiki. Administrator OAuth będzie musiał zatwierdzić twoją aplikację zanim będzie mogła być autoryzowana przez innych użytkowników.\n\nKika rekomendacji i uwag:\n* Staraj się używać tak niewielu dostępów, jak to możliwe. Unikaj dostępów, które nie są niezbędne w danej chwili.\n* Wersje podawane są w formacie „główne.poboczne.wydanie” (z czego dwa ostatnie są opcjonalne) i wzrastają, gdy zmiany w dostępach są konieczne.\n* Wprowadź publiczny klucz RSA (w formacie PEM) gdy to możliwe; w innym wypadku wykorzystany zostanie (mniej bezpieczny) token bezpieczeństwa.\n* Możesz użyć identyfikatora projektu aby ograniczyć konsumenta do pojedynczego projektu na stronie (użycie „*” da dostęp do wszystkich projektów).",
+ "mwoauthconsumerregistration-update-text": "Użyj poniższego formularza aby zaktualizować aspekty konsumenta OAuth, którego kontrolujesz.\n\nWszystkie wartości podane tutaj nadpiszą poprzednie. Nie zostawiaj pustych pól, chyba że umyślnie chcesz wyczyścić ich wartości.",
+ "mwoauthconsumerregistration-maintext": "Ta strona służy deweloperom do proponowania i aktualizowania aplikacji konsumentów OAuth w rejestrze tej strony.\n\nStąd możesz:\n* [[Special:OAuthConsumerRegistration/propose|Zażądać tokena dla nowego konsumenta]].\n* [[Special:OAuthConsumerRegistration/list|Zarządzać swoimi istniejącymi konsumentami]].\n\nAby dowiedzieć się więcej o OAuth zobacz [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth dokumentację rozszerzenia].",
+ "mwoauthconsumerregistration-propose-legend": "Nowa aplikacja konsumenta OAuth",
+ "mwoauthconsumerregistration-update-legend": "Aktualizuj aplikację konsumenta OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Zaproponuj konsumenta",
+ "mwoauthconsumerregistration-update-submit": "Zaktualizuj konsumenta",
+ "mwoauthconsumerregistration-none": "Nie kontrolujesz żadnych konsumentów OAuth.",
+ "mwoauthconsumerregistration-name": "Konsument",
+ "mwoauthconsumerregistration-user": "Wydawca",
+ "mwoauthconsumerregistration-description": "Opis",
+ "mwoauthconsumerregistration-email": "Adres e-mail",
+ "mwoauthconsumerregistration-consumerkey": "Klucz konsumenta",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Ostatnia zmiana",
+ "mwoauthconsumerregistration-manage": "zarządzaj",
+ "mwoauthconsumerregistration-resetsecretkey": "Zresetuj tajny klucz do nowej wartości",
+ "mwoauthconsumerregistration-proposed": "Twój wniosek o konsumenta OAuth został odebrany.\n\nPrzyznano ci token konsumenta '''$1''' i sekretny token '''$2'''. ''Zapisz je do wykorzystania w przyszłości.''",
+ "mwoauthconsumerregistration-created-owner-only": "Twój konsument OAuth został utworzony.\n\nTwoimi tokenami są:\n; Token konsumenta: $1\n; Sekretny klucz konsumenta: $2\n; Token dostępu: $3\n; Sekretny klucz dostępu: $4\n<em>Zapisz je do wykorzystania w przyszłości.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "Twój konsument OAuth 2.0 został utworzony.\n\nTwoimi tokenami są:\n; Token konsumenta: $1\n; Sekretny klucz konsumenta: $2\n; Token dostępu: $3\n<em>Zapisz je do wykorzystania w przyszłości.</em>",
+ "mwoauthconsumerregistration-updated": "Twoja rejestracja konsumenta OAuth została zaktualizowana.",
+ "mwoauthconsumerregistration-secretreset": "Przypisano ci sekretny token konsumenta '''$1'''. ''Zapisz to do wykorzystania w przyszłości.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Twoje tokeny konsumenta OAuth zostały zresetowane. Nowymi tokenami są:\n; Token konsumenta: $1\n; Sekretny klucz konsumenta: $2\n; Token dostępu: $3\n; Sekretny klucz dostępu: $4\n<em>Zapisz je do wykorzystania w przyszłości.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Twoje tokeny konsumenta OAuth 2.0 zostały zresetowane. Nowymi tokenami są:\n; Token konsumenta: $1\n; Sekretny klucz konsumenta: $2\n; Token dostępu: $3\n<em>Zapisz je do wykorzystania w przyszłości.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Tworzenie aplikacji OAuth jest możliwe dopiero po zweryfikowaniu adresu e‐mail.\nPodaj adres e‐mail i potwierdź go w swoich [[Special:Preferences|ustawieniach użytkownika]].",
+ "oauthmanageconsumers": "Zarządzanie konsumentami OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Musisz być zalogowany, aby uzyskać dostęp do tej strony.",
+ "mwoauthmanageconsumers-type": "Kolejki:",
+ "mwoauthmanageconsumers-showproposed": "Proponowane wnioski",
+ "mwoauthmanageconsumers-showrejected": "Odrzucone wnioski",
+ "mwoauthmanageconsumers-showexpired": "Przeterminowane wnioski",
+ "mwoauthmanageconsumers-linkproposed": "wnioski proponowane",
+ "mwoauthmanageconsumers-linkrejected": "wnioski odrzucone",
+ "mwoauthmanageconsumers-linkexpired": "wnioski przeterminowane",
+ "mwoauthmanageconsumers-linkapproved": "wnioski zatwierdzone",
+ "mwoauthmanageconsumers-linkdisabled": "wyłączone wnioski",
+ "mwoauthmanageconsumers-main": "Główna",
+ "mwoauthmanageconsumers-maintext": "Ta strona służy do zarządzania konsumentami OAuth (zobacz https://oauth.net) oraz zatwierdzania lub odrzucania wniosków o dodanie aplikacji konsumentów.",
+ "mwoauthmanageconsumers-queues": "Wybierz kolejkę potwierdzania konsumentów:",
+ "mwoauthmanageconsumers-q-proposed": "Kolejka proponowanych wniosków konsumentów",
+ "mwoauthmanageconsumers-q-rejected": "Kolejka odrzuconych wniosków konsumentów",
+ "mwoauthmanageconsumers-q-expired": "Kolejka przeterminowanych wniosków konsumentów",
+ "mwoauthmanageconsumers-lists": "Wybierz listę status konsumenta:",
+ "mwoauthmanageconsumers-l-approved": "Lisa ostatnio zaakceptowanych konsumentów",
+ "mwoauthmanageconsumers-l-disabled": "Lista ostatnio wyłączonych konsumentów",
+ "mwoauthmanageconsumers-none-proposed": "Nie ma proponowanych konsumentów na tej liście.",
+ "mwoauthmanageconsumers-none-rejected": "Nie ma proponowanych konsumentów na tej liście.",
+ "mwoauthmanageconsumers-none-expired": "Nie ma proponowanych konsumentów na tej liście.",
+ "mwoauthmanageconsumers-none-approved": "Nie znaleziono konsumentów pasujących do tych kryteriów.",
+ "mwoauthmanageconsumers-none-disabled": "Nie znaleziono konsumentów pasujących do tych kryteriów.",
+ "mwoauthmanageconsumers-name": "Konsument",
+ "mwoauthmanageconsumers-user": "Wydawca",
+ "mwoauthmanageconsumers-description": "Opis",
+ "mwoauthmanageconsumers-email": "Kontaktowy adres e-mail",
+ "mwoauthmanageconsumers-consumerkey": "Klucz konsumenta",
+ "mwoauthmanageconsumers-lastchange": "Ostatnia zmiana",
+ "mwoauthmanageconsumers-review": "przejrzyj/zarządzaj",
+ "mwoauthmanageconsumers-confirm-text": "Użyj tego formularza aby zatwierdzić, odrzucić, wyłączyć lub zrestartować tego konsumenta.",
+ "mwoauthmanageconsumers-confirm-legend": "Zarządzanie konsumentem OAuth",
+ "mwoauthmanageconsumers-action": "Zmiana statusu:",
+ "mwoauthmanageconsumers-approve": "Zatwierdzone",
+ "mwoauthmanageconsumers-reject": "Odrzucone",
+ "mwoauthmanageconsumers-rsuppress": "Odrzucony i ukryty",
+ "mwoauthmanageconsumers-disable": "Wyłączony",
+ "mwoauthmanageconsumers-dsuppress": "Wyłączony i ukryty",
+ "mwoauthmanageconsumers-reenable": "Zatwierdzone",
+ "mwoauthmanageconsumers-reason": "Powód:",
+ "mwoauthmanageconsumers-confirm-submit": "Zaktualizuj status konsumenta",
+ "mwoauthmanageconsumers-success-approved": "Zgłoszenie zostało zaakceptowane.",
+ "mwoauthmanageconsumers-success-rejected": "Zgłoszenie zostało odrzucone.",
+ "mwoauthmanageconsumers-success-disabled": "Konsument został wyłączony.",
+ "mwoauthmanageconsumers-success-reanable": "Konsument został zrestartowany.",
+ "mwoauthmanageconsumers-search-name": "konsumenci o tej samej nazwie",
+ "mwoauthmanageconsumers-search-publisher": "konsumenci tego użytkownika",
+ "oauthlistconsumers": "Lista aplikacji OAuth",
+ "mwoauthlistconsumers-legend": "Przegląd aplikacji OAuth",
+ "mwoauthlistconsumers-view": "szczegóły",
+ "mwoauthlistconsumers-none": "Nie znaleziono aplikacji pasujących do tych kryteriów.",
+ "mwoauthlistconsumers-name": "Nazwa aplikacji",
+ "mwoauthlistconsumers-version": "Wersja konsumenta",
+ "mwoauthlistconsumers-user": "Wydawca",
+ "mwoauthlistconsumers-description": "Opis",
+ "mwoauthlistconsumers-wiki": "Dotyczy projektu",
+ "mwoauthlistconsumers-callbackurl": "Adres URL wywołania zwrotnego OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Zezwól konsumentowi na ustalenie wywołania zwrotnego w żądaniu i użyj adresu URL wywołania zwrotnego podanego powyżej jako wymaganego przedrostka.",
+ "mwoauthlistconsumers-grants": "Zastosowane uprawnienia",
+ "mwoauthlistconsumers-basicgrantsonly": "(tylko podstawowy dostęp)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "dowolny",
+ "mwoauthlistconsumers-status-proposed": "proponowane",
+ "mwoauthlistconsumers-status-approved": "zatwierdzone",
+ "mwoauthlistconsumers-status-disabled": "wyłączone",
+ "mwoauthlistconsumers-status-rejected": "odrzucone",
+ "mwoauthlistconsumers-status-expired": "przeterminowane",
+ "mwoauthlistconsumers-navigation": "Nawigacja:",
+ "mwoauthlistconsumers-update-link": "Zaktualizuj konsumenta",
+ "mwoauthlistconsumers-manage-link": "Zarządzaj konsumentem",
+ "mwoauthlistconsumers-rclink": "Ostatnie zmiany wykonane za pośrednictwem tej aplikacji",
+ "oauthmanagemygrants": "Zarządzanie włączonymi aplikacjami",
+ "mwoauthmanagemygrants-text": "Poniższa strona zawiera listę wszystkich aplikacji, które mogą korzystać z Twojego konta. Zakres uprawnień każdej aplikacji jest ograniczony przez dostępy, jakich jej {{GENDER:|udzieliłeś|udzieliłaś}} podczas autoryzacji do działania w Twoim imieniu. Jeżeli oddzielnie {{GENDER:|autoryzowałeś|autoryzowałaś}} dostęp aplikacji do swojego konta na innym siostrzanym projekcie, poniżej zobaczysz oddzielną konfigurację dla każdego z tych projektów.\n\nPołączone aplikacje mają dostęp do twojego konta za pomocą protokołu OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Dowiedz się więcej o połączonych aplikacjach])</span>",
+ "mwoauthmanagemygrants-navigation": "Nawigacja:",
+ "mwoauthmanagemygrants-showlist": "Lista połączonych aplikacji",
+ "mwoauthmanagemygrants-none": "Nie ma aplikacji związanych z Twoim kontem.",
+ "mwoauthmanagemygrants-user": "Wydawca:",
+ "mwoauthmanagemygrants-description": "Opis",
+ "mwoauthmanagemygrants-wikiallowed": "Dozwolone w projekcie:",
+ "mwoauthmanagemygrants-grants": "Zastosowane dostępy",
+ "mwoauthmanagemygrants-grantsallowed": "Dostępne uprawnienia",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Przydzielone uprawnienia:",
+ "mwoauthmanagemygrants-review": "zarządzanie dostępem",
+ "mwoauthmanagemygrants-revoke": "usuń dostęp",
+ "mwoauthmanagemygrants-grantaccept": "Przyznane",
+ "mwoauthmanagemygrants-update-text": "Użyj poniższego formularza, aby zarządzać uprawnieniami do działania w twoim imieniu przyznanymi aplikacjom .",
+ "mwoauthmanagemygrants-revoke-text": "Użyj poniższego formularza, aby anulować aplikacji dostęp do działania w twoim imieniu.",
+ "mwoauthmanagemygrants-confirm-legend": "Zarządzaj połączonymi aplikacjami",
+ "mwoauthmanagemygrants-update": "Aktualizuj",
+ "mwoauthmanagemygrants-renounce": "Anuluj dostęp",
+ "mwoauthmanagemygrants-action": "Zmień status",
+ "mwoauthmanagemygrants-confirm-submit": "Zaktualizuj status tokena dostępu",
+ "mwoauthmanagemygrants-success-update": "Ustawienia dla tej aplikacji zostały zaktualizowane.",
+ "mwoauthmanagemygrants-success-renounce": "Dostęp tej aplikacji do twojego konta został anulowany.",
+ "mwoauthmanagemygrants-basic-tooltip": "Dlaczego nie możesz zaktualizować tego dostępu? Daje on twojej połączonej aplikacji podstawowe uprawnienia, których ta wymaga do prawidłowego działania. Jeżeli nie chcesz, aby ta aplikacja miała te uprawnienia, powinieneś odebrać jej dostęp.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Dlaczego nie możesz zaktualizować tego dostępu? Jeżeli nie chcesz, aby ta aplikacja miała te uprawnienia, powinieneś odebrać jej dostęp.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Twoje}} edycje za pośrednictwem tej aplikacji",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Twoje}} operacje za pośrednictwem tej aplikacji",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|zaproponował|zaproponowała}} konsumenta OAuth (klucz konsumenta $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|zaktualizował|zaktualizowała}} konsumenta OAuth (klucz konsumenta $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|zatwierdził|zatwierdziła}} konsumenta OAuth autorstwa $3 (klucz konsumenta $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|odrzucił|odrzuciła}} konsumenta OAuth autorstwa $3 (klucz konsumenta $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|wyłączył|wyłączyła}} konsumenta OAuth autorstwa $3 (klucz konsumenta $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|zrestartował|zrestartowała}} konsumenta OAuth autorstwa $3 (klucz konsumenta $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|stworzył|stworzyła}} konsumenta OAuth na własny użytek (klucz konsumenta $4)",
+ "log-action-filter-mwoauthconsumer": "Rodzaj operacji konsumenta OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Zatwierdzenie konsumenta OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Utworzenie konsumenta OAuth tylko dla właściciela",
+ "log-action-filter-mwoauthconsumer-disable": "Wyłączenie konsumenta OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Propozycja konsumenta OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Restartowanie konsumenta OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Odrzucenie konsumenta OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Aktualizacja konsumenta OAuth",
+ "mwoauthconsumer-consumer-logpage": "Rejestr konsumentów OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Rejestr zatwierdzonych, odrzuconych oraz wyłączonych zarejestrowanych konsumentów OAuth.",
+ "mwoauth-bad-request-missing-params": "Przepraszamy, coś poszło nie tak podczas konfiguracji połączonej aplikacji. Skontaktuj się z autorem aplikacji.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Brakujące parametry OAuth – $1</span>",
+ "mwoauth-bad-request-invalid-action": "Przepraszamy, coś poszło nie tak. Musisz skontaktować się z autorem aplikacji aby uzyskać pomoc.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nieznany adres URL – $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Przepraszamy, coś poszło nie tak. Musisz [$1 skontaktować się] z autorem aplikacji aby uzyskać pomoc.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nieznany adres URL – $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Nie odnaleziono zatwierdzonego dostępu dla tego tokena uwierzytelniającego.",
+ "mwoauthdatastore-request-token-not-found": "Przepraszamy, coś poszło nie tak podczas łączenia z ta aplikacją.\nWróć i spróbuj połączyć swoje konto ponownie, lub skontaktuj się z autorem aplikacji.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nie odnaleziono tokena OAuth – $1</span>",
+ "mwoauthdatastore-callback-not-found": "Nie odnaleziono adresu URL wywołania zwrotnego OAuth w pamięci podręcznej. Prawdopodobnie jest to błąd w sposobie w jaki aplikacja wykonuje żądania do serwera.",
+ "mwoauthdatastore-request-token-already-used": "To żądanie zostało już wykonane i nie można go przesłać ponownie.\nWróć do aplikacji i spróbuj ponownie połączyć ją ze swoim kontem, lub skontaktuj się z autorem aplikacji.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Token OAuth znajduje się już w użytku – $1</span>",
+ "mwoauthdatastore-bad-token": "Nie odnaleziono tokena pasującego do Twojego żądania.",
+ "mwoauthdatastore-bad-source-ip": "Wniosek pochodzi z nieprawidłowego adresu IP.",
+ "mwoauthdatastore-bad-verifier": "Podany kod weryfikacyjny jest nieprawidłowy.",
+ "mwoauthdatastore-invalid-token-type": "Żądany typ tokenu jest nieprawidłowy.",
+ "mwoauthgrants-general-error": "Wystąpił błąd w żądaniu OAuth.",
+ "mwoauthserver-bad-consumer": "„$1” nie jest zatwierdzona jako połączona aplikacja. [$2 Skontaktuj się] z autorem aplikacji, aby uzyskać pomoc.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Połączona aplikacja OAuth nie jest zatwierdzona – $3</span>",
+ "mwoauthserver-bad-consumer-key": "Przepraszamy, coś poszło nie tak podczas łączenia tej aplikacji.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nieznany klucz OAuth – $1</span>",
+ "mwoauthserver-insufficient-rights": "Twoje konto nie może używać połączonych aplikacji. Skontaktuj się z administratorem witryny, aby dowiedzieć się dlaczego.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Niewystarczające uprawnienia użytkownika OAuth – $1</span>",
+ "mwoauthserver-invalid-request-token": "Niewłaściwy token w Twoim żądaniu:",
+ "mwoauthserver-invalid-user": "Aby używać połączonych aplikacji na tej stronie musisz posiadać konto we wszystkich projektach. Gdy będziesz już takie posiadać, możesz spróbować podłączyć „$1” ponownie.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ujednolicone logowanie jest konieczne – $2</span>",
+ "mwoauthserver-consumer-no-secret": "Przepraszamy, coś poszło nie tak podczas łączenia tej aplikacji.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konsument nie ma sekretnego klucza – $1</span>",
+ "mwoauthserver-consumer-owner-only": "„$1” jest aplikacją tylko do użytku przez jej autora. Aby uzyskać token dostępu zobacz [[$2]]/\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konsument jest do użytku tylko przez autora – $3</span>",
+ "mwoauth-invalid-authorization-title": "Błąd autoryzacji OAuth",
+ "mwoauth-invalid-authorization": "Nagłówki autoryzacji w twoim żądaniu są nieprawidłowe: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Nagłówki autoryzacji w twoim żądaniu są nieprawidłowe dla $1",
+ "mwoauth-invalid-authorization-invalid-user": "Nagłówki autoryzacji w twoim żądaniu odnoszą się do użytkownika, który tutaj nie istnieje",
+ "mwoauth-invalid-authorization-wrong-user": "Nagłówki autoryzacji w twoim żądaniu są dla innego użytkownika",
+ "mwoauth-invalid-authorization-not-approved": "Aplikacja którą próbujesz połączyć wydaje się być nieprawidłowo ustawione. Skontaktuj się z autorem „$1”, aby uzyskać pomoc.",
+ "mwoauth-invalid-authorization-blocked-user": "Nagłówki autoryzacji w twoim żądaniu są dla zablokowanego użytkownika",
+ "mwoauth-form-description-allwikis": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje uprawnień do wykonywania w Twoim imieniu następujących działań na wszystkich projektach tej witryny:\n\n$4",
+ "mwoauth-form-description-onewiki": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje uprawnień do wykonywania w Twoim imieniu następujących działań na „{{GRAMMAR:Ms.lp|$4}}”:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje dostępu do informacji w Twoim imieniu na wszystkich projektach tej witryny. Z twojego konta nie będą wykonywane żadne zmiany.",
+ "mwoauth-form-description-onewiki-nogrants": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje dostępu do informacji w Twoim imieniu na „{{GRAMMAR:Ms.lp|$4}}”. Z twojego konta nie będą wykonywane żadne zmiany.",
+ "mwoauth-form-description-allwikis-privateinfo": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje dostępu do informacji o tobie, włączając twoje prawdziwe nazwisko i adres e-mail we wszystkich projektach tej witryny. Z twojego konta nie będą wykonywane żadne zmiany.",
+ "mwoauth-form-description-onewiki-privateinfo": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje dostępu do informacji o tobie, włączając twoje prawdziwe nazwisko i adres e-mail na „{{GRAMMAR:Ms.lp|$4}}”. Z twojego konta nie będą wykonywane żadne zmiany.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje dostępu do informacji o tobie, włączając twój adres e-mail we wszystkich projektach tej witryny. Z twojego konta nie będą wykonywane żadne zmiany.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "{{GENDER:$1|Użytkowniku|Użytkowniczko}} $1,\n\nAby wykonać żądanie aplikacja '''$2''' potrzebuje dostępu do informacji o tobie, włączając twój adres e-mail na „{{GRAMMAR:Ms.lp|$4}}”. Z twojego konta nie będą wykonywane żadne zmiany.",
+ "mwoauth-form-button-approve": "Zezwól",
+ "mwoauth-form-button-cancel": "Anuluj",
+ "mwoauth-error": "Błąd połączenia z aplikacją",
+ "mwoauth-grants-heading": "Wymagane uprawnienia:",
+ "mwoauth-grants-nogrants": "Aplikacja nie wymaga żadnych uprawnień.",
+ "mwoauth-acceptance-cancelled": "{{GENDER:|Odmówiłeś|Odmówiłaś}} dostępu „$1” do Twojego konta. „$1” nie będzie działać, dopóki na to nie zezwolisz. Możesz wrócić do „$1” lub [[Special:OAuthManageMyGrants|zarządzać]] połączonymi aplikacjami.",
+ "mwoauth-granttype-normal": "Uwierzytelnianie z konkretnymi uprawnieniami.",
+ "grant-mwoauth-authonly": "Tylko uwierzytelnianie, bez dostępu do czytania stron lub działania w imieniu użytkownika.",
+ "grant-mwoauth-authonlyprivate": "Tylko uwierzytelnianie wraz z dostępem do prawdziwego nazwiska oraz adresu e-mail. Bez dostępu do czytania stron lub działania w imieniu użytkownika.",
+ "mwoauth-listgrants-extra-summary": "== Dostępy specyficzne dla OAuth ==\nPoniższe dodatkowe dostępy są przeznaczone dla użytkowników rozszerzenia OAuth.",
+ "mwoauth-oauth-exception": "Wystąpił błąd w protokole OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback musi być ustawiony i jego wartością musi być „oob” (rozróżniana wielkość liter)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback musi być ustawiony i jego wartością musi być „oob” (rozróżniana wielkość liter) lub skonfigurowany callback musi być prefiksem dostarczonego callbacka.",
+ "right-mwoauthproposeconsumer": "Proponowanie nowych konsumentów OAuth",
+ "right-mwoauthupdateownconsumer": "Aktualizowanie kontrolowanych konsumentów OAuth",
+ "right-mwoauthmanageconsumer": "Zarządzanie konsumentami OAuth",
+ "right-mwoauthsuppress": "Utajnianie konsumentów OAuth",
+ "right-mwoauthviewsuppressed": "Podgląd utajnionych konsumentów OAuth",
+ "right-mwoauthviewprivate": "Podgląd prywatnych danych OAuth",
+ "right-mwoauthmanagemygrants": "Zarządzanie uprawnieniami OAuth",
+ "action-mwoauthmanageconsumer": "zarządzania konsumentami OAuth",
+ "action-mwoauthsuppress": "utajniania konsumentów OAuth",
+ "action-mwoauthmanagemygrants": "zarządzania uprawnieniami OAuth",
+ "action-mwoauthproposeconsumer": "proponowania nowych konsumentów OAuth",
+ "action-mwoauthupdateownconsumer": "aktualizowania kontrolowanych konsumentów OAuth",
+ "action-mwoauthviewprivate": "podglądu prywatnych danych OAuth",
+ "action-mwoauthviewsuppressed": "podglądu utajnionych konsumentów OAuth",
+ "mwoauth-tag-reserved": "Tagi rozpoczynające się od <code>OAuth CID:</code> są zarezerwowane do użytku przez OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Uwaga:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> jest bezpieczniejszą metodą od haseł bota i powinna być ona preferowana zawsze, gdy tylko jest wspierana.",
+ "mwoauth-api-module-disabled": "Moduł „$1” nie jest dostępny z OAuth.",
+ "echo-category-title-oauth-owner": "Aplikacje OAuth",
+ "echo-pref-tooltip-oauth-owner": "Powiadamiaj mnie o zdarzeniach związanych z aplikacjami OAuth, które utworzyłem.",
+ "echo-category-title-oauth-admin": "Administrator OAuth",
+ "echo-pref-tooltip-oauth-admin": "Powiadamiaj mnie o zdarzeniach związanych z przeglądaniem aplikacji OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|zaproponował|zaproponowała}} nową aplikację OAuth: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|zaktualizował|zaktualizowała}} aplikację OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|zatwierdził|zatwierdziła}} {{GENDER:$3|Twoją}} aplikację OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|odrzucił|odrzuciła}} {{GENDER:$3|Twoją}} aplikację OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|wyłączył|wyłączył}} {{GENDER:$3|Twoją}} aplikację OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|zrestartował|zrestartowała}} {{GENDER:$3|Twoją}} aplikację OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|zaproponował|zaproponowała}} nową aplikację OAuth na {{GRAMMAR:D.lp|{{SITENAME}}}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|zaktualizował|zaktualizowała}} aplikację OAuth na {{GRAMMAR:D.lp|{{SITENAME}}}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|zatwierdził|zatwierdziła}} {{GENDER:$3|Twoją}} aplikację OAuth na {{GRAMMAR:D.lp|{{SITENAME}}}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|odrzucił|odrzuciła}} {{GENDER:$3|Twoją}} aplikację OAuth na {{GRAMMAR:D.lp|{{SITENAME}}}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|wyłączył|wyłączyła}} {{GENDER:$3|Twoją}} aplikację OAuth na {{GRAMMAR:D.lp|{{SITENAME}}}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|zrestartował|zrestartowała}} {{GENDER:$3|Twoją}} aplikację OAuth na {{GRAMMAR:D.lp|{{SITENAME}}}}",
+ "notification-oauth-app-propose-primary-link": "Sprawdź aplikację",
+ "notification-oauth-app-update-primary-link": "Sprawdź aplikację",
+ "notification-oauth-app-approve-primary-link": "Zobacz aplikację",
+ "notification-oauth-app-reject-primary-link": "Zobacz aplikację",
+ "notification-oauth-app-disable-primary-link": "Zobacz aplikację",
+ "notification-oauth-app-reenable-primary-link": "Zobacz aplikację",
+ "notification-oauth-app-body": "Powód: $1",
+ "mwoauth-oauth-version": "Wersja protokołu OAuth",
+ "mwoauth-oauth2-is-confidential": "Klient jest prywatny",
+ "mwoauth-oauth2-is-confidential-help": "Prywatny klient jest aplikacją zdolną do przechowywania hasła klienta jako poufnego dla świata. Nieprywatne klienty są mniej bezpieczne.",
+ "mwoauth-oauth2-granttypes": "Dozwolone typy dostępów OAuth 2.0",
+ "mwoauth-oauth2-granttype-auth-code": "Kod autoryzacyjny",
+ "mwoauth-oauth2-granttype-refresh-token": "Odśwież token",
+ "mwoauth-oauth2-granttype-client-credentials": "Poświadczenia konsumenta",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Nie można pozyskać klucza dostępu, użytkownik nie zaakceptował wydania tego klucza",
+ "mwoauth-oauth2-error-user-approval-deny": "Użytkownik odrzucił żądanie od aplikacji konsumenta",
+ "mwoauth-oauth-unsupported-version": "Ten punkt końcowy nie jest dostępny dla OAuth w wersji $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "Zakres „$1” nie jest dostępny dla tej aplikacji",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Konsumenci do użytku tylko przez właściciela muszą mieć dostęp do używania client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Nie udało się pozyskać klucza dostępu: $1",
+ "mwoauth-oauth2-error-server-error": "Serwer autoryzacyjny napotkał niespodziewany warunek, który uniemożliwił mu wykonanie żądania.",
+ "mwoauth-oauth2-error-unauthorized-client": "Klient nie jest autoryzowany aby żądać kodu autoryzacyjnego używając tej metody.",
+ "mwoauth-oauth2-error-access-denied": "Właściciel zasobu lub serwer autoryzujący odrzucił żądanie.",
+ "mwoauth-oauth2-error-unsupported-response-type": "Serwer autoryzujący nie wspiera uzyskiwania kodu autoryzacyjnego używając tej metody.",
+ "mwoauth-oauth2-error-invalid-scope": "Żądany zakres jest nieprawidłowy, nieznany lub zniekształcony.",
+ "mwoauth-oauth2-invalid-access-token": "Nieprawidłowy klucz dostępu.",
+ "mwoauth-login-required-reason": "Musisz zalogować się na {{GRAMMAR:B.lp|{{SITENAME}}}} aby dać tej aplikacji dostęp do swojego konta.",
+ "mwoauthconsumer-consumer-view": "Zobacz tego konsumenta",
+ "mwoauthconsumer-application-view": "Zobacz tę aplikację"
+}
diff --git a/OAuth/i18n/ps.json b/OAuth/i18n/ps.json
new file mode 100644
index 00000000..00d7f88f
--- /dev/null
+++ b/OAuth/i18n/ps.json
@@ -0,0 +1,35 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ahmed-Najib-Biabani-Ibrahimkhel",
+ "Amjad Khan",
+ "Baloch Khan"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "اړيکمن کاريالونه:",
+ "mwoauth-prefs-managegrantslink": "$1 {{PLURAL:$1|نښلېدلی کاريال|نښلېدلي کاريالونه}} مهارول",
+ "mwoauth-consumer-allwikis": "په دې ويبځي ټولې پروژې",
+ "mwoauth-consumer-name": "کاريال نوم:",
+ "mwoauth-consumer-user": "خپروونکی:",
+ "mwoauth-consumer-owner-only-label": "يوازې-خاوند:",
+ "mwoauthconsumerregistration-user": "خپروونکی",
+ "mwoauthconsumerregistration-description": "څرگندونه",
+ "mwoauthmanageconsumers-user": "خپروونکی",
+ "mwoauthmanageconsumers-description": "څرگندونه",
+ "oauthlistconsumers": "د OAuth کاريالونو لړليک",
+ "mwoauthlistconsumers-name": "کاريال نوم",
+ "mwoauthlistconsumers-user": "خپروونکی",
+ "mwoauthlistconsumers-description": "څرگندونه",
+ "mwoauthlistconsumers-status": "دريځ",
+ "oauthmanagemygrants": "نښلېدلي کاريالونه مهارول",
+ "mwoauthmanagemygrants-user": "خپروونکی:",
+ "mwoauthmanagemygrants-description": "څرگندونه",
+ "mwoauth-form-button-cancel": "ناگارل",
+ "grant-mwoauth-authonly": "يواځې د کارن د پېژندنې تصديق، د پاڼو د لوستلو توان نلري یا د کارن په استازیتوب عمل وکړي",
+ "grant-mwoauth-authonlyprivate": "د کارن د پېژندنې تصدیق یوازې د اصلي نوم او بریښناليک پواسطه، د پاڼو د لوستلو توان نلري یا د کارن په استازیتوب عمل وکړي.",
+ "notification-oauth-app-approve-primary-link": "کاريال کتل",
+ "notification-oauth-app-reject-primary-link": "کاريال کتل",
+ "notification-oauth-app-disable-primary-link": "کاريال کتل",
+ "notification-oauth-app-reenable-primary-link": "کاريال کتل",
+ "notification-oauth-app-body": "سبب: $1"
+}
diff --git a/OAuth/i18n/pt-br.json b/OAuth/i18n/pt-br.json
new file mode 100644
index 00000000..c41d6fb4
--- /dev/null
+++ b/OAuth/i18n/pt-br.json
@@ -0,0 +1,336 @@
+{
+ "@metadata": {
+ "authors": [
+ "Araceletorres",
+ "Cybermandrake",
+ "Eduardo Addad de Oliveira",
+ "Felipe L. Ewald",
+ "Hamilton Abreu",
+ "He7d3r",
+ "Helder.wiki",
+ "Leonardo9387",
+ "Luckas",
+ "Opraco",
+ "Rodrigo codignoli"
+ ]
+ },
+ "oauth": "OAuth",
+ "mwoauth-desc": "Permite o uso do OAuth 1.0a e OAuth 2.0 para autorização da API",
+ "mwoauth-nosubpage-explanation": "OAuth é um mecanismo que permite que aplicativos externos identifiquem um usuário do {{SITENAME}} ou atuem em seu nome, depois de receber permissão desse usuário.\n\nPara que esta página faça alguma coisa, são necessários mais parâmetros. Se você foi enviado aqui de um aplicativo externo, isso provavelmente ocorreu devido a um erro nesse aplicativo; você deve entrar em contato com o autor.",
+ "mwoauth-verified": "O aplicativo possui permissão para acessar ao MediaWiki através da sua conta.\n\nPara concluir o processo, proceda à verificação para o aplicativo através deste valor: '''$1'''",
+ "mwoauth-db-readonly": "O banco de dados OAuth está temporariamente bloqueado. Por favor, tente novamente em alguns minutos.",
+ "mwoauth-missing-field": "Falta o valor no campo \"$1\"",
+ "mwoauth-invalid-field": "Valor fornecido para o campo \"$1\" inválido",
+ "mwoauth-invalid-field-generic": "Valor fornecido inválido",
+ "mwoauth-field-hidden": "(esta informação está oculta)",
+ "mwoauth-field-private": "(esta informação é confidencial)",
+ "mwoauth-prefs-managegrants": "Aplicativos conectados:",
+ "mwoauth-prefs-managegrantslink": "Gerenciar {{PLURAL:$1|$1 aplicativo conectado|$1 aplicativos conectados|0=aplicativos conectados}}",
+ "mwoauth-consumer-allwikis": "Todos os projetos neste site",
+ "mwoauth-consumer-key": "Chave de consumidor:",
+ "mwoauth-consumer-name": "Nome do aplicativo:",
+ "mwoauth-consumer-version": "Versão do consumidor:",
+ "mwoauth-consumer-user": "Editor:",
+ "mwoauth-consumer-stage": "Status atual:",
+ "mwoauth-consumer-email": "Endereço de e-mail:",
+ "mwoauth-consumer-email-help": "Visível somente para aqueles que estão aprovando novos consumidores",
+ "mwoauth-consumer-owner-only-label": "Apenas o proprietário:",
+ "mwoauth-consumer-owner-only": "Este consumidor é para ser usado apenas por $1.",
+ "mwoauth-consumer-owner-only-help": "Selecionar esta opção fará com que o consumidor seja automaticamente aprovado e aceito para uso em $1. Não será utilizável por nenhum outro usuário e o fluxo de autorização usual não funcionará. As ações tomadas com esse consumidor não serão marcadas.",
+ "mwoauth-consumer-description": "Descrição do aplicativo:",
+ "mwoauth-consumer-callbackurl": "OAuth \"callback\" URL:",
+ "mwoauth-consumer-callbackisprefix": "Permitir que o consumidor especifique um retorno nos pedidos e use o URL \"callback\" acima como um prefixo necessário.",
+ "mwoauth-consumer-granttypes": "Tipos de permissões sendo solicitadas:",
+ "mwoauth-consumer-grantsneeded": "Permissões aplicáveis:",
+ "mwoauth-consumer-required-grant": "Aplicável ao consumidor",
+ "mwoauth-consumer-wiki": "Projeto aplicável:",
+ "mwoauth-consumer-wiki-thiswiki": "Projeto atual ($1)",
+ "mwoauth-consumer-restrictions": "Restrições de uso:",
+ "mwoauth-consumer-restrictions-json": "Restrições de uso (JSON):",
+ "mwoauth-consumer-rsakey": "Chave RSA pública (opcional):",
+ "mwoauth-consumer-rsakey-help": "Digite uma chave pública para usar o método de assinatura RSA-SHA1. Deixe vazio para usar HMAC-SHA1 com um segredo aleatório. Se você não tem certeza, deixe-o vazio.",
+ "mwoauth-consumer-secretkey": "Chave secreta do consumidor:",
+ "mwoauth-consumer-accesstoken": "Chave de acesso:",
+ "mwoauth-consumer-reason": "Motivo:",
+ "mwoauth-consumer-developer-agreement": "Ao enviar este aplicativo, você reconhece que nos reservamos o direito de desativar seu aplicativo, removê-lo ou restringir o acesso de você ou o seu aplicativo a este site e seguir qualquer outro curso de ação que consideremos apropriado se acreditarmos, a nosso juízo, que você ou sua aplicação está violando qualquer política, diretriz e princípio orientador deste site. Podemos alterar esta Política de Aplicação a qualquer momento sem aviso prévio, a nosso exclusivo critério e, como julgamos necessário. O seu uso contínuo de OAuth constitui aceitação dessas mudanças.",
+ "mwoauth-consumer-email-unconfirmed": "O seu endereço de e-mail ainda não foi confirmado.",
+ "mwoauth-consumer-email-mismatched": "O endereço de e-mail fornecido deve coincidir com o da sua conta.",
+ "mwoauth-consumer-alreadyexists": "Um consumidor com essa combinação de nome/versão/editor já existe",
+ "mwoauth-consumer-alreadyexistsversion": "Um consumidor com essa combinação de nome/editor já existe com uma versão igual ou superior (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Não é possível atualizar informações para um pedido de consumidor pendente",
+ "mwoauth-consumer-not-proposed": "O consumidor não está proposto atualmente",
+ "mwoauth-consumer-not-disabled": "O consumidor não está desativado",
+ "mwoauth-consumer-not-approved": "O consumidor não está aprovado (ele pode ter sido desativado)",
+ "mwoauth-missing-consumer-key": "Nenhuma chave de consumidor foi fornecida.",
+ "mwoauth-invalid-consumer-key": "Nenhum consumidor existe com essa chave dada.",
+ "mwoauth-invalid-access-token": "Nenhum token de acesso existe com a chave fornecida.",
+ "mwoauth-invalid-access-wrongwiki": "O consumidor só pode ser usado no projeto \"$1\".",
+ "mwoauth-consumer-conflict": "Alguém mudou os atributos deste consumidor como você o viu. Por favor, tente novamente. Você pode querer verificar o log de alterações.",
+ "mwoauth-consumer-grantshelp": "Cada concessão dá acesso aos direitos de usuário listados que uma conta de usuário já possui. Veja a [[Special:ListGrants|tabela de permissões]] para obter mais informações.",
+ "mwoauth-consumer-stage-proposed": "proposto",
+ "mwoauth-consumer-stage-rejected": "rejeitado",
+ "mwoauth-consumer-stage-expired": "expirado",
+ "mwoauth-consumer-stage-approved": "aprovado",
+ "mwoauth-consumer-stage-disabled": "desabilitado",
+ "mwoauth-consumer-stage-suppressed": "suprimido",
+ "oauthconsumerregistration": "Registro do consumidor OAuth",
+ "mwoauthconsumerregistration-navigation": "Navegação:",
+ "mwoauthconsumerregistration-propose": "Propor novo consumidor",
+ "mwoauthconsumerregistration-list": "Minha lista de consumidores",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-text": "Os desenvolvedores devem usar a forma abaixo para propôr um novo consumidor da OAuth (veja a [//www.mediawiki.org/wiki/Extension:OAuth documentação da extensão] para obter mais detalhes). Depois de enviar esta forma você receberá um token que seu aplicativo usará para se identificar com o MediaWiki. Um administrador do OAuth precisará aprovar seu aplicativo antes que ele possa ser autorizado por outros usuários.\n\nAlgumas recomendações e observações:\n* Tente usar o mínimo possível de concessões. Evite concessões que não são realmente necessárias agora.\n* As versões são da forma \"major.minor.release\" (os últimos dois sendo opcionais) e aumentam à medida que são necessárias alterações de concessão.\n* Forneça uma chave pública RSA (em Formato PEM), se possível; Caso contrário, será necessário usar um token secreto (menos seguro). \n* Você pode usar uma ID do projeto para restringir o consumidor a um único projeto neste site (use \"*\" para todos os projetos).",
+ "mwoauthconsumerregistration-update-text": "Use o formulário abaixo para atualizar aspectos de um consumidor OAuth que você controla. \n\nTodos os valores aqui substituirão os anteriores. Não deixe campos vazios, a menos que você pretenda limpar esses valores.",
+ "mwoauthconsumerregistration-maintext": "Esta página é para permitir aos desenvolvedores propor e atualizar os aplicativos de consumo da OAuth no registro deste site.\n\nDepois, você pode:\n* [[Special:OAuthConsumerRegistration/propose|Solicitar um token para um novo consumidor]].\n* [[Special:OAuthConsumerRegistration/list|Gerenciar seu consumidor existente]].\n\nPara obter mais informações sobre o Outh, consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth documentação da extensão].",
+ "mwoauthconsumerregistration-propose-legend": "Novo aplicativo do consumidor OAuth",
+ "mwoauthconsumerregistration-update-legend": "Atualizar aplicativo de consumidor OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Propor consumidor",
+ "mwoauthconsumerregistration-update-submit": "Atualizar consumidor",
+ "mwoauthconsumerregistration-none": "Você não controla nenhum consumidor OAuth.",
+ "mwoauthconsumerregistration-name": "Consumidor",
+ "mwoauthconsumerregistration-user": "Editor",
+ "mwoauthconsumerregistration-description": "Descrição",
+ "mwoauthconsumerregistration-email": "E-mail de contato",
+ "mwoauthconsumerregistration-consumerkey": "Chave de consumidor",
+ "mwoauthconsumerregistration-stage": "Estado",
+ "mwoauthconsumerregistration-lastchange": "Última alteração",
+ "mwoauthconsumerregistration-manage": "administrar",
+ "mwoauthconsumerregistration-resetsecretkey": "Redefinir chave secreta para um novo valor",
+ "mwoauthconsumerregistration-proposed": "Sua solicitação de consumidor da OAuth foi recebida.\n\nVocê recebeu um token do consumidor de '''$1''' e um token secreto de '''$2'''. ''Grave-os para referência futura''.",
+ "mwoauthconsumerregistration-created-owner-only": "O seu consumidor OAuth foi criado.\n\nSeus tokens são:\n; Token do consumidor: $1\n; Segredo do consumidor: $2\n; Token de acesso: $3\n; Acesso ao segredo: $4\n<em>Por favor, grave estes para futuras referências.</em>",
+ "mwoauthconsumerregistration-updated": "Seu registro de consumidor OAuth foi atualizado.",
+ "mwoauthconsumerregistration-secretreset": "Você recebeu um token secreto do consumidor de '''$1'''.''Por favor, grave isso para referências futuras''.",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Seus tokens de consumidor OAuth foram redefinidos. Os novos tokens são:\n; $1\n; Segredo do consumidor: $2\n;Token de acesso: $3\n; Acesso ao segredo: $4\n<em>Por favor, grave estes para futuras referências.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Você deve confirmar seu endereço de e-mail antes de criar aplicativos OAuth.\nPor favor, configure e valide seu endereço de e-mail através de suas [[Special:Preferences|preferências de usuário]].",
+ "oauthmanageconsumers": "Gerenciar os consumidores da OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Você precisa estar logado para acessar esta página.",
+ "mwoauthmanageconsumers-type": "Filas:",
+ "mwoauthmanageconsumers-showproposed": "Pedidos propostos",
+ "mwoauthmanageconsumers-showrejected": "Pedidos rejeitados",
+ "mwoauthmanageconsumers-showexpired": "Pedidos expirados",
+ "mwoauthmanageconsumers-linkproposed": "pedidos propostos",
+ "mwoauthmanageconsumers-linkrejected": "pedidos rejeitados",
+ "mwoauthmanageconsumers-linkexpired": "pedidos expirados",
+ "mwoauthmanageconsumers-linkapproved": "pedidos aprovados",
+ "mwoauthmanageconsumers-linkdisabled": "pedidos desativados",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-maintext": "Esta página destina-se a lidar com solicitações de aplicativos de consumidor OAuth (ver http://oauth.net) e gerenciar consumidores OAuth estabelecidos.",
+ "mwoauthmanageconsumers-queues": "Selecione uma fila de confirmação de consumidor abaixo:",
+ "mwoauthmanageconsumers-q-proposed": "Fila dos pedidos de consumidores propostos",
+ "mwoauthmanageconsumers-q-rejected": "Fila de pedidos de consumidores rejeitados",
+ "mwoauthmanageconsumers-q-expired": "Fila de pedidos de consumidor expirados",
+ "mwoauthmanageconsumers-lists": "Selecione uma lista de status do consumidor abaixo:",
+ "mwoauthmanageconsumers-l-approved": "Lista de consumidores aprovados atualmente",
+ "mwoauthmanageconsumers-l-disabled": "Lista de consumidores desativados atualmente",
+ "mwoauthmanageconsumers-none-proposed": "Não há consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-rejected": "Não há consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-expired": "Não há consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-approved": "Nenhum consumidor atende a esse critério.",
+ "mwoauthmanageconsumers-none-disabled": "Nenhum consumidor atende a esse critério.",
+ "mwoauthmanageconsumers-name": "Consumidor",
+ "mwoauthmanageconsumers-user": "Editor",
+ "mwoauthmanageconsumers-description": "Descrição",
+ "mwoauthmanageconsumers-email": "E-mail de contato",
+ "mwoauthmanageconsumers-consumerkey": "Chave de consumidor",
+ "mwoauthmanageconsumers-lastchange": "Última alteração",
+ "mwoauthmanageconsumers-review": "revisar/administrar",
+ "mwoauthmanageconsumers-confirm-text": "Use este formulário para aprovar, rejeitar, desativar ou reativar este consumidor.",
+ "mwoauthmanageconsumers-confirm-legend": "Gerenciar o consumidor do OAuth",
+ "mwoauthmanageconsumers-action": "Alterar status:",
+ "mwoauthmanageconsumers-approve": "Aprovado",
+ "mwoauthmanageconsumers-reject": "Rejeitado",
+ "mwoauthmanageconsumers-rsuppress": "Rejeitado e suprimido",
+ "mwoauthmanageconsumers-disable": "Desativado",
+ "mwoauthmanageconsumers-dsuppress": "Desabilitado e suprimido",
+ "mwoauthmanageconsumers-reenable": "Aprovado",
+ "mwoauthmanageconsumers-reason": "Motivo:",
+ "mwoauthmanageconsumers-confirm-submit": "Atualizar status do consumidor",
+ "mwoauthmanageconsumers-success-approved": "O pedido foi aprovado.",
+ "mwoauthmanageconsumers-success-rejected": "O pedido foi rejeitado.",
+ "mwoauthmanageconsumers-success-disabled": "O consumidor foi desabilitado.",
+ "mwoauthmanageconsumers-success-reanable": "O consumidor foi reativado.",
+ "mwoauthmanageconsumers-search-name": "consumidores com este nome",
+ "mwoauthmanageconsumers-search-publisher": "consumidores para este usuário",
+ "oauthlistconsumers": "Listar aplicativos OAuth",
+ "mwoauthlistconsumers-legend": "Procurar aplicativos OAuth",
+ "mwoauthlistconsumers-view": "detalhes",
+ "mwoauthlistconsumers-none": "Não foram encontrados aplicativos com estes critérios.",
+ "mwoauthlistconsumers-name": "Nome do aplicativo",
+ "mwoauthlistconsumers-version": "Versão do consumidor",
+ "mwoauthlistconsumers-user": "Editor",
+ "mwoauthlistconsumers-description": "Descrição",
+ "mwoauthlistconsumers-wiki": "Projeto aplicável",
+ "mwoauthlistconsumers-callbackurl": "OAuth \"callback URL\"",
+ "mwoauthlistconsumers-callbackisprefix": "Permitir que o consumidor especifique um retorno nos pedidos e use o URL \"callback\" acima como um prefixo necessário.",
+ "mwoauthlistconsumers-grants": "Permissões aplicáveis",
+ "mwoauthlistconsumers-basicgrantsonly": "(apenas acesso básico)",
+ "mwoauthlistconsumers-status": "Estado",
+ "mwoauth-consumer-stage-any": "qualquer",
+ "mwoauthlistconsumers-status-proposed": "proposto",
+ "mwoauthlistconsumers-status-approved": "aprovado",
+ "mwoauthlistconsumers-status-disabled": "desabilitado",
+ "mwoauthlistconsumers-status-rejected": "rejeitado",
+ "mwoauthlistconsumers-status-expired": "expirado",
+ "mwoauthlistconsumers-navigation": "Navegação:",
+ "mwoauthlistconsumers-update-link": "Atualizar consumidor",
+ "mwoauthlistconsumers-manage-link": "Gerenciar consumidor",
+ "mwoauthlistconsumers-grants-link": "Gerenciar doações",
+ "mwoauthlistconsumers-rclink": "Alterações recentes por este aplicativo",
+ "oauthmanagemygrants": "Administrar aplicativos conectados",
+ "mwoauthmanagemygrants-text": "Esta página lista todos os aplicativos que podem usar sua conta. Para qualquer um desses aplicativos, o alcance do seu acesso é limitado pelas permissões que você concedeu ao aplicativo quando você o autorizou a agir em seu nome. Se você autorizou separadamente um aplicativo para acessar diferentes projetos irmãos em seu nome, você verá uma configuração separada para cada um desses projetos abaixo.\n\nAs aplicações associadas acessam sua conta usando o protocolo OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Saiba mais sobre aplicações conectadas])</span>",
+ "mwoauthmanagemygrants-navigation": "Navegação:",
+ "mwoauthmanagemygrants-showlist": "Lista de aplicativos conectados",
+ "mwoauthmanagemygrants-none": "Não existem aplicativos ligados à sua conta.",
+ "mwoauthmanagemygrants-user": "Editor:",
+ "mwoauthmanagemygrants-description": "Descrição",
+ "mwoauthmanagemygrants-wikiallowed": "Permitido no projeto:",
+ "mwoauthmanagemygrants-grants": "Permissões aplicáveis",
+ "mwoauthmanagemygrants-grantsallowed": "Concessões permitidas",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Permissões aplicáveis permitidas:",
+ "mwoauthmanagemygrants-review": "administrar acesso",
+ "mwoauthmanagemygrants-revoke": "revogar acesso",
+ "mwoauthmanagemygrants-grantaccept": "Concedido",
+ "mwoauthmanagemygrants-update-text": "Use o formulário abaixo para modificar as permissões concedidas a um aplicativo para atuar em seu nome.",
+ "mwoauthmanagemygrants-revoke-text": "Utilize o formulário abaixo para revogar o acesso a um aplicativo que esteja a aceder à sua conta.",
+ "mwoauthmanagemygrants-confirm-legend": "Administrar aplicativos conectados",
+ "mwoauthmanagemygrants-update": "Atualizar permissões",
+ "mwoauthmanagemygrants-renounce": "Desautorizar",
+ "mwoauthmanagemygrants-action": "Alterar status:",
+ "mwoauthmanagemygrants-confirm-submit": "Atualize o status do token de acesso",
+ "mwoauthmanagemygrants-success-update": "Suas preferências para este aplicativo foram atualizadas.",
+ "mwoauthmanagemygrants-success-renounce": "O acesso do aplicativo à sua conta foi revogado.",
+ "mwoauthmanagemygrants-basic-tooltip": "Por que não posso atualizar essa concessão? Esta concessão dá as permissões básicas do aplicativo conectado que ele precisa para funcionar corretamente. Se você não quiser que este aplicativo conectado tenha esses direitos, você deve revogar o acesso do aplicativo.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Por que não posso atualizar essa concessão? Se você não quiser que este aplicativo conectado tenha esse direito, você deve revogar o acesso do aplicativo.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Suas}} edições por este aplicativo",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Suas}} ações por este aplicativo",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|propos}} um consumidor da OAuth (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|atualizou}} um consumidor OAuth (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|aprovOU}} uma chave de consumidor OAuth por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|rejeitou}} um consumidor da OAuth por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|desativou}} uma chave de consumidor OAuth por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|reativou}} um consumidor OAuth por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|criou}} um consumidor da OAuth apenas para o proprietário (chave do consumidor $4)",
+ "log-action-filter-mwoauthconsumer": "Tipo de ação do consumidor de OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Aprovação do consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Criação do consumidor OAuth apenas para proprietários",
+ "log-action-filter-mwoauthconsumer-disable": "Incapacidade do consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Proposta do consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "reajuste do consumidor de OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Rejeição do consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Atualização do consumidor OAuth",
+ "mwoauthconsumer-consumer-logpage": "Registro de consumidor OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Registro de aprovações, rejeições e desativação de consumidores registrados da OAuth.",
+ "mwoauth-bad-request-missing-params": "Desculpe, mas algo correu mal ao configurar esta aplicação ligada. Contacte o programador da aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Faltam parâmetros do OAuth, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Desculpe, mas algo correu mal e precisa de contactar o autor da aplicação para obter ajuda com o problema.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconhecido, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Desculpe, mas algo correu mal. Precisa de [$1 contactar] o autor da aplicação para obter ajuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconhecido, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Nenhuma concessão aprovada foi encontrada para esse token de autorização.",
+ "mwoauthdatastore-request-token-not-found": "Desculpe, algo correu mal ao ligar esta aplicação.\nVolte atrás e tente ligar a sua conta novamente, ou contacte o autor da aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">A chave OAuth não foi encontrada, $1</span>",
+ "mwoauthdatastore-callback-not-found": "URL de retorno de chamada OAuth não encontrado no cache. Este é provavelmente um erro na forma como o aplicativo faz solicitações para o servidor.",
+ "mwoauthdatastore-request-token-already-used": "Esta solicitação já foi concluída e não pode ser reenviada.\nVolte para o aplicativo e tente conectar sua conta novamente ou entre em contato com o autor da aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Token OAuth Já usado, $1</span>",
+ "mwoauthdatastore-bad-token": "Nenhum token foi encontrado correspondendo ao seu pedido.",
+ "mwoauthdatastore-bad-source-ip": "O pedido veio de um endereço de IP invalido.",
+ "mwoauthdatastore-bad-verifier": "O código de verificação fornecido não era válido.",
+ "mwoauthdatastore-invalid-token-type": "O tipo de token solicitado é inválido.",
+ "mwoauthgrants-general-error": "Ocorreu um erro no seu pedido OAuth.",
+ "mwoauthserver-bad-consumer": "\"$1\" não está aprovada como aplicação ligada. [$2 Contacte] o autor da aplicação para obter ajuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">A aplicação OAuth ligada não está aprovada, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Desculpe, algo correu mal ao ligar esta aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">A chave OAuth é desconhecida, $1</span>",
+ "mwoauthserver-insufficient-rights": "A sua conta não está autorizada a usar aplicações ligadas. Contacte o administrador do seu sítio para saber porquê.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Privilégios insuficientes de utilizador OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Token inválido no seu pedido.",
+ "mwoauthserver-invalid-user": "Para utilizar as aplicações ligadas nesta wiki, deve ter uma conta em todos os projetos. Quando tiver uma conta em todos os projetos, poderá tentar ligar \"$1\" novamente.\n\n<span class=\"plainlinks mw-mwoautherror-details\">É necessária uma autenticação unificada, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Desculpe, algo deu errado ao conectar esta aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">O consumidor não tem nenhuma chave secreta, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" é um aplicativo conectado apenas proprietário. Para buscar o token de acesso, consulte [[$2]].\n\n\n\n<span class=\"plainlinks mw-mwoautherror-details\">Consumer is owner-only, $3</span>",
+ "mwoauth-invalid-authorization-title": "Erro de autorização OAuth",
+ "mwoauth-invalid-authorization": "Os cabeçalhos de autorização em seu pedido não são válidos: $1 \\",
+ "mwoauth-invalid-authorization-wrong-wiki": "Os cabeçalhos de autorização em seu pedido não são válidos por $1 \\",
+ "mwoauth-invalid-authorization-invalid-user": "Os cabeçalhos de autorização em seu pedido são para um usuário que não existe aqui",
+ "mwoauth-invalid-authorization-wrong-user": "Os cabeçalhos de autorização em seu pedido são para um usuário diferente",
+ "mwoauth-invalid-authorization-not-approved": "O aplicativo que você está tentando conectar parece estar configurado incorretamente. Entre em contato com o autor de \"$1\" para obter ajuda.",
+ "mwoauth-invalid-authorization-blocked-user": "Os cabeçalhos de autorização em seu pedido são para um usuário bloqueado",
+ "mwoauth-form-description-allwikis": "Olá $1,\n\nPara concluir a sua solicitação, '''$2''' necessita de permissão para executar as seguintes ações em seu nome em todos os projetos deste site:\n\n$4",
+ "mwoauth-form-description-onewiki": "Olá $1,\n\nPara concluir a sua solicitação, '''$2''' necessita de permissão para executar as seguintes ações em seu nome em ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Olá $1,\n\nPara concluir a sua solicitação, '''$2''' necessita de permissão para acessar informações em todos os projetos deste site em seu nome. Nenhuma alteração será feita com a sua conta.",
+ "mwoauth-form-description-onewiki-nogrants": "Olá $1,\n\nPara concluir a sua solicitação, '''$2''' necessita de permissão para acessar informações em ''$4'' em seu nome. Nenhuma alteração será feita com a sua conta.",
+ "mwoauth-form-description-allwikis-privateinfo": "Olá $1,\n\nPara concluir a sua solicitação, '''$2''' necessita de permissão para acessar informações sobre você, incluindo, seu nome real e endereço de e-mail. Nenhuma alteração será feita com a sua conta.",
+ "mwoauth-form-description-onewiki-privateinfo": "Olá $1,\n\nPara concluir a sua solicitação, '''$2''' necessita de permissão para acessar informações, incluindo, seu nome real e endereço de e-mail, em ''$4''. Nenhuma alteração será feita com a sua conta.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' precisa de permissão para aceder à informação sobre si, incluindo o seu nome real e o endereço de e-mail, em todos os projetos deste site. Não serão efetuadas alterações na sua conta.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Olá, $1,\n\nPara concluir sua solicitação, '''$2''' precisa de permissão para acessar informações, incluindo seu endereço de e-mail, em ''$4''. Nenhuma alteração será feita com sua conta.",
+ "mwoauth-form-button-approve": "Permitir",
+ "mwoauth-form-button-cancel": "Cancelar",
+ "mwoauth-error": "Erro de conexão do aplicativo",
+ "mwoauth-grants-heading": "Permissões solicitadas:",
+ "mwoauth-grants-nogrants": "O aplicativo não solicitou nenhuma permissão.",
+ "mwoauth-acceptance-cancelled": "Escolheu que o aplicativo \"$1\" não pode aceder à sua conta. \"$1\" não irá funcionar a menos que permita o seu acesso. Pode regressar a \"$1\" ou [[Special:OAuthManageMyGrants|gerir]] os seus aplicativos conectados.",
+ "mwoauth-granttype-normal": "Solicite autorização para permissões específicas.",
+ "grant-mwoauth-authonly": "Somente verificação da identidade do usuário, sem capacidade de ler páginas ou agir em nome de um usuário.",
+ "grant-mwoauth-authonlyprivate": "Verificação de identidade do usuário apenas com acesso ao nome real e endereço de e-mail, sem capacidade de ler páginas ou atuar em nome do usuário.",
+ "mwoauth-listgrants-extra-summary": "== OAuth-specific grants ==\n\nEstas subvenções adicionais são aplicáveis ​​aos consumidores da OAuth.",
+ "mwoauth-oauth-exception": "Ocorreu um erro no protocolo OAuth: $1 \\",
+ "mwoauth-callback-not-oob": "oauth_callback deve ser configurado e deve ser configurado para \"oob\" (case-sensitive)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback deve ser configurado e deve ser configurado para \"oob\" (sensível a maiúsculas e minúsculas) ou o retorno de chamada configurado deve ser um prefixo do retorno de chamada fornecido.",
+ "right-mwoauthproposeconsumer": "Propor novos consumidores da OAuth",
+ "right-mwoauthupdateownconsumer": "Atualize os consumidores OAuth que você controla",
+ "right-mwoauthmanageconsumer": "Gerenciar os consumidores da OAuth",
+ "right-mwoauthsuppress": "Suprimir os consumidores do OAuth",
+ "right-mwoauthviewsuppressed": "Ver consumidores OAuth suprimidos",
+ "right-mwoauthviewprivate": "Ver dados OAuth privados",
+ "right-mwoauthmanagemygrants": "Gerenciar concessões do OAuth",
+ "action-mwoauthmanageconsumer": "gerenciar os consumidores da OAuth",
+ "action-mwoauthsuppress": "suprimir os consumidores do OAuth",
+ "action-mwoauthmanagemygrants": "gerenciar suas concessões OAuth",
+ "action-mwoauthproposeconsumer": "propor novos consumidores da OAuth",
+ "action-mwoauthupdateownconsumer": "atualize os consumidores OAuth que você controla",
+ "action-mwoauthviewprivate": "ver dados OAuth privados",
+ "action-mwoauthviewsuppressed": "ver consumidores OAuth suprimidos",
+ "mwoauth-tag-reserved": "As tags começando com <code>OAuth CID:</code> são reservadas para uso pelo OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Nota:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> é mais seguro do que as senhas do bot e deve ser preferido sempre que o bot o suportar.",
+ "mwoauth-api-module-disabled": "O módulo \"$1\" não está disponível com o OAuth.",
+ "echo-category-title-oauth-owner": "Desenvolvimento do OAuth",
+ "echo-pref-tooltip-oauth-owner": "Notificar-me sobre os eventos relacionados com as aplicações OAuth que eu criei.",
+ "echo-category-title-oauth-admin": "Administrador de OAuth",
+ "echo-pref-tooltip-oauth-admin": "Notificar-me sobre eventos relacionados à revisão de aplicativos OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|propôs}} uma nova aplicação OAuth: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|atualizou}} a aplicação OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|aprovou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|rejeitou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|desativou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reativou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|propôs}} uma nova aplicação OAuth em {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|atualizou}} uma aplicação OAuth em {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|aprovou}} {{GENDER:$3|a sua}} aplicação OAuth em {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|rejeitou}} {{GENDER:$3|a sua}} aplicação OAuth em {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|desativou}} {{GENDER:$3|a sua}} aplicação OAuth em {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reativou}} {{GENDER:$3|a sua}} aplicação OAuth em {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Revisar aplicação",
+ "notification-oauth-app-update-primary-link": "Revisar aplicação",
+ "notification-oauth-app-approve-primary-link": "Ver aplicativo",
+ "notification-oauth-app-reject-primary-link": "Ver aplicativo",
+ "notification-oauth-app-disable-primary-link": "Ver aplicativo",
+ "notification-oauth-app-reenable-primary-link": "Ver aplicativo",
+ "notification-oauth-app-body": "Motivo: $1",
+ "mwoauth-oauth-version": "Versão do protocolo OAuth",
+ "mwoauth-oauth2-is-confidential": "O cliente é confidencial",
+ "mwoauth-oauth2-is-confidential-help": "Um cliente confidencial é um aplicativo capaz de manter uma senha de cliente confidencial para o mundo. Clientes não confidenciais são menos seguros",
+ "mwoauth-oauth2-granttypes": "Tipos de concessão permitidos do OAuth2",
+ "mwoauth-oauth2-granttype-auth-code": "Código de autorização",
+ "mwoauth-oauth2-granttype-refresh-token": "Atualizar token",
+ "mwoauth-oauth2-granttype-client-credentials": "Credenciais do cliente",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Não é possível criar o token de acesso, o usuário não aprovou a emissão desse token de acesso",
+ "mwoauth-oauth2-error-user-approval-deny": "O usuário rejeitou a solicitação do aplicativo cliente",
+ "mwoauth-oauth-unsupported-version": "Este ponto de extremidade não é permitido para a versão do OAuth $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "O escopo \"$1\" não é permitido para este aplicativo",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Clientes somente proprietários devem ter permissão para usar client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Não foi possível recuperar o token de acesso: $1",
+ "mwoauth-oauth2-error-server-error": "O servidor de autorização encontrou uma condição inesperada que o impediu de atender à solicitação.",
+ "mwoauth-oauth2-error-invalid-request": "Na solicitação está faltando um parâmetro obrigatório, inclui um valor de parâmetro inválido, inclui um parâmetro mais de uma vez ou está malformado.",
+ "mwoauth-oauth2-error-unauthorized-client": "O cliente não está autorizado a solicitar um código de autorização usando este método.",
+ "mwoauth-oauth2-error-access-denied": "O proprietário do recurso ou o servidor de autorização negou a solicitação.",
+ "mwoauth-oauth2-error-unsupported-response-type": "O servidor de autorização não suporta a obtenção de um código de autorização usando este método.",
+ "mwoauth-oauth2-error-invalid-scope": "O escopo solicitado é inválido, desconhecido ou está incorreto.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "No momento, o servidor de autorização não pode manipular a solicitação devido a uma sobrecarga ou manutenção temporária do servidor.",
+ "mwoauth-oauth2-error-invalid-client": "Falha na autenticação do cliente (por exemplo, cliente desconhecido, nenhuma autenticação de cliente incluída ou método de autenticação não suportado)",
+ "mwoauth-oauth2-error-request-not-verified": "Tentativa de recuperar a propriedade verificada antes de verificar a solicitação",
+ "mwoauth-oauth2-invalid-access-token": "Chave eletrônica de acesso inválida",
+ "mwoauth-consumer-access-no-user": "A aprovação do consumidor deve estar vinculada a um usuário válido, usuário com o ID 0 fornecido",
+ "mwoauth-login-required-reason": "Você precisa entrar na sua conta {{SITENAME}} para autorizar aplicativos a acessá-lo.",
+ "mwoauthconsumer-consumer-view": "Ver este consumidor",
+ "mwoauthconsumer-application-view": "Ver esta aplicação"
+}
diff --git a/OAuth/i18n/pt.json b/OAuth/i18n/pt.json
new file mode 100644
index 00000000..1ed33ffd
--- /dev/null
+++ b/OAuth/i18n/pt.json
@@ -0,0 +1,303 @@
+{
+ "@metadata": {
+ "authors": [
+ "Athena in Wonderland",
+ "Dannyps",
+ "Fúlvio",
+ "Hamilton Abreu",
+ "He7d3r",
+ "Helder.wiki",
+ "Lijealso",
+ "Macofe",
+ "Mansil",
+ "MokaAkashiyaPT",
+ "Vitorvicentevalente"
+ ]
+ },
+ "mwoauth-desc": "Permite o uso do OAuth 1.0a para autorização da API",
+ "mwoauth-verified": "A aplicação tem agora permissão para aceder ao MediaWiki em seu nome.\n\nPara concluir o processo, forneça à aplicação este valor de verificação: '''$1'''",
+ "mwoauth-db-readonly": "A base de dados do OAuth está temporariamente bloqueada. Por favor, tente novamente dentro de alguns minutos.",
+ "mwoauth-missing-field": "Valor em falta para o campo \"$1\"",
+ "mwoauth-invalid-field": "Valor inválido fornecido para o campo \"$1\"",
+ "mwoauth-invalid-field-generic": "Valor fornecido inválido",
+ "mwoauth-field-hidden": "(esta informação está oculta)",
+ "mwoauth-field-private": "(esta informação é confidencial)",
+ "mwoauth-prefs-managegrants": "Aplicações ligadas:",
+ "mwoauth-prefs-managegrantslink": "Gerir {{PLURAL:$1|$1 aplicação ligada|$1 aplicações ligadas|0=aplicações ligadas}}",
+ "mwoauth-consumer-allwikis": "Todos os projetos deste sítio",
+ "mwoauth-consumer-key": "Chave de utilizador:",
+ "mwoauth-consumer-name": "Nome da aplicação:",
+ "mwoauth-consumer-version": "Versão:",
+ "mwoauth-consumer-user": "Autor:",
+ "mwoauth-consumer-stage": "Estado atual:",
+ "mwoauth-consumer-email": "Correio eletrónico de contacto:",
+ "mwoauth-consumer-email-help": "Só visível por quem está a aprovar novos consumidores",
+ "mwoauth-consumer-owner-only-label": "Reservado ao proprietário:",
+ "mwoauth-consumer-owner-only": "Este consumidor só pode ser usado por $1.",
+ "mwoauth-consumer-owner-only-help": "Selecionar esta opção causa a aprovação automática do consumidor e o seu uso por $1. O consumidor não pode ser usado por nenhum outro utilizador, e o processo normal de autorização não funcionará. As operações executadas através deste consumidor não serão etiquetadas.",
+ "mwoauth-consumer-description": "Descrição da aplicação:",
+ "mwoauth-consumer-callbackurl": "URL de retorno do OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Permitir que o consumidor especifique um retorno nos pedidos e use o URL de \"retorno\" acima como prefixo obrigatório.",
+ "mwoauth-consumer-granttypes": "Tipos de concessões que são solicitadas:",
+ "mwoauth-consumer-grantsneeded": "Concessões de permissões aplicáveis:",
+ "mwoauth-consumer-required-grant": "Aplicável ao consumidor",
+ "mwoauth-consumer-wiki": "Projeto aplicável:",
+ "mwoauth-consumer-wiki-thiswiki": "Projeto atual ($1)",
+ "mwoauth-consumer-restrictions": "Restrições de uso:",
+ "mwoauth-consumer-restrictions-json": "Restrições de uso (JSON)",
+ "mwoauth-consumer-rsakey": "Chave pública RSA (opcional):",
+ "mwoauth-consumer-rsakey-help": "Introduzir uma chave pública para usar o método de assinatura RSA-SHA1. Deixar vazio para usar HMAC-SHA1 com um segredo aleatório. Se não tem a certeza de qual deve usar, deixar vazio.",
+ "mwoauth-consumer-secretkey": "Chave secreta do consumidor:",
+ "mwoauth-consumer-accesstoken": "Chave de acesso:",
+ "mwoauth-consumer-reason": "Motivo:",
+ "mwoauth-consumer-developer-agreement": "Ao enviar esta aplicação, compreende e aceita que reservamos o direito de desativar a sua aplicação, remover ou restringir o seu acesso ou o acesso da sua aplicação a este sítio, e empreender qualquer esforço adicional que consideremos necessário, se acreditarmos, por nossa exclusiva discrição, que está a incorrer ou a sua aplicação está a incorrer na violação de qualquer norma, orientação ou princípio deste sítio. Esta Norma Para Aplicações pode ser alterada em qualquer altura sem aviso prévio, por nossa exclusiva discrição, e conforme considerarmos necessário. A sua utilização continuada do OAuth constitui uma aceitação dessas alterações.",
+ "mwoauth-consumer-email-unconfirmed": "O seu endereço de correio eletrónico ainda não foi confirmado.",
+ "mwoauth-consumer-email-mismatched": "O endereço de correio eletrónico fornecido deve coincidir com o da sua conta.",
+ "mwoauth-consumer-alreadyexists": "Já existe um consumidor com esta combinação de nome, versão e autor",
+ "mwoauth-consumer-alreadyexistsversion": "Já existe um consumidor com esta combinação de nome e autor, com uma versão igual ou superior (\"$1)",
+ "mwoauth-consumer-not-accepted": "Não foi possível atualizar a informação de um pedido de consumidor pendente",
+ "mwoauth-consumer-not-proposed": "Neste momento o consumidor não está proposto",
+ "mwoauth-consumer-not-disabled": "Neste momento o consumidor não está desativado",
+ "mwoauth-consumer-not-approved": "O consumidor não está aprovado (pode ter sido desativado)",
+ "mwoauth-missing-consumer-key": "Não foi fornecida uma chave de consumidor.",
+ "mwoauth-invalid-consumer-key": "Não existe nenhum consumidor com a chave fornecida.",
+ "mwoauth-invalid-access-token": "Não existe nenhuma chave de acesso com a chave fornecida.",
+ "mwoauth-invalid-access-wrongwiki": "O consumidor só pode ser usado no projeto \"$1\".",
+ "mwoauth-consumer-conflict": "Alguém alterou os atributos deste consumidor enquanto os visionava. Tente novamente, por favor. Talvez queira verificar o registo de alterações.",
+ "mwoauth-consumer-grantshelp": "Cada concessão de permissões dá acesso às permissões listadas que uma conta de utilizador já possua. Consulte a [[Special:ListGrants|tabela de concessões]] para mais informação.",
+ "mwoauth-consumer-stage-proposed": "proposto",
+ "mwoauth-consumer-stage-rejected": "rejeitado",
+ "mwoauth-consumer-stage-expired": "expirado",
+ "mwoauth-consumer-stage-approved": "aprovado",
+ "mwoauth-consumer-stage-disabled": "desativado",
+ "mwoauth-consumer-stage-suppressed": "suprimido",
+ "oauthconsumerregistration": "Registo de consumidores OAuth",
+ "mwoauthconsumerregistration-navigation": "Navegação:",
+ "mwoauthconsumerregistration-propose": "Propor um consumidor novo",
+ "mwoauthconsumerregistration-list": "A minha lista de consumidores",
+ "mwoauthconsumerregistration-main": "Principal",
+ "mwoauthconsumerregistration-propose-text": "Os programadores devem usar o formulário abaixo para propor um novo consumidor OAuth (consulte a [//www.mediawiki.org/wiki/Extension:OAuth documentação da extensão] para mais detalhes). Depois de enviarem o formulário, receberão uma chave que a vossa aplicação deverá usar para se identificar ao MediaWiki. Um administrador OAuth terá de aprovar a aplicação antes que ela possa ser autorizada pelos utilizadores.\n\nAlgumas recomendações e notas:\n* A sua aplicação deve usar o mínimo de privilégios possível. Evite privilégios que não sejam necessários por enquanto.\n* As versões seguem o formato \"maior.menor.revisão\" (sendo os dois últimos opcionais) e terão de ser aumentadas se a aplicação necessitar de alterações de privilégios.\n* Forneça uma chave RSA pública (em formato PEM) se for possível; se não, terá de ser usada uma chave secreta (que é menos segura).\n* Pode usar um identificador de projeto para restringir o acesso do consumidor a um único projeto deste sítio (use \"*\" para todos os projetos).",
+ "mwoauthconsumerregistration-update-text": "Use o formulário abaixo para atualizar aspetos de um consumidor OAuth que controla.\n\nTodos os valores irão sobrepor-se a quaisquer valores anteriores. Não deixe campos em branco a menos que pretenda limpar esses valores.",
+ "mwoauthconsumerregistration-maintext": "Esta página permite que programadores proponham e atualizem aplicações consumidor OAuth no registo deste sítio.\n\nA partir daqui pode:\n* [[Special:OAuthConsumerRegistration/propose|Pedir uma chave para um consumidor novo]].\n* [[Special:OAuthConsumerRegistration/list|Gerir os seus consumidores existentes]].\n\nPara mais informações acerca do OAuth, consulte a [//www.mediawiki.org/wiki/Extension:documentação da extensão OAuth].",
+ "mwoauthconsumerregistration-propose-legend": "Nova aplicação consumidor OAuth",
+ "mwoauthconsumerregistration-update-legend": "Atualizar a aplicação consumidor OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Propor consumidor",
+ "mwoauthconsumerregistration-update-submit": "Atualizar consumidor",
+ "mwoauthconsumerregistration-none": "Não controla nenhum consumidor OAuth.",
+ "mwoauthconsumerregistration-name": "Consumidor",
+ "mwoauthconsumerregistration-user": "Autor",
+ "mwoauthconsumerregistration-description": "Descrição",
+ "mwoauthconsumerregistration-email": "Correio eletrónico de contacto",
+ "mwoauthconsumerregistration-consumerkey": "Chave do consumidor",
+ "mwoauthconsumerregistration-stage": "Estado",
+ "mwoauthconsumerregistration-lastchange": "Última alteração",
+ "mwoauthconsumerregistration-manage": "gerir",
+ "mwoauthconsumerregistration-resetsecretkey": "Reiniciar chave secreta para um novo valor",
+ "mwoauthconsumerregistration-proposed": "O seu pedido de consumidor OAuth foi recebido.\n\nFoi-lhe atribuída a chave de consumidor '''$1''' e a chave secreta '''$2'''. ''Anote estes valores para referência futura, por favor.''",
+ "mwoauthconsumerregistration-created-owner-only": "O seu consumidor OAuth foi criado.\n\nAs suas chaves são:\n; Chave de consumidor: $1\n; Segredo de consumidor: $2\n; Chave de acesso: $3\n; Segredo de acesso: $4\n<em>Anote estes valores para referência futura, por favor.</em>",
+ "mwoauthconsumerregistration-updated": "O registo do seu consumidor OAuth foi atualizado.",
+ "mwoauthconsumerregistration-secretreset": "Foi-lhe atribuído o segredo de consumidor '''$1'''. ''Anote estes valores para referência futura, por favor.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "As suas chaves de consumidor OAuth foram reiniciadas. As novas chaves são:\n; Chave de consumidor: $1\n; Segredo de consumidor: $2\n; Chave de acesso: $3\n; Segredo de acesso: $4\n<em>Anote estes valores para referência futura, por favor.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Tem de confirmar o seu endereço de correio eletrónico antes de criar aplicações OAuth.\nIntroduza e valide o endereço através das [[Special:Preferences|preferências do utilizador]], por favor.",
+ "oauthmanageconsumers": "Gerir consumidores OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Precisa de ter sessão iniciada para aceder a esta página.",
+ "mwoauthmanageconsumers-type": "Filas:",
+ "mwoauthmanageconsumers-showproposed": "Pedidos propostos",
+ "mwoauthmanageconsumers-showrejected": "Pedidos rejeitados",
+ "mwoauthmanageconsumers-showexpired": "Pedidos expirados",
+ "mwoauthmanageconsumers-linkproposed": "pedidos propostos",
+ "mwoauthmanageconsumers-linkrejected": "pedidos rejeitados",
+ "mwoauthmanageconsumers-linkexpired": "pedidos expirados",
+ "mwoauthmanageconsumers-linkapproved": "pedidos aprovados",
+ "mwoauthmanageconsumers-linkdisabled": "pedidos desativados",
+ "mwoauthmanageconsumers-main": "Principal",
+ "mwoauthmanageconsumers-maintext": "Esta página destina-se a lidar com pedidos para aplicações consumidor OAuth (ver http://oauth.net) e gerir consumidores OAuth estabelecidos.",
+ "mwoauthmanageconsumers-queues": "Selecione uma fila de confirmação de consumidores abaixo:",
+ "mwoauthmanageconsumers-q-proposed": "Fila de pedidos de consumidor propostos",
+ "mwoauthmanageconsumers-q-rejected": "Fila de pedidos de consumidor rejeitados",
+ "mwoauthmanageconsumers-q-expired": "Fila de pedidos de consumidor expirados",
+ "mwoauthmanageconsumers-lists": "Selecione uma lista de estado de consumidores abaixo:",
+ "mwoauthmanageconsumers-l-approved": "Lista de consumidores atualmente aprovados",
+ "mwoauthmanageconsumers-l-disabled": "Lista de consumidores atualmente desativados",
+ "mwoauthmanageconsumers-none-proposed": "Não há consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-rejected": "Não há consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-expired": "Não há consumidores propostos nesta lista.",
+ "mwoauthmanageconsumers-none-approved": "Nenhum consumidor satisfaz estes critérios.",
+ "mwoauthmanageconsumers-none-disabled": "Nenhum consumidor satisfaz estes critérios.",
+ "mwoauthmanageconsumers-name": "Consumidor",
+ "mwoauthmanageconsumers-user": "Autor",
+ "mwoauthmanageconsumers-description": "Descrição",
+ "mwoauthmanageconsumers-email": "Correio eletrónico de contacto",
+ "mwoauthmanageconsumers-consumerkey": "Chave do consumidor",
+ "mwoauthmanageconsumers-lastchange": "Última alteração",
+ "mwoauthmanageconsumers-review": "rever/gerir",
+ "mwoauthmanageconsumers-confirm-text": "Utilize este formulário para aprovar, rejeitar, desativar ou reativar este consumidor.",
+ "mwoauthmanageconsumers-confirm-legend": "Gerir consumidor OAuth",
+ "mwoauthmanageconsumers-action": "Alterar estado:",
+ "mwoauthmanageconsumers-approve": "Aprovado",
+ "mwoauthmanageconsumers-reject": "Rejeitado",
+ "mwoauthmanageconsumers-rsuppress": "Rejeitado e suprimido",
+ "mwoauthmanageconsumers-disable": "Desativado",
+ "mwoauthmanageconsumers-dsuppress": "Desativado e suprimido",
+ "mwoauthmanageconsumers-reenable": "Aprovado",
+ "mwoauthmanageconsumers-reason": "Motivo:",
+ "mwoauthmanageconsumers-confirm-submit": "Atualizar estado do consumidor",
+ "mwoauthmanageconsumers-success-approved": "O pedido foi aprovado.",
+ "mwoauthmanageconsumers-success-rejected": "O pedido foi rejeitado.",
+ "mwoauthmanageconsumers-success-disabled": "O consumidor foi desativado.",
+ "mwoauthmanageconsumers-success-reanable": "O consumidor foi reativado.",
+ "mwoauthmanageconsumers-search-name": "consumidores com este nome",
+ "mwoauthmanageconsumers-search-publisher": "consumidores deste utilizador",
+ "oauthlistconsumers": "Aplicações OAuth",
+ "mwoauthlistconsumers-legend": "Procurar aplicações OAuth",
+ "mwoauthlistconsumers-view": "detalhes",
+ "mwoauthlistconsumers-none": "Não foram encontradas aplicações que satisfaçam estes critérios.",
+ "mwoauthlistconsumers-name": "Nome da aplicação",
+ "mwoauthlistconsumers-version": "Versão do consumidor",
+ "mwoauthlistconsumers-user": "Autor",
+ "mwoauthlistconsumers-description": "Descrição",
+ "mwoauthlistconsumers-wiki": "Projeto aplicável",
+ "mwoauthlistconsumers-callbackurl": "\"URL de retorno\" do OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Permitir que o consumidor especifique um retorno nos pedidos e use o URL de \"retorno\" acima como prefixo obrigatório.",
+ "mwoauthlistconsumers-grants": "Concessões de permissões aplicáveis",
+ "mwoauthlistconsumers-basicgrantsonly": "(apenas acesso básico)",
+ "mwoauthlistconsumers-status": "Estado",
+ "mwoauth-consumer-stage-any": "qualquer",
+ "mwoauthlistconsumers-status-proposed": "proposto",
+ "mwoauthlistconsumers-status-approved": "aprovado",
+ "mwoauthlistconsumers-status-disabled": "desativado",
+ "mwoauthlistconsumers-status-rejected": "rejeitado",
+ "mwoauthlistconsumers-status-expired": "expirado",
+ "mwoauthlistconsumers-rclink": "Mudanças recentes por esta aplicação",
+ "oauthmanagemygrants": "Gerir aplicações ligadas",
+ "mwoauthmanagemygrants-text": "Esta página lista todas as aplicações que podem usar a sua conta. O âmbito de acesso de qualquer aplicação é limitado pelas permissões que concede à aplicação quando a autoriza a agir em seu nome. Se autorizou uma aplicação a aceder a vários projetos em separado, verá abaixo uma configuração separada para cada projeto.\n\nAs aplicações ligadas acedem à sua conta usando o protocolo OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Saiba mais])</span>",
+ "mwoauthmanagemygrants-navigation": "Navegação:",
+ "mwoauthmanagemygrants-showlist": "Lista de aplicações ligadas",
+ "mwoauthmanagemygrants-none": "Não existem aplicações ligadas à sua conta.",
+ "mwoauthmanagemygrants-user": "Autor:",
+ "mwoauthmanagemygrants-description": "Descrição",
+ "mwoauthmanagemygrants-wikiallowed": "Permitido no projeto:",
+ "mwoauthmanagemygrants-grants": "Concessões de permissões aplicáveis",
+ "mwoauthmanagemygrants-grantsallowed": "Concessões de permissões permitidas",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Concessões de permissões permitidas aplicáveis:",
+ "mwoauthmanagemygrants-review": "gerir acesso",
+ "mwoauthmanagemygrants-revoke": "revogar acesso",
+ "mwoauthmanagemygrants-grantaccept": "Concedido",
+ "mwoauthmanagemygrants-update-text": "Utilize o formulário abaixo para modificar as permissões concedidas a uma aplicação para agir em seu nome.",
+ "mwoauthmanagemygrants-revoke-text": "Utilize o formulário abaixo para revogar o acesso a uma aplicação que esteja a agir em seu nome.",
+ "mwoauthmanagemygrants-confirm-legend": "Gerir aplicação ligada",
+ "mwoauthmanagemygrants-update": "Atualizar concessões de permissões",
+ "mwoauthmanagemygrants-renounce": "Remover a autorização",
+ "mwoauthmanagemygrants-action": "Alterar estado:",
+ "mwoauthmanagemygrants-confirm-submit": "Atualizar estado da chave de acesso",
+ "mwoauthmanagemygrants-success-update": "As suas preferências para esta aplicação foram atualizadas.",
+ "mwoauthmanagemygrants-success-renounce": "O acesso da aplicação à sua conta foi revogado.",
+ "mwoauthmanagemygrants-basic-tooltip": "Porque é que não posso atualizar esta concessão de permissões? Esta concessão de permissões dá à sua aplicação ligada as permissões básicas que ela necessita para funcionar devidamente. Se não deseja que a aplicação tenha estas permissões, deve revogar o acesso da aplicação.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Porque é que não posso atualizar esta concessão de permissões? Se não deseja que a esta aplicação ligada tenha esta permissão, deve revogar o acesso da aplicação.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|As suas}} edições por esta aplicação",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|As suas}} operações por esta aplicação",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|propôs}} um consumidor OAuth (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|atualizou}} um consumidor OAuth (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|aprovou}} um consumidor OAuth proposto por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|rejeitou}} um consumidor OAuth proposto por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|disativou}} um consumidor OAuth proposto por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|reativou}} um consumidor OAuth proposto por $3 (chave do consumidor $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|criou}} um consumidor OAuth reservado ao proprietário (chave do consumidor $4)",
+ "log-action-filter-mwoauthconsumer": "Tipo de ação do consumidor OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Aprovação do consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Criação de consumidor OAuth reservada para proprietários",
+ "log-action-filter-mwoauthconsumer-disable": "Desativação de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Proposta de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Reativação de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Rejeição de consumidor OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Atualização de consumidor OAuth",
+ "mwoauthconsumer-consumer-logpage": "Registo de consumidores OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Registo de aprovações, rejeições e desativação de consumidores OAuth registados.",
+ "mwoauth-bad-request-missing-params": "Desculpe, mas algo correu mal ao configurar esta aplicação ligada. Contacte o programador da aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Faltam parâmetros do OAuth, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Desculpe, mas algo correu mal e precisa de contactar o autor da aplicação para obter ajuda com o problema.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconhecido, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Desculpe, mas algo correu mal. Precisa de [$1 contactar] o autor da aplicação para obter ajuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL desconhecido, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Não foi encontrada nenhuma concessão de permissões aprovada para essa chave de autorização.",
+ "mwoauthdatastore-request-token-not-found": "Desculpe, algo correu mal ao ligar esta aplicação.\nVolte atrás e tente ligar a sua conta novamente, ou contacte o autor da aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">A chave OAuth não foi encontrada, $1</span>",
+ "mwoauthdatastore-callback-not-found": "O URL de retorno OAuth não foi encontrado na ''cache''. Isto provavelmente resulta de um erro na forma como a aplicação faz pedidos ao servidor.",
+ "mwoauthdatastore-request-token-already-used": "Este pedido já foi concluído e não pode ser apresentado novamente.\nRetroceda para a aplicação e tente ligar a sua conta novamente, ou contacte o autor da aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">A chave OAuth já foi usada, $1</span>",
+ "mwoauthdatastore-bad-token": "Não foi encontrada nenhuma chave que corresponda ao seu pedido.",
+ "mwoauthdatastore-bad-source-ip": "O pedido partiu de um endereço IP inválido.",
+ "mwoauthdatastore-bad-verifier": "O código de verificação fornecido não era válido.",
+ "mwoauthdatastore-invalid-token-type": "O tipo de chave pedido não é válido.",
+ "mwoauthgrants-general-error": "Ocorreu um erro no seu pedido OAuth.",
+ "mwoauthserver-bad-consumer": "\"$1\" não está aprovada como aplicação ligada. [$2 Contacte] o autor da aplicação para obter ajuda.\n\n<span class=\"plainlinks mw-mwoautherror-details\">A aplicação OAuth ligada não está aprovada, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Desculpe, algo correu mal ao ligar esta aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">A chave OAuth é desconhecida, $1</span>",
+ "mwoauthserver-insufficient-rights": "A sua conta não está autorizada a usar aplicações ligadas. Contacte o administrador do seu sítio para saber porquê.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Privilégios insuficientes de utilizador OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Chave inválida no seu pedido.",
+ "mwoauthserver-invalid-user": "Para utilizar as aplicações ligadas nesta wiki, deve ter uma conta em todos os projetos. Quando tiver uma conta em todos os projetos, poderá tentar ligar \"$1\" novamente.\n\n<span class=\"plainlinks mw-mwoautherror-details\">É necessária uma autenticação unificada, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Desculpe, algo correu mal ao ligar esta aplicação.\n\n<span class=\"plainlinks mw-mwoautherror-details\">O consumidor não tem uma chave secreta, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" é uma aplicação ligada reservada ao proprietário. Para obter a chave de acesso, consulte [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">O consumidor está reservado ao proprietário, $3</span>",
+ "mwoauth-invalid-authorization-title": "Erro de autorização OAuth",
+ "mwoauth-invalid-authorization": "Os cabeçalhos de autorização no seu pedido não são válidos: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Os cabeçalhos de autorização no seu pedido não são válidos para $1",
+ "mwoauth-invalid-authorization-invalid-user": "Os cabeçalhos de autorização no seu pedido são para um utilizador que não existe aqui",
+ "mwoauth-invalid-authorization-wrong-user": "Os cabeçalhos de autorização no seu pedido são para um utilizador diferente",
+ "mwoauth-invalid-authorization-not-approved": "A aplicação que está atentar ligar parece ter sido configurada incorretamente. Contacte o autor de \"$1\" para obter ajuda.",
+ "mwoauth-invalid-authorization-blocked-user": "Os cabeçalhos de autorização no seu pedido são para um utilizador que foi bloqueado",
+ "mwoauth-form-description-allwikis": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' necessita de permissão para executar as seguintes ações em seu nome em todos os projetos deste sítio:\n\n$4",
+ "mwoauth-form-description-onewiki": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' necessita de permissão para executar as seguintes ações em seu nome em ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' necessita de permissão para aceder, no seu nome, a informações de todos os projetos deste sítio. Não será realizada nenhuma alteração com a sua conta.",
+ "mwoauth-form-description-onewiki-nogrants": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' necessita de permissão para aceder a informações em ''$4'' em seu nome. Não será realizada nenhuma alteração com a sua conta.",
+ "mwoauth-form-description-allwikis-privateinfo": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' necessita de permissão para aceder a informações sobre si, incluindo o seu nome real e o endereço de correio eletrónico, de todos os projetos deste sítio. Não será realizada nenhuma alteração com a sua conta.",
+ "mwoauth-form-description-onewiki-privateinfo": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' necessita de permissão para aceder a informações, incluindo o seu nome real e o endereço de correio eletrónico, em ''$4''. Não será realizada nenhuma alteração com a sua conta.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' precisa de permissão para aceder à informação sobre si, incluindo o seu endereço de correio eletrónico, em todos os projetos deste site. Não serão efetuadas alterações com a sua conta.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Olá $1,\n\nPara concluir o seu pedido, '''$2''' necessita de permissão para aceder a informações, incluindo o seu endereço de correio eletrónico, em ''$4''. Não será realizada nenhuma alteração com a sua conta.",
+ "mwoauth-form-button-approve": "Permitir",
+ "mwoauth-form-button-cancel": "Cancelar",
+ "mwoauth-error": "Erro de ligação da aplicação",
+ "mwoauth-grants-heading": "Permissões solicitadas:",
+ "mwoauth-grants-nogrants": "A aplicação não solicitou quaisquer permissões.",
+ "mwoauth-acceptance-cancelled": "Escolheu não permitir que a aplicação \"$1\" possa aceder à sua conta. \"$1\" não irá funcionar a menos que permita o acesso. Pode regressar a \"$1\" ou [[Special:OAuthManageMyGrants|gerir]] as suas aplicações ligadas.",
+ "mwoauth-granttype-normal": "Solicitar autorização para permissões específicas.",
+ "grant-mwoauth-authonly": "Apenas verificação da identidade do utilizador, sem capacidade de ler páginas ou agir em nome de um utilizador.",
+ "grant-mwoauth-authonlyprivate": "Apenas verificação da identidade do utilizador, com acesso ao nome real e ao endereço de correio eletrónico, sem capacidade de ler páginas ou agir em nome de um utilizador.",
+ "mwoauth-listgrants-extra-summary": "== Concessões de permissões específicas para o OAuth ==\n\nEstas concessões adicionais de permissões aplicam-se a aplicações (consumidores) ligadas à wiki pelo OAuth.",
+ "mwoauth-oauth-exception": "Ocorreu um erro no protocolo OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback tem de estar definido, e tem de ter o valor \"oob\" (em minúsculas)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback tem de estar definido, e tem de ter o valor \"oob\" (em minúsculas), ou o URL de retorno configurado tem de ser um prefixo do retorno fornecido",
+ "right-mwoauthproposeconsumer": "Propor novos consumidores OAuth",
+ "right-mwoauthupdateownconsumer": "Atualizar os consumidores OAuth que controla",
+ "right-mwoauthmanageconsumer": "Gerir consumidores OAuth",
+ "right-mwoauthsuppress": "Suprimir consumidores OAuth",
+ "right-mwoauthviewsuppressed": "Ver consumidores OAuth suprimidos",
+ "right-mwoauthviewprivate": "Ver dados OAuth privados",
+ "right-mwoauthmanagemygrants": "Gerir concessões de permissões OAuth",
+ "action-mwoauthmanageconsumer": "gerir consumidores OAuth",
+ "action-mwoauthsuppress": "suprimir consumidores OAuth",
+ "action-mwoauthmanagemygrants": "gerir as suas concessões de permissões OAuth",
+ "action-mwoauthproposeconsumer": "propor novos consumidores OAuth",
+ "action-mwoauthupdateownconsumer": "atualizar os consumidores OAuth que controla",
+ "action-mwoauthviewprivate": "ver dados OAuth privados",
+ "action-mwoauthviewsuppressed": "ver os consumidores OAuth suprimidos",
+ "mwoauth-tag-reserved": "O uso de etiquetas iniciadas por <code>OAuth CID:</code> está reservado para o OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Nota:</strong> O <span class=\"plainlinks\">[$1 OAuth]</span> é mais seguro do que as palavras-passe dos robôs e deve ser preferido sempre que o robô o permita.",
+ "mwoauth-api-module-disabled": "O módulo \"$1\" não está disponível com OAuth.",
+ "echo-category-title-oauth-owner": "Desenvolvimento do OAuth",
+ "echo-pref-tooltip-oauth-owner": "Notificar-me sobre os eventos relacionados com as aplicações OAuth que eu criei.",
+ "echo-category-title-oauth-admin": "Administrador de OAuth",
+ "echo-pref-tooltip-oauth-admin": "Notificar-me sobre eventos relacionados com a revisão de aplicações OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|propôs}} uma nova aplicação OAuth: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|atualizou}} a aplicação OAuth \"$2\"",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|aprovou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|rejeitou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|desativou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|reativou}} {{GENDER:$3|a sua}} aplicação OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|propôs}} uma nova aplicação OAuth na wiki {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|atualizou}} uma aplicação OAuth na wiki {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|aprovou}} {{GENDER:$3|a sua}} aplicação OAuth na wiki {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|rejeitou}} {{GENDER:$3|a sua}} aplicação OAuth na wiki {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|desativou}} {{GENDER:$3|a sua}} aplicação OAuth na wiki {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|reativou}} {{GENDER:$3|a sua}} aplicação OAuth na wiki {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Análise da aplicação",
+ "notification-oauth-app-update-primary-link": "Análise da aplicação",
+ "notification-oauth-app-approve-primary-link": "Ver aplicação",
+ "notification-oauth-app-reject-primary-link": "Ver aplicação",
+ "notification-oauth-app-disable-primary-link": "Ver aplicação",
+ "notification-oauth-app-reenable-primary-link": "Ver aplicação",
+ "notification-oauth-app-body": "Motivo: $1"
+}
diff --git a/OAuth/i18n/qqq.json b/OAuth/i18n/qqq.json
new file mode 100644
index 00000000..55183e4e
--- /dev/null
+++ b/OAuth/i18n/qqq.json
@@ -0,0 +1,343 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "Hamilton Abreu",
+ "Liuxinyu970226",
+ "Mar(c)",
+ "Mormegil",
+ "Pyscowicz",
+ "Raymond",
+ "Shirayuki",
+ "Siebrand",
+ "Sunny00217",
+ "Tgr",
+ "Umherirrender"
+ ]
+ },
+ "oauth": "{{Optional}}\nTitle of MWOAuth page.\n{{Identical|OAuth}}",
+ "mwoauth-desc": "{{desc|name=OAuth|url=https://www.mediawiki.org/wiki/Extension:OAuth}}",
+ "mwoauth-nosubpage-explanation": "Non-technical explanation shown to when a person accidentally lands on Special:OAuth",
+ "mwoauth-verified": "Displayed to the user when the consumer does not have a callback URL, to provide the verification token that the consumer needs to complete the app authorization process.\n\nParameters:\n* $1 - verification token\n* $2 - (Unused) request token (the app should already have this)",
+ "mwoauth-db-readonly": "Error displayed when an admin has temporarily make OAuth read-only",
+ "mwoauth-missing-field": "Parameters:\n* $1 - field name\nSee also:\n* {{msg-mw|Mwoauth-invalid-field}}",
+ "mwoauth-invalid-field": "Parameters:\n* $1 - field name\nSee also:\n* {{msg-mw|Mwoauth-missing-field}}",
+ "mwoauth-invalid-field-generic": "Used as generic error message for form field validation.",
+ "mwoauth-field-hidden": "Used if the information has been deleted and the user is not allowed to view suppressed information.\n\nSee also:\n* {{msg-mw|Mwoauth-field-private}}",
+ "mwoauth-field-private": "Used if the user is not allowed to view private information.\n\nSee also:\n* {{msg-mw|Mwoauth-field-hidden}}",
+ "mwoauth-prefs-managegrants": "Used as label in [[Special:Preferences]].\n\nSee also:\n* {{msg-mw|Mwoauth-prefs-managegrantslink}}.",
+ "mwoauth-prefs-managegrantslink": "Used in [[Special:Preferences]]. See example: [[mw:Special:Preferences]].\n\nUsed as text for the link which points to [[Special:OAuthManageMyGrants]].\n\nPreceded by the label {{msg-mw|Mwoauth-prefs-managegrants}}.\n\nParameters:\n* $1 - Number of connected applications",
+ "mwoauth-consumer-allwikis": "Description of scope of consumer access when the scope is all wiki projects on the site",
+ "mwoauth-consumer-key": "Used as label for the \"Consumer key\" input box.\n{{Identical|Consumer key}}",
+ "mwoauth-consumer-name": "Used as label for the \"Application name\" input box.\n{{Identical|Application name}}",
+ "mwoauth-consumer-version": "Used as label for the \"Version\" input box.\n{{Identical|Consumer version}}",
+ "mwoauth-consumer-user": "Used as label for the \"Central username\" box.\n{{Identical|Publisher}}",
+ "mwoauth-consumer-stage": "Used as label for the \"Stage\" value\n\nFollowed by any one of the following messages:\n* {{msg-mw|Mwoauth-consumer-stage-proposed}}\n* {{msg-mw|Mwoauth-consumer-stage-rejected}}\n* {{msg-mw|Mwoauth-consumer-stage-expired}}\n* {{msg-mw|Mwoauth-consumer-stage-approved}}\n* {{msg-mw|Mwoauth-consumer-stage-disabled}}\n* {{msg-mw|Mwoauth-consumer-stage-suppressed}}\n{{Identical|Current status}}",
+ "mwoauth-consumer-email": "Used as label for the \"Email address\" input box.",
+ "mwoauth-consumer-email-help": "Used as help message for the \"Email address\" input box, in the consumer registration form.",
+ "mwoauth-consumer-owner-only-label": "Used as label for the \"Owner only\" field.",
+ "mwoauth-consumer-owner-only": "Used as label for the \"Owner only\" checkbox.",
+ "mwoauth-consumer-owner-only-help": "Used as help text for the \"Owner only\" checkbox.",
+ "mwoauth-consumer-description": "Used as label for the \"description\" textarea.\n{{Identical|Application description}}",
+ "mwoauth-consumer-callbackurl": "Used as label for the \"Callback URL\" input box.\n\nSee [[w:Callback (computer programming)]].",
+ "mwoauth-consumer-callbackisprefix": "Used as a label for the check box where user can decide if their consumer should use \"Callback URL\" as a string prefix (checked), or if the consumer cannot customize the callback URL in its requests as is required to specify \"oob\" (unchecked, default).",
+ "mwoauth-consumer-granttypes": "Used as label to select between authorization-only (with or without private info) and normal API access",
+ "mwoauth-consumer-grantsneeded": "Used as label.\n\nFollowed by the list of grants.\n{{Identical|Applicable grant}}",
+ "mwoauth-consumer-required-grant": "Used as table column header.",
+ "mwoauth-consumer-wiki": "Used as label for the input box. The default value for the input box is \"*\".\n{{Identical|Applicable project}}",
+ "mwoauth-consumer-wiki-thiswiki": "Label for selection-list option, indicating the wiki this user is currently visiting.\n\nParameters:\n* $1 - wiki ID",
+ "mwoauth-consumer-restrictions": "Used as label for the textarea. (The value is written in JSON format.)\n\nFollowed by the textarea or the message {{msg-mw|Mwoauthmanageconsumers-field-hidden}}.\n{{Identical|Usage restriction}}",
+ "mwoauth-consumer-restrictions-json": "Used as label for the \"Restrictions\" textarea.\n{{Identical|Usage restriction}}",
+ "mwoauth-consumer-rsakey": "Used as label for the textarea.\n\nFollowed by the textarea or the message {{msg-mw|Mwoauthmanageconsumers-field-hidden}}.",
+ "mwoauth-consumer-rsakey-help": "Used as help message for the textarea, on the consumer registration form.",
+ "mwoauth-consumer-secretkey": "Used as label for the textarea.",
+ "mwoauth-consumer-accesstoken": "Unused at this time.",
+ "mwoauth-consumer-reason": "Used as label for the \"Reason\" value.\n{{Identical|Reason}}",
+ "mwoauth-consumer-developer-agreement": "Agreement shown on application form, indicating that the app author understands their responsibilities by submitting this form.\n\n\"Application\" means \"app, software application\".",
+ "mwoauth-consumer-email-unconfirmed": "Used as failure message when taking some action which requires email-confirmation.",
+ "mwoauth-consumer-email-mismatched": "Used as failure message when taking some action.",
+ "mwoauth-consumer-alreadyexists": "Used as failure message.",
+ "mwoauth-consumer-alreadyexistsversion": "Used as failure message. Parameters:\n* $1 - current consumer version number",
+ "mwoauth-consumer-not-accepted": "Unused at this time.",
+ "mwoauth-consumer-not-proposed": "Used as failure message when approving or rejecting the consumer.\n\nSee also:\n* {{msg-mw|Mwoauth-consumer-not-disabled}}",
+ "mwoauth-consumer-not-disabled": "Used as failure message when re-enabling the consumer.\n\nSee also:\n* {{msg-mw|Mwoauth-consumer-not-proposed}}",
+ "mwoauth-consumer-not-approved": "Used as failure message.",
+ "mwoauth-missing-consumer-key": "Used as error message when showing consumer information.",
+ "mwoauth-invalid-consumer-key": "Used as failure message.",
+ "mwoauth-invalid-access-token": "Used as failure message.",
+ "mwoauth-invalid-access-wrongwiki": "Used as error message. Parameters:\n* $1 - the wiki ID the consumer is applicable to",
+ "mwoauth-consumer-conflict": "Used as failure message.",
+ "mwoauth-consumer-grantshelp": "Help text shown on consumer proposal form.\n\nIdentical:\n* {{msg-mw|Botpasswords-help-grants}}",
+ "mwoauth-consumer-stage-proposed": "{{Related|Mwoauth-consumer-stage}}\n{{Identical|Proposed}}",
+ "mwoauth-consumer-stage-rejected": "{{Related|Mwoauth-consumer-stage}}\n{{Identical|Rejected}}",
+ "mwoauth-consumer-stage-expired": "{{Related|Mwoauth-consumer-stage}}\n{{Identical|Expired}}",
+ "mwoauth-consumer-stage-approved": "{{Related|Mwoauth-consumer-stage}}\n{{Identical|Approved}}",
+ "mwoauth-consumer-stage-disabled": "{{Related|Mwoauth-consumer-stage}}\n{{Identical|Disabled}}",
+ "mwoauth-consumer-stage-suppressed": "{{Related|Mwoauth-consumer-stage}}\n{{Identical|Suppressed}}",
+ "oauthconsumerregistration": "{{doc-special|MWOAuthConsumerRegistration}}",
+ "mwoauthconsumerregistration-navigation": "Used in page subtitle.\n{{Identical|Navigation}}",
+ "mwoauthconsumerregistration-propose": "Text for the link that developers follow to request that their application is accepted as an OAuth application on this site.",
+ "mwoauthconsumerregistration-list": "Used in page subtitle link text",
+ "mwoauthconsumerregistration-main": "Used as label for \"View all\" link.\n\nPreceded by list of the links (\"|\" separated) which have any one of the following link texts:\n* {{msg-mw|Mwoauthconsumerregistration-propose}}\n* {{msg-mw|Mwoauthconsumerregistration-list}}\n{{Identical|Main}}",
+ "mwoauthconsumerregistration-propose-text": "Used as introduction text for the form.\n\n\"Application\" means \"app, software application\".",
+ "mwoauthconsumerregistration-update-text": "Used as introduction text for the form.",
+ "mwoauthconsumerregistration-maintext": "Used as introduction text in [[Special:OAuthConsumerRegistration]].",
+ "mwoauthconsumerregistration-propose-legend": "Used as fieldset label.",
+ "mwoauthconsumerregistration-update-legend": "Used as fieldset label.",
+ "mwoauthconsumerregistration-propose-submit": "Used as label for the Submit button.",
+ "mwoauthconsumerregistration-update-submit": "Used as label for the Submit button.",
+ "mwoauthconsumerregistration-none": "Used if there are no OAuth consumers to list.",
+ "mwoauthconsumerregistration-name": "Used as table row header.\n{{Identical|Consumer}}",
+ "mwoauthconsumerregistration-user": "{{Identical|Publisher}}",
+ "mwoauthconsumerregistration-description": "{{Identical|Description}}",
+ "mwoauthconsumerregistration-email": "field on registration form for email",
+ "mwoauthconsumerregistration-consumerkey": "Used as table row header.\n{{Identical|Consumer key}}",
+ "mwoauthconsumerregistration-stage": "Used as table row header.\n\nFollowed by any one of the following messages:\n* {{msg-mw|Mwoauth-consumer-stage-proposed}}\n* {{msg-mw|Mwoauth-consumer-stage-rejected}}\n* {{msg-mw|Mwoauth-consumer-stage-expired}}\n* {{msg-mw|Mwoauth-consumer-stage-approved}}\n* {{msg-mw|Mwoauth-consumer-stage-disabled}}\n* {{msg-mw|Mwoauth-consumer-stage-suppressed}}\n{{Identical|Status}}",
+ "mwoauthconsumerregistration-lastchange": "Used as table row header.\n{{Identical|Last change}}",
+ "mwoauthconsumerregistration-manage": "Used as link text.\n{{Identical|Manage}}",
+ "mwoauthconsumerregistration-resetsecretkey": "Used a label for a checkbox",
+ "mwoauthconsumerregistration-proposed": "Used as success message.\n\nParameters:\n* $1 - consumer key\n* $2 - secret key",
+ "mwoauthconsumerregistration-created-owner-only": "Used as success message.\n\nParameters:\n* $1 - consumer key\n* $2 - consumer secret\n* $3 - access key\n* $4 - access secret",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "Used as success message.\n\nParameters:\n* $1 - consumer key\n* $2 - consumer secret\n* $3 - access token",
+ "mwoauthconsumerregistration-updated": "Shown as success message",
+ "mwoauthconsumerregistration-secretreset": "Shown on success message. Parameters:\n* $1 - new secret token",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Shown on success message.\n\nParameters:\n* $1 - consumer key\n* $2 - consumer secret\n* $3 - access key\n* $4 - access secret",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Shown on success message.\n\nParameters:\n* $1 - consumer key\n* $2 - consumer secret\n* $3 - access key",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Shown when the user tries to use the consumer management special page without a confirmed email address.",
+ "oauthmanageconsumers": "{{doc-special|MWOAuthManageConsumers}}\n{{Identical|Manage OAuth consumer}}",
+ "mwoauthmanageconsumers-notloggedin": "Used if the user is not logged in.",
+ "mwoauthmanageconsumers-type": "Used as subtitle.\n\nFollowed by any one (or zero) of the following messages:\n* {{msg-mw|Mwoauthmanageconsumers-showproposed}}\n* {{msg-mw|Mwoauthmanageconsumers-showrejected}}\n* {{msg-mw|Mwoauthmanageconsumers-showexpired}}\n{{Identical|Queue}}",
+ "mwoauthmanageconsumers-showproposed": "Used as link text or plain text in a navigation menu.\n\nSee also:\n* {{msg-mw|Mwoauthmanageconsumers-type}}",
+ "mwoauthmanageconsumers-showrejected": "Used as link text or plain text in a navigation menu.\n\nSee also:\n* {{msg-mw|Mwoauthmanageconsumers-type}}",
+ "mwoauthmanageconsumers-showexpired": "Used as link text or plain text in a navigation menu.\n\nSee also:\n* {{msg-mw|Mwoauthmanageconsumers-type}}",
+ "mwoauthmanageconsumers-linkproposed": "Used as link text inside a sentence.",
+ "mwoauthmanageconsumers-linkrejected": "Used as link text inside a sentence.",
+ "mwoauthmanageconsumers-linkexpired": "Used as link text inside a sentence.",
+ "mwoauthmanageconsumers-linkapproved": "Used as link text inside a sentence.",
+ "mwoauthmanageconsumers-linkdisabled": "Used as link text inside a sentence.",
+ "mwoauthmanageconsumers-main": "Used as link text.\n\nPreceded by a list of links which have any one of the following labels:\n* {{msg-mw|Mwoauthmanageconsumers-showproposed}}\n* {{msg-mw|Mwoauthmanageconsumers-showrejected}}\n* {{msg-mw|Mwoauthmanageconsumers-showexpired}}\n{{Identical|Main}}",
+ "mwoauthmanageconsumers-maintext": "Used in [[Special:OAuthManageConsumers]].\n\nFollowed by the message {{msg-mw|Mwoauthmanageconsumers-queues}}.",
+ "mwoauthmanageconsumers-queues": "Used as label.\n\nFollowed by a list of links which point to [[Special:OAuthManageConsumers]].\n\nText for the link is any one of the following messages:\n* {{msg-mw|Mwoauthmanageconsumers-q-proposed}}\n* {{msg-mw|Mwoauthmanageconsumers-q-rejected}}\n* {{msg-mw|Mwoauthmanageconsumers-q-expired}}",
+ "mwoauthmanageconsumers-q-proposed": "Used as text for the link which points to [[Special:OAuthManageConsumers]].\n\nThe list is preceded by the label {{msg-mw|Mwoauthmanageconsumers-queues}}.",
+ "mwoauthmanageconsumers-q-rejected": "Used as text for the link which points to [[Special:OAuthManageConsumers]].\n\nThe list is preceded by the label {{msg-mw|Mwoauthmanageconsumers-queues}}.",
+ "mwoauthmanageconsumers-q-expired": "Used as text for the link which points to [[Special:OAuthManageConsumers]].\n\nThe list is preceded by the label {{msg-mw|Mwoauthmanageconsumers-queues}}.",
+ "mwoauthmanageconsumers-lists": "Used as subtitle which is followed by a list of links.\n\nThe links are points to [[Special:OAuthManageConsumers]].\n\nThe text fo the link is any one of the following messages:\n* {{msg-mw|Mwoauthmanageconsumers-l-approved}}\n* {{msg-mw|Mwoauthmanageconsumers-l-disabled}}",
+ "mwoauthmanageconsumers-l-approved": "Used as text for the link which points to [[Special:OAuthManageConsumers]].\n\nThe list is preceded by the label {{msg-mw|Mwoauthmanageconsumers-lists}}.",
+ "mwoauthmanageconsumers-l-disabled": "Used as text for the link which points to [[Special:OAuthManageConsumers]].\n\nThe list is preceded by the label {{msg-mw|Mwoauthmanageconsumers-lists}}.",
+ "mwoauthmanageconsumers-none-proposed": "Used if there are not consumers to list.\n{{Related|Mwoauthmanageconsumers-none}}",
+ "mwoauthmanageconsumers-none-rejected": "Used if there are not consumers to list.\n{{Related|Mwoauthmanageconsumers-none}}",
+ "mwoauthmanageconsumers-none-expired": "Used if there are not consumers to list.\n{{Related|Mwoauthmanageconsumers-none}}",
+ "mwoauthmanageconsumers-none-approved": "Used if there are not consumers to list.\n{{Related|Mwoauthmanageconsumers-none}}",
+ "mwoauthmanageconsumers-none-disabled": "Used if there are not consumers to list.\n{{Related|Mwoauthmanageconsumers-none}}",
+ "mwoauthmanageconsumers-name": "Used as table row header.\n{{Identical|Consumer}}",
+ "mwoauthmanageconsumers-user": "Used as table row header for the \"Central username\".\n{{Identical|Publisher}}",
+ "mwoauthmanageconsumers-description": "Used as table row header.\n{{Identical|Description}}",
+ "mwoauthmanageconsumers-email": "Followed by an email address or the message {{msg-mw|Mwoauth-consumer-stage-suppressed}}.",
+ "mwoauthmanageconsumers-consumerkey": "Used as table row header.\n{{Identical|Consumer key}}",
+ "mwoauthmanageconsumers-lastchange": "Used as table row header.\n{{Identical|Last change}}",
+ "mwoauthmanageconsumers-review": "Used as label for the link which points to [[Special:OAuthManageConsumers]].",
+ "mwoauthmanageconsumers-confirm-text": "Used as introduction text for the form.",
+ "mwoauthmanageconsumers-confirm-legend": "Used as fieldset label.\n{{Identical|Manage OAuth consumer}}",
+ "mwoauthmanageconsumers-action": "Used as label for the radio box group.\n\nFollowed by the following radio boxes:\n* {{msg-mw|mwoauthmanageconsumers-approve}}\n* {{msg-mw|mwoauthmanageconsumers-reject}}\n* {{msg-mw|mwoauthmanageconsumers-rsuppress}}\n* {{msg-mw|mwoauthmanageconsumers-disable}}\n* {{msg-mw|mwoauthmanageconsumers-dsuppress}}\n* {{msg-mw|mwoauthmanageconsumers-reenable}}\n{{Identical|Change status}}",
+ "mwoauthmanageconsumers-approve": "Used as label for the radio box.\n{{Related|Mwoauthmanageconsumers}}\n{{Identical|Approved}}",
+ "mwoauthmanageconsumers-reject": "Used as label for the radio box.\n{{Related|Mwoauthmanageconsumers}}\n{{Identical|Rejected}}",
+ "mwoauthmanageconsumers-rsuppress": "Used as label for the radio box.\n{{Related|Mwoauthmanageconsumers}}",
+ "mwoauthmanageconsumers-disable": "Used as label for the radio box.\n{{Related|Mwoauthmanageconsumers}}\n{{Identical|Disabled}}",
+ "mwoauthmanageconsumers-dsuppress": "Used as label for the radio box. Describes a consumer.\n{{Related|Mwoauthmanageconsumers}}",
+ "mwoauthmanageconsumers-reenable": "Used as label for the radio box.\n{{Related|Mwoauthmanageconsumers}}\n{{Identical|Approved}}",
+ "mwoauthmanageconsumers-reason": "Used as label for the \"Reason\" input box.\n{{Identical|Reason}}",
+ "mwoauthmanageconsumers-confirm-submit": "Used as label for the Submit button.",
+ "mwoauthmanageconsumers-success-approved": "Used as success message.\n{{Related|Mwoauthmanageconsumers-success}}",
+ "mwoauthmanageconsumers-success-rejected": "Used as success message.\n{{Related|Mwoauthmanageconsumers-success}}",
+ "mwoauthmanageconsumers-success-disabled": "Used as success message.\n{{Related|Mwoauthmanageconsumers-success}}",
+ "mwoauthmanageconsumers-success-reanable": "Used as success message.\n{{Related|Mwoauthmanageconsumers-success}}",
+ "mwoauthmanageconsumers-search-name": "Link to search for consumers with the same name.\n\nSee also:\n* {{msg-mw|Mwoauthmanageconsumers-search-publisher}}",
+ "mwoauthmanageconsumers-search-publisher": "Link to search for consumers by the same user.\n\nSee also:\n* {{msg-mw|Mwoauthmanageconsumers-search-name}}",
+ "oauthlistconsumers": "{{doc-special|MWOAuthListConsumers}}",
+ "mwoauthlistconsumers-legend": "Legend used for filter form fieldset.\n\nFollowed by the following labels:\n* {{msg-mw|mwoauth-consumer-name}}\n* {{msg-mw|mwoauth-consumer-user}}\n* {{msg-mw|mwoauth-consumer-stage}}",
+ "mwoauthlistconsumers-view": "Link to view consumer details.\n{{Identical|Detail}}",
+ "mwoauthlistconsumers-none": "Shown when a list of consumers is empty",
+ "mwoauthlistconsumers-name": "Used as a field name in consumer lists.\n{{Identical|Application name}}",
+ "mwoauthlistconsumers-version": "Used as a field name in consumer lists.\n{{Identical|Consumer version}}",
+ "mwoauthlistconsumers-user": "Used as a field name in consumer lists.\n{{Identical|Publisher}}",
+ "mwoauthlistconsumers-description": "Used as label for the \"Description\" field in consumer lists.\n{{Identical|Description}}",
+ "mwoauthlistconsumers-wiki": "Used as a field name in consumer lists.\n{{Identical|Applicable project}}",
+ "mwoauthlistconsumers-callbackurl": "Used as a field name in consumer lists",
+ "mwoauthlistconsumers-callbackisprefix": "Used as a field name in consumer lists",
+ "mwoauthlistconsumers-grants": "Used as a field name in consumer lists.\n{{Identical|Applicable grant}}",
+ "mwoauthlistconsumers-basicgrantsonly": "Message used when only hidden grants are used by a consumer (or none at all)",
+ "mwoauthlistconsumers-status": "Used as a field name in consumer lists.\n{{Identical|Status}}",
+ "mwoauth-consumer-stage-any": "Used as special selector field for \"all consumer states\".\n{{Identical|Any}}",
+ "mwoauthlistconsumers-status-proposed": "{{Related|Mwoauthlistconsumers-status}}\n{{Identical|Proposed}}",
+ "mwoauthlistconsumers-status-approved": "{{Related|Mwoauthlistconsumers-status}}\n{{Identical|Approved}}",
+ "mwoauthlistconsumers-status-disabled": "{{Related|Mwoauthlistconsumers-status}}\n{{Identical|Disabled}}",
+ "mwoauthlistconsumers-status-rejected": "{{Related|Mwoauthlistconsumers-status}}\n{{Identical|Rejected}}",
+ "mwoauthlistconsumers-status-expired": "{{Related|Mwoauthlistconsumers-status}}\n{{Identical|Expired}}",
+ "mwoauthlistconsumers-navigation": "Used as subtitle.\n\nFollowed by a link with the link text {{msg-mw|mwoauthlistconsumers-update-link}}. It can be without link.\n{{Identical|Navigation}}",
+ "mwoauthlistconsumers-update-link": "Used as link text or plain text in a navgation menu.",
+ "mwoauthlistconsumers-manage-link": "Used as link text or plain text in a navgation menu.",
+ "mwoauthlistconsumers-grants-link": "Used as link text or plain text in a navgation menu.",
+ "mwoauthlistconsumers-rclink": "Text of the link shown on [[Special:OAuthListConsumers]] which shows the recent changes by this application.",
+ "oauthmanagemygrants": "{{doc-special|OAuthManageMyGrants}}",
+ "mwoauthmanagemygrants-text": "Explanatory text for Special:OAuthManageMyGrants page",
+ "mwoauthmanagemygrants-navigation": "Used as subtitle.\n\nFollowed by a link with the link text {{msg-mw|Mwoauthmanagemygrants-showlist}}. It can be without link.\n{{Identical|Navigation}}",
+ "mwoauthmanagemygrants-showlist": "Used as link text or as plain text",
+ "mwoauthmanagemygrants-none": "Message when a user has not authorized any OAuth consumers",
+ "mwoauthmanagemygrants-user": "Used as table row header for \"Central username\".\n{{Identical|Publisher}}",
+ "mwoauthmanagemygrants-description": "Used as table row header.\n{{Identical|Description}}",
+ "mwoauthmanagemygrants-wikiallowed": "Used as field label",
+ "mwoauthmanagemygrants-grants": "Used as field label.\n{{Identical|Applicable grant}}",
+ "mwoauthmanagemygrants-grantsallowed": "Used as field label",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Used as field label",
+ "mwoauthmanagemygrants-review": "Used as link text.",
+ "mwoauthmanagemygrants-revoke": "Used as link text.",
+ "mwoauthmanagemygrants-grantaccept": "Used as checkbox column label",
+ "mwoauthmanagemygrants-update-text": "Explanatory text for [[Special:OAuthManageMyGrants]] form",
+ "mwoauthmanagemygrants-revoke-text": "Explanatory text for Special:OAuthManageMyGrants form",
+ "mwoauthmanagemygrants-confirm-legend": "Used as fieldset label",
+ "mwoauthmanagemygrants-update": "Used as label for the radio box.\n\nSee also:\n* {{msg-mw|Mwoauthmanagemygrants-action}}",
+ "mwoauthmanagemygrants-renounce": "Used as label for the radio box.\n\nSee also:\n* {{msg-mw|Mwoauthmanagemygrants-action}}",
+ "mwoauthmanagemygrants-action": "Used as label for the radio box group.\n\nFollowed by the following radio boxes:\n* {{msg-mw|Mwoauthmanagemygrants-update}}\n* {{msg-mw|Mwoauthmanagemygrants-renounce}}\n{{Identical|Change status}}",
+ "mwoauthmanagemygrants-confirm-submit": "Used as label for the Submit button",
+ "mwoauthmanagemygrants-success-update": "Message shown when grants for an OAuth consumer are updated by a user",
+ "mwoauthmanagemygrants-success-renounce": "Message shown when grants for an OAuth consumer are totally revoked",
+ "mwoauthmanagemygrants-basic-tooltip": "Message for the tooltip shown next to the disabled checkbox for the \"basic\" grant on [[Special:OAuthManageMyGrants]], explaining why the checkbox cannot be modified.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Message for the tooltip shown next to the disabled checkboxes for the \"authonly\" and \"authonlyprivate\" grants on [[Special:OAuthManageMyGrants]], explaining why the checkbox cannot be modified.\n\nCf. {{msg-mw|Mwoauthmanagemygrants-basic-tooltip}}",
+ "mwoauthmanagemygrants-editslink": "Text of the link shown on [[Special:OAuthManageMyGrants]] which shows the edits this application made in the name of the user. $1 is the current user.",
+ "mwoauthmanagemygrants-actionslink": "Text of the link shown on [[Special:OAuthManageMyGrants]] which shows the logged actions this application made in the name of the user. $1 is the current user.",
+ "logentry-mwoauthconsumer-propose": "{{logentry}}",
+ "logentry-mwoauthconsumer-update": "{{logentry}}\n* $4 - consumer key",
+ "logentry-mwoauthconsumer-approve": "{{logentry}}\n* $4 - consumer key",
+ "logentry-mwoauthconsumer-reject": "{{logentry}}\n* $4 - consumer key",
+ "logentry-mwoauthconsumer-disable": "{{logentry}}\n* $4 - consumer key",
+ "logentry-mwoauthconsumer-reenable": "{{logentry}}\n* $4 - consumer key",
+ "logentry-mwoauthconsumer-create-owner-only": "{{logentry}}\n* $4 - consumer key",
+ "log-action-filter-mwoauthconsumer": "{{doc-log-action-filter-type|mwoauthconsumer}}\n{{Related|Log-action-filter}}",
+ "log-action-filter-mwoauthconsumer-approve": "{{doc-log-action-filter-action|mwoauthconsumer|approve}}",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "{{doc-log-action-filter-action|mwoauthconsumer|create-owner-only}}",
+ "log-action-filter-mwoauthconsumer-disable": "{{doc-log-action-filter-action|mwoauthconsumer|disable}}",
+ "log-action-filter-mwoauthconsumer-propose": "{{doc-log-action-filter-action|mwoauthconsumer|propose}}",
+ "log-action-filter-mwoauthconsumer-reenable": "{{doc-log-action-filter-action|mwoauthconsumer|reenable}}",
+ "log-action-filter-mwoauthconsumer-reject": "{{doc-log-action-filter-action|mwoauthconsumer|reject}}",
+ "log-action-filter-mwoauthconsumer-update": "{{doc-log-action-filter-action|mwoauthconsumer|update}}",
+ "mwoauthconsumer-consumer-logpage": "{{doc-logpage}}",
+ "mwoauthconsumer-consumer-logpagetext": "Description of the OAuth consumer log.",
+ "mwoauth-bad-request-missing-params": "Error message when MediaWiki makes an error during the authorization process, and fails to send all the required url parameters\n* $1 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauth-bad-request-invalid-action": "Error, when the 3rd-party OAuth developers sends users to a bad authorization url\n* $1 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauth-bad-request-invalid-action-contact": "Error, when the 3rd-party OAuth developers sends users to a bad authorization url, but we know which application made the request and we can link the user to a page to contact the developer.\n\nParameters:\n* $1 - an URL to contact with the author.\n* $2 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauthdatastore-access-token-not-found": "Error message when an invalid access token was submitted",
+ "mwoauthdatastore-request-token-not-found": "Error message when an invalid request token was submitted\n* $1 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauthdatastore-callback-not-found": "Error message when the callback URL is not found in cache.",
+ "mwoauthdatastore-request-token-already-used": "Error message when a request token that has already been used was resubmitted. Parameters:\n* $1 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauthdatastore-bad-token": "Error message when an invalid token was submitted",
+ "mwoauthdatastore-bad-source-ip": "Error message when a request comes from an IP address which is not among those whitelisted",
+ "mwoauthdatastore-bad-verifier": "Error message when an invalid verification code was submitted",
+ "mwoauthdatastore-invalid-token-type": "Error message when an invalid page was requested",
+ "mwoauthgrants-general-error": "Generic error, when something unexpected happened while processing the OAuth request",
+ "mwoauthserver-bad-consumer": "Error message when an invalid consumer identifier was submitted. Parameters:\n* $1 - application name\n* $2 - central wiki's user talk page\n* $3 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauthserver-bad-consumer-key": "Generic error for users when a 3rd-party OAuth developer sends users to an invalid url\n* $1 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauthserver-insufficient-rights": "Error message that the user does not have the required rights to perform this request",
+ "mwoauthserver-invalid-request-token": "Error message when an invalid request token was submitted",
+ "mwoauthserver-invalid-user": "Error when the user attempts to use OAuth, but they do not have a unified (SUL) account, which is required.\n\nParameters:\n* $1 - application name\n* $2 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauthserver-consumer-no-secret": "Error when a consumer with no secret or RSA was submitted. Parameters:\n* $1 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauthserver-consumer-owner-only": "Error when an owner-only consumer attempted to use the authorization process. Parameters:\n* $1 - consumer name\n* $2 - title of OAuth Consumer registration update\n* $3 - a link to the explanation of the error. The text link is the error code.",
+ "mwoauth-invalid-authorization-title": "Title of the error page when the Authorization header is invalid",
+ "mwoauth-invalid-authorization": "Text of the error page when the Authorization header is invalid. Parameters are:\n* $1 - Specific error message from the OAuth layer, probably not localized",
+ "mwoauth-invalid-authorization-wrong-wiki": "Text of the error page when the Authorization header is for the wrong wiki. Parameters are:\n* $1 - wiki id",
+ "mwoauth-invalid-authorization-invalid-user": "Text of the error page when the Authorization header is for a user that doesn't exist",
+ "mwoauth-invalid-authorization-wrong-user": "Text of the error page when the Authorization header is for the wrong user",
+ "mwoauth-invalid-authorization-not-approved": "Text of the error page when the Authorization header is for a consumer that isn't approved.\n\nParameters:\n* $1 - ...",
+ "mwoauth-invalid-authorization-blocked-user": "Text of the error page when Authorization header is for a user who is blocked",
+ "mwoauth-form-description-allwikis": "Description of a form requesting the user authorize an OAuth consumer to use MediaWiki on their behalf.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\n* $4 - formatted list of grants\nSee also:\n{{related|mwoauth-form-description}}",
+ "mwoauth-form-description-onewiki": "Description of a form requesting the user authorize an OAuth consumer to use MediaWiki on their behalf, without any non-hidden grants.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\n* $4 - wiki project name\nSee also:{{related|mwoauth-form-description}}",
+ "mwoauth-form-description-allwikis-nogrants": "Description of a form requesting the user authorize an OAuth consumer to use MediaWiki on their behalf, without any non-hidden grants.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\nSee also:\n{{related|mwoauth-form-description}}",
+ "mwoauth-form-description-onewiki-nogrants": "Description of a form requesting the user authorize an OAuth consumer to use MediaWiki on their behalf, without any non-hidden grants.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\n* $4 - wiki project name\nSee also:\n{{related|mwoauth-form-description}}",
+ "mwoauth-form-description-allwikis-privateinfo": "Description of a form requesting the user authorize an OAuth consumer to access private information about the user but with no grants. The language should parallel {{msg-mw|grant-mwoauth-authonlyprivate}}.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\nSee also:\n{{related|mwoauth-form-description}}",
+ "mwoauth-form-description-onewiki-privateinfo": "Description of a form requesting the user authorize an OAuth consumer to access private information about the user but with no grants. The language should parallel {{msg-mw|grant-mwoauth-authonlyprivate}}.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\n* $4 - wiki project name\nSee also:\n{{related|mwoauth-form-description}}",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Description of a form requesting the user authorize an OAuth consumer to access private information about the user but with no grants, on a wiki which does not use real names. The language should parallel {{msg-mw|grant-mwoauth-authonlyprivate}}.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\nSee also:\n{{related|mwoauth-form-description}}",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Description of a form requesting the user authorize an OAuth consumer to access private information about the user but with no grants, on a wiki which does not use real names. The language should parallel {{msg-mw|grant-mwoauth-authonlyprivate}}.\n\nParameters:\n* $1 - the username\n* $2 - application name\n* $3 - application publisher\n* $4 - wiki project name\nSee also:\n{{related|mwoauth-form-description}}",
+ "mwoauth-form-legal": "Message used for wiki-specific legal notes. Keep this blank.",
+ "mwoauth-form-button-approve": "Button label, indicating the user wants to allow access.\n\nSee also:\n* {{msg-mw|Mwoauth-form-button-cancel}}\n{{Identical|Allow}}",
+ "mwoauth-form-button-cancel": "Button label, indicating the user wants to cancel granting access.\n\nSee also:\n* {{msg-mw|Mwoauth-form-button-approve}}\n{{Identical|Cancel}}",
+ "mwoauth-error": "Heading on the page, whenever an OAuth error is presented to a user.",
+ "mwoauth-grants-heading": "Used as label for the grants list.\n\nSee also:\n* {{msg-mw|Grant-blockusers}}\n* {{msg-mw|Grant-createaccount}}\n* {{msg-mw|Grant-createeditmovepage}}\n* {{msg-mw|Grant-delete}}\n* {{msg-mw|Grant-editinterface}}\n* {{msg-mw|Grant-editmycssjs}}\n* {{msg-mw|Grant-editmywatchlist}}\n* {{msg-mw|Grant-editpage}}\n* {{msg-mw|Grant-editprotected}}\n* {{msg-mw|Grant-highvolume}}\n* {{msg-mw|Grant-oversight}}\n* {{msg-mw|Grant-patrol}}\n* {{msg-mw|Grant-protect}}\n* {{msg-mw|Grant-rollback}}\n* {{msg-mw|Grant-sendemail}}\n* {{msg-mw|Grant-uploadeditmovefile}}\n* {{msg-mw|Grant-uploadfile}}\n* {{msg-mw|Grant-basic}}\n* {{msg-mw|Grant-viewdeleted}}\n* {{msg-mw|Grant-viewmywatchlist}}",
+ "mwoauth-grants-nogrants": "Warning message that the OAuth consumer has not requested any permissions",
+ "mwoauth-acceptance-cancelled": "Message shown when an OAuth authorization request is declined. Parameters:\n* $1 - consumer name",
+ "mwoauth-granttype-normal": "{{Related|Mwoauth-consumer-granttypes}}",
+ "grant-mwoauth-authonly": "Name for OAuth grant \"mwoauth-authonly\".\n{{Related|Grant}}",
+ "grant-mwoauth-authonlyprivate": "Name for OAuth grant \"mwoauth-authonlyprivate\".\n{{Related|Grant}}",
+ "mwoauth-listgrants-extra-summary": "Header for the OAuth-specific grants section on [[Special:ListGrants]]",
+ "mwoauth-oauth-exception": "Used as failure message. Parameters:\n* $1 - Exception message text",
+ "mwoauth-callback-not-oob": "Warning that the OAuth developer failed to include the required \"oauth_callback\" parameter, which must be set to the case-sensitive string \"oob\"",
+ "mwoauth-callback-not-oob-or-prefix": "Warning that the OAuth developer failed to include the required \"oauth_callback\" parameter, which must be set to the case-sensitive string \"oob\", or the callback which was configured at a consumer registration time must be a strict (character by character) prefix of the supplied value.",
+ "right-mwoauthproposeconsumer": "{{doc-right|mwoauthproposeconsumer}}",
+ "right-mwoauthupdateownconsumer": "{{doc-right|mwoauthupdateownconsumer}}",
+ "right-mwoauthmanageconsumer": "{{doc-right|mwoauthmanageconsumer}}\n{{Identical|Manage OAuth consumer}}",
+ "right-mwoauthsuppress": "{{doc-right|mwoauthsuppress}}",
+ "right-mwoauthviewsuppressed": "{{doc-right|mwoauthviewsuppressed}}",
+ "right-mwoauthviewprivate": "{{doc-right|mwoauthviewprivate}}",
+ "right-mwoauthmanagemygrants": "{{doc-right|mwoauthmanagemygrants}}",
+ "action-mwoauthmanageconsumer": "{{doc-action|mwoauthmanageconsumer}}\n{{Identical|Manage OAuth consumer}}",
+ "action-mwoauthsuppress": "{{doc-action|mwoauthsuppress}}",
+ "action-mwoauthmanagemygrants": "{{doc-action|mwoauthmanagemygrants}}",
+ "action-mwoauthproposeconsumer": "{{doc-action|mwoauthproposeconsumer}}",
+ "action-mwoauthupdateownconsumer": "{{doc-action|mwoauthupdateownconsumer}}",
+ "action-mwoauthviewprivate": "{{doc-action|mwoauthviewprivate}}",
+ "action-mwoauthviewsuppressed": "{{doc-action|mwoauthviewsuppressed}}",
+ "mwoauth-tag-reserved": "Error message displayed on [[Special:Tags]] when a user attempts to manually create a change tag reserved by OAuth.",
+ "mwoauth-botpasswords-note": "Added to the top of [[Special:BotPasswords]] to point out that OAuth is a superior alternative. Parameters:\n* $1 - URL for [[Special:OAuthConsumerRegistration]], which might be on another wiki",
+ "mwoauth-api-module-disabled": "Error message for when an API module is disabled when OAuth is in use. Parameters:\n* $1 - The API module name.",
+ "echo-category-title-oauth-owner": "{{doc-echo-category-title|tooltip=Echo-pref-tooltip-oauth-owner}}",
+ "echo-pref-tooltip-oauth-owner": "{{doc-echo-pref-tooltip|title=Echo-category-title-oauth-owner}}",
+ "echo-category-title-oauth-admin": "{{doc-echo-category-title|tooltip=Echo-pref-tooltip-oauth-admin}}",
+ "echo-pref-tooltip-oauth-admin": "{{doc-echo-pref-tooltip|title=Echo-category-title-oauth-admin}}",
+ "notification-oauth-app-propose-title": "Text of the notification sent to OAuth admins when a new app is proposed.\n* $1: app owner\n* $2: name of the app",
+ "notification-oauth-app-update-title": "Text of the notification sent to OAuth admins when the app is updated.\n* $1: app owner\n* $2: name of the app",
+ "notification-oauth-app-approve-title": "Text of the notification sent to OAuth app owner when the app is approved.\n* $1: approving admin\n* $2: name of the app\n* $3: app owner",
+ "notification-oauth-app-reject-title": "Text of the notification sent to OAuth app owner when the app is rejected.\n* $1: rejecting admin\n* $2: name of the app\n* $3: app owner",
+ "notification-oauth-app-disable-title": "Text of the notification sent to OAuth app owner when the app is disabled.\n* $1: disabling admin\n* $2: name of the app\n* $3: app owner",
+ "notification-oauth-app-reenable-title": "Text of the notification sent to OAuth app owner when the app is reenabled.\n* $1: reenabling admin\n* $2: name of the app\n* $3: app owner",
+ "notification-oauth-app-propose-subject": "Email subject of the notification sent to OAuth admins when a new app is proposed.\n* $1: app owner\n* $2: name of the app\n\nSee also: {{msg-mw|notification-oauth-app-propose-email-batch-body}}",
+ "notification-oauth-app-update-subject": "Email subject of the notification sent to OAuth admins when the app is updated.\n* $1: app owner\n* $2: name of the app\n\nSee also: {{msg-mw|notification-oauth-app-update-email-batch-body}}",
+ "notification-oauth-app-approve-subject": "Email subject of the notification sent to OAuth app owner when the app is approved.\n* $1: approving admin\n* $2: name of the app\n* $3: app owner\n\nSee also: {{msg-mw|notification-oauth-app-approve-email-batch-body}}",
+ "notification-oauth-app-reject-subject": "Email subject of the notification sent to OAuth app owner when the app is rejected.\n* $1: rejecting admin\n* $2: name of the app\n* $3: app owner\n\nSee also: {{msg-mw|notification-oauth-app-reject-email-batch-body}}",
+ "notification-oauth-app-disable-subject": "Email subject of the notification sent to OAuth app owner when the app is disabled.\n* $1: disabling admin\n* $2: name of the app\n* $3: app owner\n\nSee also: {{msg-mw|notification-oauth-app-disable-email-batch-body}}",
+ "notification-oauth-app-reenable-subject": "Email subject of the notification sent to OAuth app owner when the app is reenabled.\n* $1: reenabling admin\n* $2: name of the app\n* $3: app owner\n\nSee also: {{msg-mw|notification-oauth-app-reenable-email-batch-body}}",
+ "notification-oauth-app-propose-primary-link": "Text of the link which appears at the end of the email notification {{msg-mw|notification-oauth-app-propose-email-batch-body}}. It links to the app management page for OAuth admins.",
+ "notification-oauth-app-update-primary-link": "Text of the link which appears at the end of the email notification {{msg-mw|notification-oauth-app-update-email-batch-body}}. It links to the app management page for OAuth admins.",
+ "notification-oauth-app-approve-primary-link": "Text of the link which appears at the end of the email notification {{msg-mw|notification-oauth-app-approve-email-batch-body}}. It links to the app update page for app owners.",
+ "notification-oauth-app-reject-primary-link": "Text of the link which appears at the end of the email notification {{msg-mw|notification-oauth-app-reject-email-batch-body}}. It links to the app update page for app owners.",
+ "notification-oauth-app-disable-primary-link": "Text of the link which appears at the end of the email notification {{msg-mw|notification-oauth-app-disable-email-batch-body}}. It links to the app update page for app owners.",
+ "notification-oauth-app-reenable-primary-link": "Text of the link which appears at the end of the email notification {{msg-mw|notification-oauth-app-reenable-email-batch-body}}. It links to the app update page for app owners.",
+ "notification-oauth-app-body": "Text of the body of app stage change notifications. $1 is the reason given by the user who made the change.\n{{Identical|Reason}}",
+ "mwoauth-oauth-version": "Used as a label in multiple places for the field indicating OAuth version consumer is registered to use",
+ "mwoauth-oauth-version-1": "Label for OAuth version 1.0a",
+ "mwoauth-oauth-version-2": "Label for OAuth version 2.0",
+ "mwoauth-oauth2-is-confidential": "Used as a field label that indicates if the consumer (client) is confidential",
+ "mwoauth-oauth2-is-confidential-help": "Help message for the field that indicates if the consumer (client) is confidential",
+ "mwoauth-oauth2-granttypes": "Used as a label for field that determines which types of OAuth2 grants consumer can use",
+ "mwoauth-oauth2-granttype-auth-code": "Display label for grant type authorization_code",
+ "mwoauth-oauth2-granttype-refresh-token": "Display label for grant type refresh_token",
+ "mwoauth-oauth2-granttype-client-credentials": "Display label for grant type client_credentials",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Error when client application attempts to retrieve access token, without having the user approve the application first",
+ "mwoauth-oauth2-error-user-approval-deny": "Error message returned to the client application if user rejected the approval request",
+ "mwoauth-oauth-unsupported-version": "Error message when targeted endpoint does not support OAuth version being used.\n$1 - OAuth version",
+ "mwoauth-oauth2-error-unauthorized-scope": "Error when application is not allowed to use requested scope.\n$1 - first not allowed scope",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Error for OAuth2 owner-only clients, when client is not allowed to use 'client_credentials' grant",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Error displayed when OAuth2 access token cannot be retrieved for owner-only application.\n$1 - error message (reason)",
+ "mwoauth-oauth2-error": "Error message to be displayed to the user on OAuth2 errors.\n$1 - error type\n$2 - error message\n$3-hint",
+ "mwoauth-oauth2-error-server-error": "OAuth2 error for error type server_error",
+ "mwoauth-oauth2-error-invalid-request": "OAuth2 error for error type invalid_request",
+ "mwoauth-oauth2-error-unauthorized-client": "OAuth2 error for error type unauthorized_client",
+ "mwoauth-oauth2-error-access-denied": "OAuth2 error for error type access_denied",
+ "mwoauth-oauth2-error-unsupported-response-type": "OAuth2 error for error type unsupported_response_type",
+ "mwoauth-oauth2-error-invalid-scope": "OAuth2 error for error type invalid_scope",
+ "mwoauth-oauth2-error-temporarily-unavailable": "OAuth2 error for error type temporarily_unavailable",
+ "mwoauth-oauth2-error-invalid-client": "OAuth2 error for error type invalid_client",
+ "mwoauth-oauth2-error-request-not-verified": "Error message when verified properties are accessed before verifying the request",
+ "mwoauth-oauth2-invalid-access-token": "Error when verifying the access token and the token is invalid",
+ "mwoauth-consumer-access-no-user": "Error message when trying to save user approval with anonymous user",
+ "mwoauth-login-required-reason": "Helper text that appears when a user is redirected to Special:Login page because they attempted authorisation while not logged in on the provider",
+ "mwoauthconsumer-consumer-view": "Used as link text in a navigation menu.",
+ "mwoauthconsumer-application-view": "Used as link text in a navigation menu."
+}
diff --git a/OAuth/i18n/ro.json b/OAuth/i18n/ro.json
new file mode 100644
index 00000000..449d4353
--- /dev/null
+++ b/OAuth/i18n/ro.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Minisarm",
+ "Rsocol",
+ "XXN"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplicații conectate:",
+ "mwoauth-prefs-managegrantslink": "Administrează {{PLURAL:$1|$1 aplicație conectată|$1 aplicații conectate|$1 de aplicații conectate}}",
+ "oauthmanagemygrants": "Administrare aplicații conectate",
+ "mwoauthmanagemygrants-text": "Această pagină afișează toate aplicațiile care pot folosi contul dumneavoastră. Pentru fiecare astfel de aplicație, accesul este limitat de permisiunile pe care le-ați acordat aplicației atunci când ați autorizat-o să acționeze în numele dumneavoastră. Dacă ați autorizat separat o aplicație să acceseze anumite proiecte în numele dumneavoastră, veți vedea mai jos configurări separate pentru fiecare proiect.\n\nAplicațiile conectate accesează contul dumneavoastră folosind protocolul OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Aflați mai multe despre aplicații conectate])</span>"
+}
diff --git a/OAuth/i18n/roa-tara.json b/OAuth/i18n/roa-tara.json
new file mode 100644
index 00000000..d3a40e46
--- /dev/null
+++ b/OAuth/i18n/roa-tara.json
@@ -0,0 +1,79 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joetaras"
+ ]
+ },
+ "mwoauth-desc": "Permette l'ause de le API de OAuth 1.0a e OAuth 2.0 de autendicazione",
+ "mwoauth-missing-field": "Valore zumbate pu cambe \"$1\"",
+ "mwoauth-invalid-field": "Valore date invalide pu cambe \"$1\"",
+ "mwoauth-field-hidden": "(sta 'mbormazione jè scunnute)",
+ "mwoauth-field-private": "(sta 'mbormazione jè private)",
+ "mwoauth-consumer-key": "Chiave d'u consumatore:",
+ "mwoauth-consumer-name": "Nome de l'applicazione:",
+ "mwoauth-consumer-version": "Versione d'u consumatore:",
+ "mwoauth-consumer-user": "Pubblecatore:",
+ "mwoauth-consumer-stage": "State de mò:",
+ "mwoauth-consumer-email": "Indirizze email de condatte:",
+ "mwoauth-consumer-description": "Descrizione de l'applicazione:",
+ "mwoauth-consumer-callbackurl": "URL de \"callback\" OAuth:",
+ "mwoauth-consumer-wiki": "Pruggette applicabbile:",
+ "mwoauth-consumer-restrictions": "Ause le restriziune:",
+ "mwoauth-consumer-rsakey": "Chiave pubblche RSA (opzionale):",
+ "mwoauth-consumer-reason": "Mutive:",
+ "mwoauth-consumer-stage-proposed": "proposte",
+ "mwoauth-consumer-stage-rejected": "scettate",
+ "mwoauth-consumer-stage-expired": "scadute",
+ "mwoauth-consumer-stage-approved": "approvate",
+ "mwoauth-consumer-stage-disabled": "disabbilitate",
+ "mwoauth-consumer-stage-suppressed": "scangellate",
+ "mwoauthconsumerregistration-navigation": "Navigazzione:",
+ "mwoauthconsumerregistration-main": "Prengepàle",
+ "mwoauthconsumerregistration-user": "Pubblecatore",
+ "mwoauthconsumerregistration-description": "Descrizione",
+ "mwoauthconsumerregistration-email": "Email de condatte",
+ "mwoauthconsumerregistration-consumerkey": "Chiave d'u consumatore",
+ "mwoauthconsumerregistration-stage": "State",
+ "mwoauthconsumerregistration-lastchange": "Urteme cangiamende",
+ "mwoauthconsumerregistration-manage": "gestisce",
+ "mwoauthmanageconsumers-type": "Code:",
+ "mwoauthmanageconsumers-showproposed": "Richieste proposte",
+ "mwoauthmanageconsumers-showrejected": "Richieste scettate",
+ "mwoauthmanageconsumers-showexpired": "Richieste scadute",
+ "mwoauthmanageconsumers-linkproposed": "richieste proposte",
+ "mwoauthmanageconsumers-linkrejected": "richieste scettate",
+ "mwoauthmanageconsumers-linkexpired": "richieste scadute",
+ "mwoauthmanageconsumers-linkapproved": "richieste approvate",
+ "mwoauthmanageconsumers-linkdisabled": "richieste disabbilitate",
+ "mwoauthmanageconsumers-main": "Prengepàle",
+ "mwoauthmanageconsumers-name": "Cliente",
+ "mwoauthmanageconsumers-user": "Pubblecatore",
+ "mwoauthmanageconsumers-description": "Descrizione",
+ "mwoauthmanageconsumers-email": "Email de condatte",
+ "mwoauthmanageconsumers-consumerkey": "Chiave d'u consumatore",
+ "mwoauthmanageconsumers-lastchange": "Urteme cangiamende",
+ "mwoauthmanageconsumers-review": "revisione/gestisce",
+ "mwoauthmanageconsumers-approve": "Approvate",
+ "mwoauthmanageconsumers-reject": "Scettate",
+ "mwoauthmanageconsumers-rsuppress": "Scettate e soppresse",
+ "mwoauthmanageconsumers-disable": "Disabbilitate",
+ "mwoauthmanageconsumers-dsuppress": "Disabilitate e soppresse",
+ "mwoauthmanageconsumers-reenable": "Approvate",
+ "mwoauthmanageconsumers-reason": "Mutive:",
+ "mwoauthlistconsumers-view": "dettaglie",
+ "mwoauthlistconsumers-name": "Nome de l'applicazione",
+ "mwoauthlistconsumers-version": "Versione d'u consumatore",
+ "mwoauthlistconsumers-user": "Pubblecatore",
+ "mwoauthlistconsumers-description": "Descrizione",
+ "mwoauthlistconsumers-wiki": "Pruggette applicabbile",
+ "mwoauthlistconsumers-callbackurl": "URL de \"callback\" OAuth",
+ "mwoauthlistconsumers-grants": "Deritte applicabbile",
+ "mwoauthlistconsumers-basicgrantsonly": "(sulamende accesse base)",
+ "mwoauthlistconsumers-status": "State",
+ "mwoauth-consumer-stage-any": "tutte",
+ "mwoauthlistconsumers-status-proposed": "proposte",
+ "mwoauthlistconsumers-status-approved": "approvate",
+ "mwoauthlistconsumers-status-disabled": "disabbilitate",
+ "mwoauthlistconsumers-status-rejected": "scettate",
+ "mwoauthlistconsumers-status-expired": "scadute"
+}
diff --git a/OAuth/i18n/ru.json b/OAuth/i18n/ru.json
new file mode 100644
index 00000000..a34dde41
--- /dev/null
+++ b/OAuth/i18n/ru.json
@@ -0,0 +1,264 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alexander Yukal",
+ "Av6",
+ "Diman Russkov",
+ "Diralik",
+ "INS Pirat",
+ "Illythr",
+ "Iltever",
+ "Iluvatar",
+ "Kaganer",
+ "Katunchik",
+ "Lockal",
+ "Mailman",
+ "Marina Melik-Adamyan",
+ "Meshkov.a",
+ "Metastasis",
+ "Midnight Gambler",
+ "Movses",
+ "NBS",
+ "Nk88",
+ "Okras",
+ "Putnik",
+ "Rubin",
+ "Rubin16",
+ "SergeyButkov",
+ "Vlad5250",
+ "Yurik",
+ "Дмитрий"
+ ]
+ },
+ "mwoauth-desc": "Позволяет использование OAuth 1.0a и OAuth 2.0 для API авторизации",
+ "mwoauth-nosubpage-explanation": "OAuth - это механизм, который позволяет внешним приложениям идентифицировать участника {{SITENAME}} или действовать от его имени после получения разрешения от этого участника.\n\nЧтобы эта страница что-то сделала, требуется больше параметров. Если вы были отправлены сюда из внешнего приложения, это, вероятно, было связано с ошибкой в этом приложении; Вы должны связаться с автором.",
+ "mwoauth-verified": "Приложению теперь разрешён доступ к MediaWiki от вашего имени.\n\nДля завершения процесса, предоставьте приложению это проверочное значение: '''$1'''",
+ "mwoauth-db-readonly": "База данных OAuth временно заблокирована. Повторите действие через несколько минут.",
+ "mwoauth-missing-field": "Отсутствует значение для поля «$1»",
+ "mwoauth-invalid-field": "Недопустимое значение для поля «$1»",
+ "mwoauth-invalid-field-generic": "Недопустимое значение",
+ "mwoauth-field-hidden": "(эта информация скрыта)",
+ "mwoauth-field-private": "(эта информация конфиденциальна)",
+ "mwoauth-prefs-managegrants": "Подключённые приложения:",
+ "mwoauth-prefs-managegrantslink": "Управление {{PLURAL:$1|$1 подключённым приложением|$1 подключёнными приложениями|0=подключёнными приложениями}}",
+ "mwoauth-consumer-allwikis": "Все проекты на этом сайте",
+ "mwoauth-consumer-key": "Идентификатор клиента:",
+ "mwoauth-consumer-name": "Название приложения:",
+ "mwoauth-consumer-version": "Версия клиента:",
+ "mwoauth-consumer-user": "Издатель:",
+ "mwoauth-consumer-stage": "Текущее состояние:",
+ "mwoauth-consumer-email": "Контактный адрес электронной почты:",
+ "mwoauth-consumer-email-help": "Видимо только для тех, кто одобряет новые клиентские приложения",
+ "mwoauth-consumer-owner-only-label": "Только для владельца:",
+ "mwoauth-consumer-owner-only": "Этот клиент используется только участником $1.",
+ "mwoauth-consumer-owner-only-help": "С этой опцией, клиент будет автоматически одобрен и принят для использования участником $1. Им не сможет воспользоваться другой участник, и обычная процедура авторизации не будет работать. Действия этого клиента не будут отмечаться тегами.",
+ "mwoauth-consumer-description": "Описание приложения:",
+ "mwoauth-consumer-callbackurl": "URL-адрес «обратного вызова» OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Позволяет клиенту указать обратный вызов в запросах и сверху использовать URL обратного вызова как обязательный префикс.",
+ "mwoauth-consumer-granttypes": "Запрошенные права:",
+ "mwoauth-consumer-grantsneeded": "Применимые разрешения:",
+ "mwoauth-consumer-required-grant": "Применимо к клиенту",
+ "mwoauth-consumer-wiki": "Применимо к проекту:",
+ "mwoauth-consumer-wiki-thiswiki": "Текущий проект ($1)",
+ "mwoauth-consumer-restrictions": "Ограничения на использование:",
+ "mwoauth-consumer-restrictions-json": "Ограничения на использование (JSON):",
+ "mwoauth-consumer-rsakey": "Публичный RSA-ключ (не обязательно):",
+ "mwoauth-consumer-rsakey-help": "Введите открытый ключ для использования метода сигнатуры RSA-SHA1. Оставьте его пустым, чтобы использовать произвольный секрет HMAC-SHA1. Если вы не знаете, что лучше использовать, оставьте это поле пустым.",
+ "mwoauth-consumer-secretkey": "Секретный ключ клиента:",
+ "mwoauth-consumer-accesstoken": "Код (токен) доступа:",
+ "mwoauth-consumer-reason": "Причина:",
+ "mwoauth-consumer-developer-agreement": "Добавляя это приложение, вы соглашаетесь с тем, что мы оставляем за собой право отключить ваше приложение, удалить или ограничить для вас или вашего приложения доступ к этому сайту, а также осуществлять любые другие действия, которые мы сочтём целесообразными, если решим, что вы или ваше приложение нарушаете любую политику, руководство и руководящий принцип этого сайта. Мы можем в любое время без предварительного уведомления изменить эту политику для приложений по нашему собственному усмотрению и по мере необходимости. Ваше дальнейшее использование OAuth означает согласие с этими изменениями.",
+ "mwoauth-consumer-email-unconfirmed": "Адрес электронной почты вашей учётной записи ещё не был подтверждён.",
+ "mwoauth-consumer-email-mismatched": "Указанный адрес электронной почты должен совпадать с тем адресом, который указан в настройках вашей учётной записи.",
+ "mwoauth-consumer-alreadyexists": "Клиент с этой комбинацией из имени, версии и издателя уже существует",
+ "mwoauth-consumer-alreadyexistsversion": "Клиент с этой комбинации имени и издателя с такой же или более высокой версией («$1») уже существует",
+ "mwoauth-consumer-not-accepted": "Не удалось обновить информацию для ожидающего клиентского запроса",
+ "mwoauth-consumer-not-proposed": "Клиент в настоящее время не предлагается",
+ "mwoauth-consumer-not-disabled": "Клиент в настоящее время не отключён",
+ "mwoauth-consumer-not-approved": "Клиент не был одобрен (может быть отключён)",
+ "mwoauth-missing-consumer-key": "Идентификатор клиента не был представлен.",
+ "mwoauth-invalid-consumer-key": "Не существует клиента с данным ключом.",
+ "mwoauth-invalid-access-token": "Не существует кода доступа с данным ключом.",
+ "mwoauth-invalid-access-wrongwiki": "Клиент может использоваться только в проекте «$1».",
+ "mwoauth-consumer-conflict": "Кто-то изменил атрибуты клиента, пока вы их смотрели. Пожалуйста, попробуйте ещё раз. Возможно, стоит проверить журнал изменений.",
+ "mwoauth-consumer-grantshelp": "Каждое разрешение предоставляет доступ к перечню пользовательских прав, имеющихся у учётной записи участника. См. более подробно в [[Special:ListGrants|таблице разрешений]].",
+ "mwoauth-consumer-stage-proposed": "предложен",
+ "mwoauth-consumer-stage-rejected": "отклонён",
+ "mwoauth-consumer-stage-expired": "истёк",
+ "mwoauth-consumer-stage-approved": "подтверждён",
+ "mwoauth-consumer-stage-disabled": "отключён",
+ "mwoauth-consumer-stage-suppressed": "подавлен",
+ "oauthconsumerregistration": "Регистрация клиента OAuth",
+ "mwoauthconsumerregistration-navigation": "Навигацияː",
+ "mwoauthconsumerregistration-propose": "Предложить новое клиентское подключение",
+ "mwoauthconsumerregistration-list": "Мой список клиентских подключений",
+ "mwoauthconsumerregistration-main": "Главная",
+ "mwoauthconsumerregistration-propose-legend": "Новое клиентское OAuth-приложение",
+ "mwoauthconsumerregistration-update-legend": "Обновить клиентское OAuth-приложение",
+ "mwoauthconsumerregistration-propose-submit": "Предложить клиента",
+ "mwoauthconsumerregistration-update-submit": "Обновить клиента",
+ "mwoauthconsumerregistration-none": "Вы не управляете никакими клиентскими OAuth-приложениями.",
+ "mwoauthconsumerregistration-name": "Клиент",
+ "mwoauthconsumerregistration-user": "Издатель",
+ "mwoauthconsumerregistration-description": "Описание",
+ "mwoauthconsumerregistration-email": "Контактный адрес электронной почты",
+ "mwoauthconsumerregistration-consumerkey": "Идентификатор клиента",
+ "mwoauthconsumerregistration-stage": "Состояние",
+ "mwoauthconsumerregistration-lastchange": "Последнее изменение",
+ "mwoauthconsumerregistration-manage": "управление",
+ "mwoauthconsumerregistration-resetsecretkey": "Сбросить секретный ключ, установив новое значение",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Ваши OAuth-токены были сброшены. Новые токены:\n; Токен клиента: $1\n; Секрет клиента: $2\n; Токен доступа: $3\n; Секрет доступа: $4\n<em>Пожалуйста, сохраните эти данные для будущего использования.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Вы должны подтвердить ваш адрес электронной почты, прежде чем создавать приложения с OAuth.\nПожалуйста, задайте и подтвердите адрес электронной почты через ваши [[Special:Preferences|пользовательские настройки]].",
+ "oauthmanageconsumers": "Управление клиентскими OAuth-приложениями",
+ "mwoauthmanageconsumers-notloggedin": "Вы должны авторизоваться на сайте для доступа к этой странице.",
+ "mwoauthmanageconsumers-type": "Очереди:",
+ "mwoauthmanageconsumers-showproposed": "Предлагаемые запросы",
+ "mwoauthmanageconsumers-showrejected": "Отклонённые запросы",
+ "mwoauthmanageconsumers-showexpired": "Устаревшие запросы",
+ "mwoauthmanageconsumers-linkproposed": "предложенные запросы",
+ "mwoauthmanageconsumers-linkrejected": "отклонённые запросы",
+ "mwoauthmanageconsumers-linkexpired": "устаревшие запросы",
+ "mwoauthmanageconsumers-linkapproved": "одобренные запросы",
+ "mwoauthmanageconsumers-linkdisabled": "отключенные запросы",
+ "mwoauthmanageconsumers-main": "Главная",
+ "mwoauthmanageconsumers-q-proposed": "Очередь предложенных запросов потребителей",
+ "mwoauthmanageconsumers-q-expired": "Очередь просроченных запросов потребителей",
+ "mwoauthmanageconsumers-l-disabled": "Список отключенных клиентов",
+ "mwoauthmanageconsumers-name": "Клиент",
+ "mwoauthmanageconsumers-user": "Издатель",
+ "mwoauthmanageconsumers-description": "Описание",
+ "mwoauthmanageconsumers-email": "Контактный адрес электронной почты",
+ "mwoauthmanageconsumers-consumerkey": "Идентификатор клиента",
+ "mwoauthmanageconsumers-lastchange": "Последнее изменение",
+ "mwoauthmanageconsumers-review": "обзор/управление",
+ "mwoauthmanageconsumers-confirm-text": "Используйте эту форму, чтобы одобрить, отклонить, отключить или повторно включить клиента.",
+ "mwoauthmanageconsumers-confirm-legend": "Управление клиентскими OAuth-приложениями",
+ "mwoauthmanageconsumers-action": "Изменить состояние:",
+ "mwoauthmanageconsumers-approve": "Одобрено",
+ "mwoauthmanageconsumers-reject": "Отклонено",
+ "mwoauthmanageconsumers-rsuppress": "Отклонено и скрыто",
+ "mwoauthmanageconsumers-disable": "Отключено",
+ "mwoauthmanageconsumers-dsuppress": "Отключено и подавлено",
+ "mwoauthmanageconsumers-reenable": "Одобрено",
+ "mwoauthmanageconsumers-reason": "Причина:",
+ "mwoauthmanageconsumers-confirm-submit": "Обновление состояния клиента",
+ "mwoauthmanageconsumers-success-approved": "Запрос был одобрен.",
+ "mwoauthmanageconsumers-success-rejected": "Запрос был отклонён.",
+ "mwoauthmanageconsumers-success-disabled": "Клиент был отключён.",
+ "mwoauthmanageconsumers-success-reanable": "Клиент был включён повторно.",
+ "mwoauthmanageconsumers-search-name": "клиенты с тем же названием",
+ "mwoauthmanageconsumers-search-publisher": "клиенты от того же участника",
+ "oauthlistconsumers": "Список OAuth-приложений",
+ "mwoauthlistconsumers-legend": "Обзор OAuth-приложений",
+ "mwoauthlistconsumers-view": "подробности",
+ "mwoauthlistconsumers-none": "Приложений по этим критериям не найдено.",
+ "mwoauthlistconsumers-name": "Название приложения",
+ "mwoauthlistconsumers-version": "Версия клиента",
+ "mwoauthlistconsumers-user": "Издатель",
+ "mwoauthlistconsumers-description": "Описание",
+ "mwoauthlistconsumers-wiki": "Применимо к проекту",
+ "mwoauthlistconsumers-callbackurl": "«URL-адрес обратного вызова» OAuth:",
+ "mwoauthlistconsumers-grants": "Применимые разрешения",
+ "mwoauthlistconsumers-basicgrantsonly": "(только базовый доступ)",
+ "mwoauthlistconsumers-status": "Состояние",
+ "mwoauth-consumer-stage-any": "любое",
+ "mwoauthlistconsumers-status-proposed": "предложено",
+ "mwoauthlistconsumers-status-approved": "одобрено",
+ "mwoauthlistconsumers-status-disabled": "отключено",
+ "mwoauthlistconsumers-status-rejected": "отклонено",
+ "mwoauthlistconsumers-status-expired": "истекло",
+ "mwoauthlistconsumers-navigation": "Навигацияː",
+ "oauthmanagemygrants": "Управление подключёнными приложениями",
+ "mwoauthmanagemygrants-text": "На этой странице перечислены все приложения, которые могут использовать вашу учётную запись. Область доступа каждого такого приложения ограничена разрешениями, которые вы ему предоставили для действий от вашего имени. Если вы предоставили приложению доступ к вашей учётной записи в различных родственных проектах, то ниже вы можете просмотреть отдельные настройки для каждого такого проекта.\n\nПодключённые приложения получают доступ к вашей учётной записи с помощью протокола OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Узнать больше о подключаемых приложениях])</span>",
+ "mwoauthmanagemygrants-navigation": "Навигацияː",
+ "mwoauthmanagemygrants-showlist": "Список подключенных приложений",
+ "mwoauthmanagemygrants-none": "Ещё нет приложений, подключенных к вашей учётной записи.",
+ "mwoauthmanagemygrants-user": "Издатель:",
+ "mwoauthmanagemygrants-description": "Описание",
+ "mwoauthmanagemygrants-wikiallowed": "Одобрено для проекта:",
+ "mwoauthmanagemygrants-grants": "Применимые разрешения",
+ "mwoauthmanagemygrants-grantsallowed": "Одобренные разрешения",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Одобрены следующие разрешения:",
+ "mwoauthmanagemygrants-review": "управлять доступом",
+ "mwoauthmanagemygrants-revoke": "отменить доступ",
+ "mwoauthmanagemygrants-grantaccept": "Разрешено",
+ "mwoauthmanagemygrants-update-text": "Используйте форму ниже, чтобы изменить разрешения, предоставленные приложению для действий от вашего имени.",
+ "mwoauthmanagemygrants-revoke-text": "Используйте форму ниже, чтобы отозвать полномочия, выданные приложению для действий от вашего имени.",
+ "mwoauthmanagemygrants-confirm-legend": "Управление подключённым приложением",
+ "mwoauthmanagemygrants-update": "Обновить разрешения",
+ "mwoauthmanagemygrants-renounce": "Разорвать авторизацию",
+ "mwoauthmanagemygrants-action": "Изменить состояние:",
+ "mwoauthmanagemygrants-confirm-submit": "Обновить состояние кода (токена) доступа",
+ "mwoauthmanagemygrants-success-update": "Настройки для этого приложения были обновлены.",
+ "mwoauthmanagemygrants-success-renounce": "Доступ приложения к вашей учётной записи аннулирован.",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|предложил|предложила}} клиент OAuth (ключ клиента: $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|обновил|обновила}} клиент OAuth (ключ клиента: $4)",
+ "log-action-filter-mwoauthconsumer": "Тип клиентского действия с OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Клиентское одобрение с OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Создание клиентов с OAuth только для владельца",
+ "log-action-filter-mwoauthconsumer-disable": "Клиентское отключение с OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Клиентское предложение с OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Клиентское перевключение с OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Клиентское отклонение с OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Клиентское обновление с OAuth",
+ "mwoauthconsumer-consumer-logpage": "Журнал клиентов OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Журнал одобрений, отклонений и отключений зарегистрированных клиентов OAuth.",
+ "mwoauthdatastore-bad-token": "Не был найден подходящий для вашего запроса токен.",
+ "mwoauthdatastore-bad-source-ip": "Запрос пришёл с некорректного IP-адреса.",
+ "mwoauthdatastore-bad-verifier": "Предоставленный проверочный код недействителен.",
+ "mwoauthdatastore-invalid-token-type": "Запрошенный тип токена некорректен.",
+ "mwoauthserver-invalid-request-token": "Недопустимый токен в вашем запросе.",
+ "mwoauth-invalid-authorization-title": "Ошибка авторизации OAuth",
+ "mwoauth-invalid-authorization": "Авторизационные заголовки вашего запроса неверны: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Авторизационные заголовки вашего запроса неверны для $1",
+ "mwoauth-invalid-authorization-wrong-user": "Авторизационные заголовки вашего запроса соотнесены с другим пользователем",
+ "mwoauth-invalid-authorization-not-approved": "Похоже, что приложение, которое вы пытаетесь подключить, настроено неправильно. Обратитесь за помощью к автору — «$1».",
+ "mwoauth-invalid-authorization-blocked-user": "Авторизационные заголовки вашего запроса относятся к заблокированному пользователю",
+ "mwoauth-form-description-allwikis": "Здравствуйте, $1!\n\nДля того, чтобы завершить ваш запрос «$2», требуется разрешение от вашего имени на выполнение следующих действий во всех проектах этого сайта:\n\n$4",
+ "mwoauth-form-description-onewiki": "Здравствуйте $1!\n\nДля того, чтобы завершить ваш запрос «$2», требуется разрешение от вашего имени на выполнение следующих действий в проекте «$4»:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Здравствуйте, $1!\n\nДля того, чтобы завершить ваш запрос «$2», требуется разрешение от вашего имени на доступ к информации о всех проектах этого сайта. В ваш аккаунт не будут внесены никакие изменения.",
+ "mwoauth-form-description-onewiki-nogrants": "Здравствуйте, $1!\n\nДля того, чтобы завершить ваш запрос «$2», требуется разрешение от вашего имени на доступ к информации в проекте «$4». В ваш аккаунт не будут внесены никакие изменения.",
+ "mwoauth-form-description-allwikis-privateinfo": "Здравствуйте, $1!\n\nДля того, чтобы выполнить ваш запрос, приложению «$2» требуется разрешение на доступ к информации о вас во всех проектах этого сайта, включая ваше настоящее имя и адрес электронной почты. В ваш аккаунт не будут внесены никакие изменения.",
+ "mwoauth-form-description-onewiki-privateinfo": "Здравствуйте, $1!\n\nДля того, чтобы завершить ваш запрос, приложению «$2» требуется разрешение на доступ к информации о вас в проекте «$4», включая ваше настоящее имя и адрес электронной почты. При этом ваш аккаунт не будет изменён.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Здравствуйте, $1!\n\nДля того, чтобы завершить ваш запрос, приложению '''$2''' требуется разрешение на доступ к информации о вас, включая ваш адрес электронной почты, во всех проектах на этом сайте. В ваш аккаунт не будут внесены никакие изменения.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Здравствуйте, $1!\n\nДля того, чтобы завершить ваш запрос, приложению '''$2''' требуется разрешение на доступ к информации о вас, включая ваш адрес электронной почты, в проекте ''$4''. В ваш аккаунт не будут внесены никакие изменения.",
+ "mwoauth-form-button-approve": "Разрешить",
+ "mwoauth-form-button-cancel": "Отменить",
+ "mwoauth-error": "Ошибка подключения приложения",
+ "mwoauth-grants-heading": "Запросить разрешения:",
+ "mwoauth-grants-nogrants": "Приложение не запрашивает никаких прав.",
+ "mwoauth-granttype-normal": "Запрос авторизации для конкретных прав.",
+ "grant-mwoauth-authonly": "Только проверки личности участника, без возможности читать страницы или действовать от имени участника.",
+ "grant-mwoauth-authonlyprivate": "Только проверка личности участника, с доступом к настоящему имени и электронному адресу, без возможности читать страницы или действовать от имени участника.",
+ "mwoauth-listgrants-extra-summary": "== Специфические разрешения для OAuth ==\n\nЭти дополнительные разрешения применяются для клиентов OAuth.",
+ "mwoauth-oauth-exception": "Произошла ошибка в протоколе OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback должен быть установлен и иметь значение «oob» (с учётом регистра)",
+ "right-mwoauthproposeconsumer": "Предложение новых клиентов OAuth",
+ "right-mwoauthupdateownconsumer": "Обновление контролируемых клиентов OAuth",
+ "right-mwoauthmanageconsumer": "Управление клиентами OAuth",
+ "right-mwoauthsuppress": "Сокрытие клиентов OAuth",
+ "right-mwoauthviewsuppressed": "Просмотр скрытых клиентов OAuth",
+ "right-mwoauthviewprivate": "Просмотр персональных данных OAuth",
+ "right-mwoauthmanagemygrants": "Управление разрешениями OAuth",
+ "action-mwoauthmanageconsumer": "управление клиентскими OAuth-приложениями",
+ "action-mwoauthsuppress": "сокрытие клиентов OAuth",
+ "action-mwoauthmanagemygrants": "управление вашими разрешениями OAuth",
+ "action-mwoauthproposeconsumer": "предложение новых клиентских OAuth-приложений",
+ "action-mwoauthupdateownconsumer": "обновление контролируемых вами клиентских OAuth-приложений",
+ "action-mwoauthviewprivate": "просмотр частных данных OAuth",
+ "action-mwoauthviewsuppressed": "просмотр подавленных клиентских OAuth-приложений",
+ "mwoauth-botpasswords-note": "Примечание: [$1 OAuth] является более безопасным методом, чем пароли ботов. Его стоит использовать во всех случаях, когда бот его поддерживает.",
+ "echo-category-title-oauth-owner": "OAuth-разработка",
+ "echo-pref-tooltip-oauth-owner": "Оповещать меня о событиях, связанных с созданными мной OAuth-приложениями.",
+ "echo-category-title-oauth-admin": "Администратор OAuth",
+ "echo-pref-tooltip-oauth-admin": "Оповещать меня о событиях, связанных с проверяемыми OAuth-приложениями.",
+ "notification-oauth-app-propose-primary-link": "Проверить приложение",
+ "notification-oauth-app-update-primary-link": "Проверить приложение",
+ "notification-oauth-app-approve-primary-link": "Просмотр приложения",
+ "notification-oauth-app-reject-primary-link": "Просмотр приложения.",
+ "notification-oauth-app-disable-primary-link": "Просмотр приложения.",
+ "notification-oauth-app-reenable-primary-link": "Просмотр приложения.",
+ "notification-oauth-app-body": "Причина: $1",
+ "mwoauth-oauth2-granttype-auth-code": "Код авторизации",
+ "mwoauthconsumer-application-view": "Посмотреть это приложение"
+}
diff --git a/OAuth/i18n/sa.json b/OAuth/i18n/sa.json
new file mode 100644
index 00000000..2b75573e
--- /dev/null
+++ b/OAuth/i18n/sa.json
@@ -0,0 +1,33 @@
+{
+ "@metadata": {
+ "authors": [
+ "NehalDaveND"
+ ]
+ },
+ "mwoauth-consumer-reason": "कारणम् :",
+ "mwoauth-consumer-email-unconfirmed": "भवतः/भवत्याः वि-पत्रसङ्केतः इतोऽपि न प्रमाण्यभवत् ।",
+ "mwoauth-consumer-stage-rejected": "तिरस्कृतम्",
+ "mwoauth-consumer-stage-disabled": "निष्क्रियः",
+ "mwoauthconsumerregistration-navigation": "सञ्चरणम् :",
+ "mwoauthconsumerregistration-main": "मुख्यम्",
+ "mwoauthconsumerregistration-user": "प्रकाशकः",
+ "mwoauthconsumerregistration-description": "वर्णनम्",
+ "mwoauthconsumerregistration-stage": "स्थितिः",
+ "mwoauthmanageconsumers-main": "मुख्यम्",
+ "mwoauthmanageconsumers-user": "प्रकाशकः",
+ "mwoauthmanageconsumers-description": "वर्णनम्",
+ "mwoauthmanageconsumers-reject": "तिरस्कृतम्",
+ "mwoauthmanageconsumers-disable": "निष्क्रियः",
+ "mwoauthmanageconsumers-reason": "कारणम् :",
+ "mwoauthlistconsumers-view": "विवरणम्",
+ "mwoauthlistconsumers-user": "प्रकाशकः",
+ "mwoauthlistconsumers-description": "वर्णनम्",
+ "mwoauthlistconsumers-status": "स्थितिः",
+ "mwoauth-consumer-stage-any": "कोऽपि",
+ "mwoauthlistconsumers-status-disabled": "निष्क्रियः",
+ "mwoauthlistconsumers-status-rejected": "तिरस्कृतम्",
+ "mwoauthmanagemygrants-navigation": "सञ्चरणम् :",
+ "mwoauthmanagemygrants-user": "प्रकाशकः",
+ "mwoauthmanagemygrants-description": "वर्णनम्",
+ "mwoauth-form-button-cancel": "निरस्यताम्"
+}
diff --git a/OAuth/i18n/scn.json b/OAuth/i18n/scn.json
new file mode 100644
index 00000000..6e10b621
--- /dev/null
+++ b/OAuth/i18n/scn.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sarvaturi"
+ ]
+ },
+ "oauthlistconsumers": "Elencu di l'applicazzioni OAuth",
+ "oauthmanagemygrants": "Gistioni di l'applicazzioni cunnessi"
+}
diff --git a/OAuth/i18n/sco.json b/OAuth/i18n/sco.json
new file mode 100644
index 00000000..5ef07b2d
--- /dev/null
+++ b/OAuth/i18n/sco.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "John Reid",
+ "MJL"
+ ]
+ },
+ "mwoauth-db-readonly": "The OAuth database is temperarilie lockit. Please ettle at it again in twa-three minutes.",
+ "mwoauthconsumerregistration-description": "Descreeption",
+ "mwoauthmanageconsumers-description": "Descreeption",
+ "mwoauthlistconsumers-description": "Descreeption",
+ "mwoauthmanagemygrants-description": "Descreeption",
+ "mwoauthmanagemygrants-basic-tooltip": "Why can Ah naw update this graunt? This graunt gies yer connected appleecation basic permeessions that it requires fer tae function properlie. Gif ye dinna want this connected appleecation tae hae thir richts, ye shid revoke the appleecation's access."
+}
diff --git a/OAuth/i18n/sd.json b/OAuth/i18n/sd.json
new file mode 100644
index 00000000..f14ffc1d
--- /dev/null
+++ b/OAuth/i18n/sd.json
@@ -0,0 +1,61 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mehtab ahmed",
+ "Tweety"
+ ]
+ },
+ "mwoauth-field-hidden": "(هي ڄاڻ خفيا آهي)",
+ "mwoauth-field-private": "(هي ذاتي ڄاڻ آهي)",
+ "mwoauth-prefs-managegrants": "ڳنڍيل ايپس:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|$1 ڳنڍيل ايپليڪيشن|$1 ڳنڍيل ايپليڪيشنون|0=ڳنڍيل ايپليڪيشنون}} سنڀاليو",
+ "mwoauth-consumer-allwikis": "هن وڪي جي سڀئي منصوبن ۾",
+ "mwoauth-consumer-key": "واپرائيندڙ ڄاٻي:",
+ "mwoauth-consumer-name": "ايپليڪشن نالو:",
+ "mwoauth-consumer-version": "واپرائيندڙ ورزن:",
+ "mwoauth-consumer-user": "ڇاپيندڙ:",
+ "mwoauth-consumer-stage": "هاڻوڪي حالت:",
+ "mwoauth-consumer-email": "رابطي جو برقٽپال پتو:",
+ "mwoauth-consumer-owner-only-label": "رڳو-مالڪ:",
+ "mwoauth-consumer-description": "ايپليڪشن تفصيل",
+ "mwoauth-consumer-granttypes": "درخواست ڪيل اجازتون:",
+ "mwoauth-consumer-grantsneeded": "مناسب اجازتون:",
+ "mwoauth-consumer-reason": "سبب:",
+ "mwoauthconsumerregistration-navigation": "رهنمائي:",
+ "mwoauthconsumerregistration-main": "مُک",
+ "mwoauthconsumerregistration-propose-submit": "واپرائڻ جو سبب",
+ "mwoauthconsumerregistration-user": "ڇاپيندڙ",
+ "mwoauthconsumerregistration-description": "تفصيل",
+ "mwoauthconsumerregistration-email": "رابطي جي برقٽپال",
+ "mwoauthconsumerregistration-stage": "حالت",
+ "mwoauthconsumerregistration-lastchange": "آخري بدلاءُ",
+ "mwoauthconsumerregistration-manage": "انتظام",
+ "mwoauthmanageconsumers-type": "قطار:",
+ "mwoauthmanageconsumers-main": "مُک",
+ "mwoauthmanageconsumers-user": "ڇاپيندڙ",
+ "mwoauthmanageconsumers-description": "تفصيل",
+ "mwoauthmanageconsumers-email": "رابطي جي برقٽپال",
+ "mwoauthmanageconsumers-consumerkey": "واپرائيندڙ ڄاٻي:",
+ "mwoauthmanageconsumers-lastchange": "آخري بدلاءُ",
+ "mwoauthmanageconsumers-review": "جائزو/انتظام",
+ "mwoauthmanageconsumers-approve": "قبول ڪيل",
+ "mwoauthmanageconsumers-reject": "رد ڪيل",
+ "mwoauthmanageconsumers-disable": "غيرفعال",
+ "mwoauthmanageconsumers-reenable": "قبول ڪيل",
+ "mwoauthmanageconsumers-reason": "سبب:",
+ "mwoauth-consumer-stage-any": "ڪابہ",
+ "oauthmanagemygrants": "ڳنڊيل اپليڪيشنن جو انتظام",
+ "mwoauthmanagemygrants-text": "هتي اهي ايپليڪيشنون ڏنل آهن، جيڪي اوهان جي کاتي سان ڳنڍيل آهن. ڪنھن بہ ايپليڪيشن کي ڪيترا اختيار ڏنل آهن، اهي اوهان ايپليڪشن کي پھرين ڀيري استعمال ڪرڻ وقت ڏيندا آهيو. جيڪڏهن اوهان ڪنھن ٻي وڪي پراجيڪٽ لاءِ ڪو ايپليڪشن الڳ سان استعمال ڪريو ٿا تہ ان جي لاءِ هيٺ الڳ ترتيبون ڏنل آهن. \n\nڳنڍيل ايپليڪشن OAuth protocol ذريعي اوهان جي کاتي جو اختيار رکي ٿي. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth ڳنڍيل ايپليڪشنن بابت وڌيڪ پرايو])</span>",
+ "mwoauthmanagemygrants-showlist": "فھرست ڳنڍيل ايپليڪشن",
+ "mwoauthmanagemygrants-wikiallowed": "منصوبن ۾ اجازت:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "مناسب اجازت ڏنل:",
+ "mwoauthmanagemygrants-review": "رسد سنڀاليو",
+ "mwoauthmanagemygrants-revoke": "اختيار ختم ڪيو",
+ "mwoauthmanagemygrants-grantaccept": "اجازت",
+ "mwoauthmanagemygrants-revoke-text": "هيٺ ڄاڻيل اپليڪيشن جو اختيار ختم ڪيو.",
+ "mwoauthmanagemygrants-confirm-legend": "ڳنڊيل اپليڪيشنن جو انتظام",
+ "mwoauthmanagemygrants-update": "اجازتن جي ٻيهر ترتيب",
+ "mwoauthmanagemygrants-renounce": "خاتمو اختيار",
+ "mwoauthmanagemygrants-action": "حالت بدلايو:",
+ "mwoauth-form-button-cancel": "رد"
+}
diff --git a/OAuth/i18n/sh.json b/OAuth/i18n/sh.json
new file mode 100644
index 00000000..77713aeb
--- /dev/null
+++ b/OAuth/i18n/sh.json
@@ -0,0 +1,256 @@
+{
+ "@metadata": {
+ "authors": [
+ "Conquistador",
+ "Vlad5250"
+ ]
+ },
+ "mwoauth-desc": "Omogućuje upotrebu OAuth 1.0a za autorizaciju API-ja",
+ "mwoauth-verified": "Izvršniku je dopušten pristup MediaWikiju u vaše ime.\n\nDa biste dovršili postupak, na izvršniku navedite sledeću kontrolnu vrijednost: '''$1'''",
+ "mwoauth-db-readonly": "Baza podataka OAuth privremeno je zaključana. Pokušajte ponovo za nekoliko minuta.",
+ "mwoauth-missing-field": "Nedostaje vrijednost u polju \"$1\"",
+ "mwoauth-invalid-field": "U polju \"$1\" dana je nevažeća vrijednost",
+ "mwoauth-invalid-field-generic": "Navedena je nevažeća vrijednost",
+ "mwoauth-field-hidden": "(ovaj podatak je skriven)",
+ "mwoauth-field-private": "(ovaj podatak je povjerljiv)",
+ "mwoauth-prefs-managegrants": "Povezani izvršnici:",
+ "mwoauth-prefs-managegrantslink": "Upravljanje {{PLURAL:$1|$1 povezanim izvršnikom|$1 povezanim izvršnicima|0=povezanim izvršnicima}}",
+ "mwoauth-consumer-allwikis": "Svim projektima na ovom sajtu",
+ "mwoauth-consumer-key": "Potrošački ključ:",
+ "mwoauth-consumer-name": "Naziv izvršnika:",
+ "mwoauth-consumer-version": "Potrošačka verzija:",
+ "mwoauth-consumer-user": "Izdavač:",
+ "mwoauth-consumer-stage": "Trenutni status:",
+ "mwoauth-consumer-email": "E-pošta za kontakt:",
+ "mwoauth-consumer-email-help": "Vidljivo samo onima koji odobravaju nove korisnike",
+ "mwoauth-consumer-owner-only-label": "Samo vlasnik:",
+ "mwoauth-consumer-owner-only": "Ovaj je potrošač samo za upotrebu $1.",
+ "mwoauth-consumer-owner-only-help": "Označavanjem ove mogućnosti potrošač će biti automatski odobren i prihvaćen za korištenje od $1. Drugi ga suradnici neće moći koristiti, a normalni tijek ovlašćenja neće raditi. Radnje ovog potrošača neće biti označene.",
+ "mwoauth-consumer-description": "Opis izvršnika:",
+ "mwoauth-consumer-callbackurl": "URL poziva OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Omogući potrošaču povratni poziv u zahtjevima i iznad kako bi koristio URL za povratni poziv kao obvezni prigovor.",
+ "mwoauth-consumer-granttypes": "Vrste zahtijevanih dopuštenja:",
+ "mwoauth-consumer-grantsneeded": "Primjenjive dozvole:",
+ "mwoauth-consumer-required-grant": "Primjenjivi potrošač",
+ "mwoauth-consumer-wiki": "Primjenjivi projekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Trenutačni projekt ($1)",
+ "mwoauth-consumer-restrictions": "Ograničenja upotrebe:",
+ "mwoauth-consumer-restrictions-json": "Ograničenja upotrebe (JSON):",
+ "mwoauth-consumer-rsakey": "Javni RSA ključ (nije obavezno):",
+ "mwoauth-consumer-rsakey-help": "Unesite javni ključ za upotrebu metode potpisa RSA-SHA1. Ostavite ga praznim da biste koristili proizvoljnu tajnu HMAC-SHA1. Ako niste sigurni, ostavite prazno.",
+ "mwoauth-consumer-secretkey": "Tajna potrošačka šifra:",
+ "mwoauth-consumer-accesstoken": "Pristupna šifra:",
+ "mwoauth-consumer-reason": "Razlog:",
+ "mwoauth-consumer-developer-agreement": "Slanjem ovog izvršnika potvrđujete da zadržavamo pravo isključiti, ukloniti Vaš pristup ili pristup izvršnika na ovu web-lokaciju i pokrenuti druge radnje koje smatramo potrebnima ako uzmemo u obzir, prema vlastitom nahođenju, da Vi ili Vaš izvršnik kršite neko pravilo, smjernicu i načelo umrežavanja. Možemo smijeniti ova Pravila za izvršnike u bilo koje vrijeme bez prethodne najave, prema vlastitom nahođenju, prema onome što smatramo potrebnim. Vaše daljnje korištenje OAutha znači prihvaćanje tih izmjena.",
+ "mwoauth-consumer-email-unconfirmed": "Vaša adresa e-pošte još nije potvrđena.",
+ "mwoauth-consumer-email-mismatched": "Navedena e-pošta mora odgovarati onoj na Vašem računu.",
+ "mwoauth-consumer-alreadyexists": "Već postoji potrošač s takvom kombinacijom imena/verzije/izdavača",
+ "mwoauth-consumer-alreadyexistsversion": "Već postoji potrošač s ovom kombinacijom imena/izdavača s jednakom ili višom verzijom (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Ne mogu izmijeniti informacije o potrošačkom zahtjevu na čekanju",
+ "mwoauth-consumer-not-proposed": "Potrošač trenutačno nije predložen",
+ "mwoauth-consumer-not-disabled": "Potrošač trenutačno nije onemogućen",
+ "mwoauth-consumer-not-approved": "Potrošač nije odobren (možda je onemogućen)",
+ "mwoauth-missing-consumer-key": "Nije naveden potrošački ključ.",
+ "mwoauth-invalid-consumer-key": "Ne postoji potrošač s takvim ključem.",
+ "mwoauth-invalid-access-token": "Nema pristupnog koda s takvim ključem.",
+ "mwoauth-invalid-access-wrongwiki": "Potrošač može biti korišten samo na projektu \"$1\".",
+ "mwoauth-consumer-conflict": "Za vrijeme pregledavanja netko je izmijenio atribute ovog potrošača. Pokušajte ponovno. Možete pogledati i dnevnik izmjena.",
+ "mwoauth-consumer-grantshelp": "Svaka dozvola daje pristup navedenim pravima koja su već dodijeljena korisničkom računu. Više ćete naći na [[Special:ListGrants|tablici dozvola]].",
+ "mwoauth-consumer-stage-proposed": "predložen",
+ "mwoauth-consumer-stage-rejected": "odbijen",
+ "mwoauth-consumer-stage-expired": "isteklo",
+ "mwoauth-consumer-stage-approved": "odobren",
+ "mwoauth-consumer-stage-disabled": "onemogućen",
+ "mwoauth-consumer-stage-suppressed": "potisnut",
+ "oauthconsumerregistration": "Registracija potrošača OAutha",
+ "mwoauthconsumerregistration-navigation": "Navigacija:",
+ "mwoauthconsumerregistration-propose": "Predloži novog potrošača",
+ "mwoauthconsumerregistration-list": "Popis mojih potrošača",
+ "mwoauthconsumerregistration-main": "Glavna",
+ "mwoauthconsumerregistration-propose-text": "Programeri bi trebali koristiti ovaj predložak za predlaganje novog potrošača OAutha (više ćete naći u [//www.mediawiki.org/wiki/Extension:OAuth dokumentaciji za dodatak]). Nakon što pošaljete obrazac, dobit ćete kod s kojim ćete biti predstavljeni na MediaWiki. Administrator usluge OAuth morat će odobriti vašu aplikaciju prije nego što mogu autorizirati korisnike.\n\n\nNekoliko preporuka i primjedbi:\n* Odobrava kao malo odobrenja. Izbjegavajte one koje trenutno nisu potrebne.\n* Verzije su \"major.minor.release\" (posljednja dva su opcionalna) i povećavaju se s potrebom za izmjenama u dozvolama.\n* Ako je moguće, unesite javni RSA ključ (u PEM formatu); inače (manje sigurni) dat ćemo vam tajni ključ.\n* Možete ograničiti potrošača na samo jedan projekt na ovom sajtu, unoseći naznaku projekta (\"*\" za sve projekte).",
+ "mwoauthconsumerregistration-update-text": "Obrazac ispod služi za mijenjanje aspekata potrošača OAutha koje kontrolirate.\n\nSve vrijednosti ovdje biti će prepisane preko postojećih. Ne ostavljajte prazna polja osim ako ih ne želite izbrisati.",
+ "mwoauthconsumerregistration-maintext": "Ova stranica omogućuje programerima da predlože i obnove (promijene) potrošačke izvršnike za OAuth (v. http://oauth.net) u registru ove web-lokacije.\n\nOdavde možete: [[Special:OAuthConsumerRegistration/propose|predložiti novog potrošača]] ili pak [[Special:OAuthConsumerRegistration/list|upravljati svojim postojećim potrošačima]].",
+ "mwoauthconsumerregistration-propose-legend": "Novi korisnički izvršnik za OAuth",
+ "mwoauthconsumerregistration-update-legend": "Izmjena korisničkog izvršnika za OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Predloži potrošača",
+ "mwoauthconsumerregistration-update-submit": "Izmjeni potrošača",
+ "mwoauthconsumerregistration-none": "Ne kontrolirate niti jednog OAuth potrošača.",
+ "mwoauthconsumerregistration-name": "Potrošač",
+ "mwoauthconsumerregistration-user": "Izdavač",
+ "mwoauthconsumerregistration-description": "Opis",
+ "mwoauthconsumerregistration-email": "E-pošta za kontakt",
+ "mwoauthconsumerregistration-consumerkey": "Potrošački ključ",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Posljednja izmjena",
+ "mwoauthconsumerregistration-manage": "upravljaj",
+ "mwoauthconsumerregistration-resetsecretkey": "Daj novu vrijednost tajnom ključu",
+ "mwoauthconsumerregistration-proposed": "Vaš je potrošački zahtjev za OAuth zaprimljen.\n\nVaš potrošački kod čita '''$1''', a tajni kod čita '''$2'''. ''Spremi ih jer će ti možda trebati u budućnosti.''",
+ "mwoauthconsumerregistration-created-owner-only": "Vaš je korisnik OAuth izrađen.\n\nVaši su kodovi:\n; Potrošački kod: $1\n; Potrošačka tajna: $2\n; Pristupni kod: $3\n; Pristupna tajna: $4\n<em>Spremite ih za buduće konzultacije.</em>",
+ "mwoauthconsumerregistration-updated": "Vaš potrošački registar OAutha je izmijenjen.",
+ "mwoauthconsumerregistration-secretreset": "Vaš tajni potrošački kod čita '''$1'''. ''Spremi ga jer će ti možda trebati u budućnosti.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Vaši OAuth potrošački kodovi su smijenjemi. Evo novih:\n\n; Potrošački kod: $1\n; Potrošačka tajna: $2\n; Pristupni kod: $3\n; Pristupna tajna: $4\n<em>Spremite ih za buduće konzultacije.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Morate potvtditi vašu adresu e-pošte prije stvaranja priloga s OAuthom.\nMolimo unesite i potvrdite vašu adresu e-pošte u [[Special:Preferences|korisničkim postavkama]].",
+ "oauthmanageconsumers": "Upravljanje potrošačima OAutha",
+ "mwoauthmanageconsumers-notloggedin": "Morate biti prijavljeni da biste otvorili stranicu.",
+ "mwoauthmanageconsumers-type": "Redovi:",
+ "mwoauthmanageconsumers-showproposed": "Predloženi zahtjevi",
+ "mwoauthmanageconsumers-showrejected": "Odbijeni zahtjevi",
+ "mwoauthmanageconsumers-showexpired": "Istekli zahtjevi",
+ "mwoauthmanageconsumers-linkproposed": "predloženi zahtjevi",
+ "mwoauthmanageconsumers-linkrejected": "odbijeni zahtjevi",
+ "mwoauthmanageconsumers-linkexpired": "istekli zahtjevi",
+ "mwoauthmanageconsumers-linkapproved": "odobreni zahtjevi",
+ "mwoauthmanageconsumers-linkdisabled": "onemogućeni zahtjevi",
+ "mwoauthmanageconsumers-main": "Glavna",
+ "mwoauthmanageconsumers-maintext": "Ova je stranica predviđena za rad sa zahtjevima korisničkih OAuth izvršnika (vidi http://oauth.net) i upravljanje postojećim potrošačima.",
+ "mwoauthmanageconsumers-queues": "Niže odaberite red potrošača za odobrenje:",
+ "mwoauthmanageconsumers-q-proposed": "Red zahtjeva predlaganja potrošača",
+ "mwoauthmanageconsumers-q-rejected": "Red odbijenih zahtjeva potrošača",
+ "mwoauthmanageconsumers-q-expired": "Red isteklih zahtjeva potrošača",
+ "mwoauthmanageconsumers-lists": "Ispod odaberite popis statusa potrošača:",
+ "mwoauthmanageconsumers-l-approved": "Popis trenutačno odobrenih potrošača",
+ "mwoauthmanageconsumers-l-disabled": "Popis trenutačno onemogućenih potrošača",
+ "mwoauthmanageconsumers-none-proposed": "Na popisu nema predloženih potrošača.",
+ "mwoauthmanageconsumers-none-rejected": "Na popisu nema predloženih potrošača.",
+ "mwoauthmanageconsumers-none-expired": "Na popisu nema predloženih potrošača.",
+ "mwoauthmanageconsumers-none-approved": "Nema potrošača što odgovaraju zadanim uvjetima.",
+ "mwoauthmanageconsumers-none-disabled": "Nema potrošača što odgovaraju zadanim uvjetima.",
+ "mwoauthmanageconsumers-name": "Potrošač",
+ "mwoauthmanageconsumers-user": "Izdavač",
+ "mwoauthmanageconsumers-description": "Opis",
+ "mwoauthmanageconsumers-email": "E-pošta za kontakt",
+ "mwoauthmanageconsumers-consumerkey": "Potrošački ključ",
+ "mwoauthmanageconsumers-lastchange": "Posljednja izmjena",
+ "mwoauthmanageconsumers-review": "provjera/upravljanje",
+ "mwoauthmanageconsumers-confirm-text": "Ovaj obrazac služi za odobravanje, odbijanje ili preomogućavanje korisnika.",
+ "mwoauthmanageconsumers-confirm-legend": "Upravljanje potrošačom OAutha",
+ "mwoauthmanageconsumers-action": "Status izmjene:",
+ "mwoauthmanageconsumers-approve": "Odobren",
+ "mwoauthmanageconsumers-reject": "Odbijen",
+ "mwoauthmanageconsumers-rsuppress": "Odbijen i potisnut",
+ "mwoauthmanageconsumers-disable": "Onemogućen",
+ "mwoauthmanageconsumers-dsuppress": "Onemogućen i potisnut",
+ "mwoauthmanageconsumers-reenable": "Odobreno",
+ "mwoauthmanageconsumers-reason": "Razlog:",
+ "mwoauthmanageconsumers-confirm-submit": "Izmjeni potr. status",
+ "mwoauthmanageconsumers-success-approved": "Zahtjev je odobren.",
+ "mwoauthmanageconsumers-success-rejected": "Zahtjev je odbijen.",
+ "mwoauthmanageconsumers-success-disabled": "Potrošač je onemogućen.",
+ "mwoauthmanageconsumers-success-reanable": "Potrošač je preomogućen.",
+ "mwoauthmanageconsumers-search-name": "potrošači s ovim imenom",
+ "mwoauthmanageconsumers-search-publisher": "potrošači ovog suradnika",
+ "oauthlistconsumers": "Popis izvršnika OAutha",
+ "mwoauthlistconsumers-legend": "Pregledaj izvršnike OAutha",
+ "mwoauthlistconsumers-view": "detalji",
+ "mwoauthlistconsumers-none": "Nema izvršnika što odgovaraju zadanim uvjetima.",
+ "mwoauthlistconsumers-name": "Naziv izvršnika",
+ "mwoauthlistconsumers-version": "Potrošačka verzija",
+ "mwoauthlistconsumers-user": "Izdavač",
+ "mwoauthlistconsumers-description": "Opis",
+ "mwoauthlistconsumers-wiki": "Primjenjivi projekt",
+ "mwoauthlistconsumers-callbackurl": "URL za pozivanje OAutha",
+ "mwoauthlistconsumers-callbackisprefix": "Omogući potrošaču povratni poziv u zahtjevima i iznad kako bi koristio URL za povratni poziv kao obavezni prefiks.",
+ "mwoauthlistconsumers-grants": "Primjenjive dozvole",
+ "mwoauthlistconsumers-basicgrantsonly": "(samo osnovni pristup)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "bilo koji",
+ "mwoauthlistconsumers-status-proposed": "predložen",
+ "mwoauthlistconsumers-status-approved": "odobren",
+ "mwoauthlistconsumers-status-disabled": "onemogućen",
+ "mwoauthlistconsumers-status-rejected": "odbijen",
+ "mwoauthlistconsumers-status-expired": "isteklo",
+ "oauthmanagemygrants": "Upravljanje povezanim izvršnicima",
+ "mwoauthmanagemygrants-text": "Ova stranica navodi izvršnike što mogu koristiti vaš račun. Njihov stupanj pristupa je ograničen tim što ste im dopustili da rade kada ih odobravaju. Ako ste izvršniku dali posebno odobrenje za pristup drugom zbratimljenom projektu, tada ispod će vam se pojaviti posebne postavke za svaki odeljen projekt.\n\nPovezani izvršnici koriste vaš račun putem protokola OAutha. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth?uselang=sh Saznajte više o povezanim izvršnicima])</span>",
+ "mwoauthmanagemygrants-showlist": "Spisak povezanih izvršnika",
+ "mwoauthmanagemygrants-none": "Nema izvršnika povezanih s vašim računom.",
+ "mwoauthmanagemygrants-user": "Izdavač:",
+ "mwoauthmanagemygrants-description": "Opis",
+ "mwoauthmanagemygrants-wikiallowed": "Dozvoljen na:",
+ "mwoauthmanagemygrants-grants": "Primjenjive dozvole",
+ "mwoauthmanagemygrants-grantsallowed": "Dopuštene dozvole",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Dopuštene primjenjive dozvole:",
+ "mwoauthmanagemygrants-review": "upravl. pristupom",
+ "mwoauthmanagemygrants-revoke": "oduzmi pristup",
+ "mwoauthmanagemygrants-grantaccept": "Odobreno",
+ "mwoauthmanagemygrants-update-text": "S ovim obrazcom možete izmijeniti dopuštenja odobrena nekom izvršniku da djeluje u vaše ime.",
+ "mwoauthmanagemygrants-revoke-text": "S ovim obrazcom možete opozvati pristup nekom izvršniku da djeluje u vaše ime.",
+ "mwoauthmanagemygrants-confirm-legend": "Upravljanje povezanim izvršnikom",
+ "mwoauthmanagemygrants-update": "Izmijeni dozvole",
+ "mwoauthmanagemygrants-renounce": "Oduzmi dozvolu",
+ "mwoauthmanagemygrants-action": "Smijeni status:",
+ "mwoauthmanagemygrants-confirm-submit": "Izmijeni status pristup. šifre",
+ "mwoauthmanagemygrants-success-update": "Vaša podešavanja ovog priloga su izmijenjena.",
+ "mwoauthmanagemygrants-success-renounce": "Pristup priloga vašem računu je opozvan.",
+ "mwoauthmanagemygrants-basic-tooltip": "Zašto ne mogu ažurirati ovo odobrenje? Ovo odobrenje dava povezanome izvršniku potrebna dopuštenja da djeluje kao što treba. Ako ne želite imati ta prava, treba oduzeti jemu pristup.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Zašto ne mogu ažurirati odobrenjevo? Ukoliko ne želite da ovaj vezani izvršnik ima ovo pravo, trebali biste povući njegov pristup.",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|predložio|predložila}} je potrošač OAutha (potrošački ključ $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|izmijenio|izmijenila}} je potrošač OAutha (potrošački ključ $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|odobrio|odobrila}} je potrošač OAutha s $3 (potrošački ključ $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|odbacio|odbacila}} je potrošač OAutha s $3 (potrošački ključ $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|onemogućio|onemogućila}} je potrošač OAutha s $3 (potrošački ključ $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|preomogućio|preomogućila}} je potrošač OAutha s $3 (potrošački ključ $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|napravio|napravila}} je OAuth-potršač namijenjen samo vlasniku (potrošački ključ $4)",
+ "mwoauthconsumer-consumer-logpagetext": "Evidencija odobravanja, odbacivanja i onemogućavanja registriranih potrošača OAutha.",
+ "mwoauth-bad-request-missing-params": "Nažalost, došlo je do problema prilikom postavljivanja izvršnika. Obratite se <span class=\"plainlinks\"> [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth mjestu za podršku]</span> kako biste saznali kako je riješiti.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuthu nedostaju parametre, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Nažalost, došlo je do problema prilikom postavljivanja izvršnik. Obratite se njegovom autoru kako biste je riješiti.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nepoznata URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Nažalost, došlo je do nekakog problema. Morat ćete [$1 obratiti se] autoru izvršnika da ga riješi.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nepoznata URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Nisam pronašao odobrenu dozvolu tim povlaštenim tokenom",
+ "mwoauthdatastore-request-token-not-found": "Nažalost, došlo je do problema prilikom povezivanja izvršnika.\nVratite se i pokušajte ponovo, ili kontaktirajte njegovog autora.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nisam našao OAuth-token, $1</span>",
+ "mwoauthdatastore-callback-not-found": "Nisam pronašao URL odgovora OAutha u međuspremniku. Ovo je vjerojatno greška u zahtjevima izvršnika prema poslužitelju.",
+ "mwoauthdatastore-request-token-already-used": "Zahtjev je već izvršen i ne može se preunesti.\nVratite se izvršniku i pokušajte ponovo povezati se s vašim računom, ili se obratite tvoritelju izvršnika.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-token je već u upotrebi, $1</span>",
+ "mwoauthdatastore-bad-token": "Nisam pronašao zahtjev što odgovara traženom",
+ "mwoauthdatastore-bad-source-ip": "Zahtjev je došao s nevažeće IP adrese.",
+ "mwoauthdatastore-bad-verifier": "Navedeni potvrdni kôd nije valjan",
+ "mwoauthdatastore-invalid-token-type": "Zatraženi tip tokena nije valjan.",
+ "mwoauthgrants-general-error": "Došlo je do greške u zahtjevu za OAuth",
+ "mwoauthserver-bad-consumer": "\"$1\" nije odobren kao povezani izvršnik. Ako vam je potrebna pomoć, [$2 kontaktirajte] njegovog autora.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Neodobren povezan izvršnik OAutha, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Nažalost, nešto nije u redu s privitkom.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nepoznati OAuth-ključ, $1</span>",
+ "mwoauthserver-insufficient-rights": "Vašem računu nije dopušteno koristiti Povezane izvršnike. Obratite se administratoru mrežnog mjesta kako biste saznali zašto.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Nedovoljna korisnička prava za OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Nevažeći token u zahtjevu.",
+ "mwoauthserver-invalid-user": "Da biste koristili povezane izvršnike na ovom sajtu, morate imati račun na svim projektima (objedinjen račun). Pokušajte ponovo spojiti \"$1\" kada napravite taki račun.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Potreban objedinjen račun, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Nažalost, nešto nije u redu s povezivanjem s izvršnikom.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Potrošač nema tajni ključ, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" je povezan privitak samo za vlasnka. Da biste dobili pristupni token, pogl. [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Potrošač je samo za vlasnika, $3</span>",
+ "mwoauth-invalid-authorization-title": "Greška s ovlašćenjem u OAuthu",
+ "mwoauth-invalid-authorization": "Ovlastiteljni zaglavlja u vašem zahtjevu su neispravni: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Ovlastiteljni zaglavlja u vašem zahtjevu su neispravni za $1",
+ "mwoauth-invalid-authorization-invalid-user": "Ovlastiteljni zaglavlja u vašem zahtjevu odnose se na korisnikota što ovdje ne postoji",
+ "mwoauth-invalid-authorization-wrong-user": "Ovlastiteljni zaglavlja u vašem zahtjevu odnose se na drugog korisnika",
+ "mwoauth-invalid-authorization-not-approved": "Izvršnik što želite povezati ispravno je postavljen. Za pomoć obratite se autoru »$1«.",
+ "mwoauth-invalid-authorization-blocked-user": "Ovlastiteljni zaglavlja u vašem zahtjevu odnose se na korisnika što je blokiran",
+ "mwoauth-form-description-allwikis": "Zdravo, $1,\n\nDa bi se mogao ispuniti Vaš zahtjev, '''$2''' potrebna je dozvola da djeluje u vaše ime na svim projektima na ovom sajtu:\n\n$4",
+ "mwoauth-form-description-onewiki": "Zdravo, $1,\n\nDa bi se mogao ispuniti Vaš zahtjev, '''$2''' potrebna je dozvola da djeluje u vaše ime na svim projektima na ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Zdravo, $1,\n\nDa bi se mogao ispuniti Vaš zahtjev, '''$2''' traži dopuštenje pristupu informacijama na svim projektima ovog mrežnog mjesta u Vaše ime. Na vašem računu neće biti izmjena.",
+ "mwoauth-form-description-onewiki-nogrants": "Zdravo, $1,\n\nDa bi se mogao ispuniti Vaš zahtjev, '''$2''' traži dopuštenje pristupu informacijama na \"$4\" u Vaše ime. Na vašem računu neće biti izmjena.",
+ "mwoauth-form-description-allwikis-privateinfo": "Zdravo, $1,\n\nDa biste dovršili zahtjev, '''$2''' traži dopuštenje pristupu Vašim informacijama, uključujući vaše pravo ime i adresu e-pošte, na svim projektima na mrežnovom mjestu. Nikakve izmjene povezane s Vašim računom neće se dogoditi.",
+ "mwoauth-form-description-onewiki-privateinfo": "Zdravo, $1,\n\nDa biste dovršili zahtjev, '''$2''' traži dopuštenje pristupu Vašim informacijama, uključujući vaše pravo ime i adresu e-pošte, na ''$4''. Nikakve izmjene povezane s Vašim računom neće se dogoditi.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Zdravo, $1,\n\nDa biste dovršili zahtjev, '''$2''' ima potrebu od dopuštenja za pristup Vašim informacijama, uključujući Vašu adresu e-pošte, na svim projektima na mrežnovom mjestu. Neće se dogoditi nikakve izmjene u Vašem računu.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Zdravo, $1,\n\nDa biste dovršili zahtjev, '''$2''' ima potrebu od dopuštenja za pristup Vašim informacijama, uključujući Vašu adresu e-pošte, na svim projektima na $4. Neće se dogoditi nikakve izmjene u Vašem računu.",
+ "mwoauth-form-button-approve": "Dozvoli",
+ "mwoauth-form-button-cancel": "Otkaži",
+ "mwoauth-error": "Pogreška u povezivanju s izvršnikom",
+ "mwoauth-grants-heading": "Tražene dozvole:",
+ "mwoauth-grants-nogrants": "Izvršnik nije zatražio nijedno dopuštenje.",
+ "mwoauth-acceptance-cancelled": "Odabrali ste da \"$1\" ne dozvolite pristup vašem računu. \"$1\" će raditi samo ako mu date dozvolu. Možete se vratiti na \"$1\" ili napraviti [[Special:OAuthManageMyGrants|podešavanja]] povezanih izvršnika.",
+ "mwoauth-granttype-normal": "Zahtijevajte ovlašćenje za pojedinačne dozvole.",
+ "mwoauth-oauth-exception": "Došlo je do greške u protokolu OAutha: $1",
+ "mwoauth-callback-not-oob": "oauth_callback mora biti postavljeno na \"oob\" (strogo u malim slovima)",
+ "mwoauth-callback-not-oob-or-prefix": "Mora biti zadano oauth_callback i biti namijesteno na \"oob\" (razlikuje velika/mala slova), ili postavljeni povratni poziv mora biti prefiksom postavljenog povratnog poziva.",
+ "right-mwoauthproposeconsumer": "Predlaganje novih potrošača OAutha",
+ "right-mwoauthupdateownconsumer": "Izmjena potrošača OAutha",
+ "right-mwoauthmanageconsumer": "Upravljanje potrošačima OAutha",
+ "right-mwoauthsuppress": "Sakrivanje potrošača OAutha",
+ "right-mwoauthviewsuppressed": "Pregled skrivenih potrošača OAutha",
+ "right-mwoauthviewprivate": "Pregled osobnih podataka za OAuth",
+ "right-mwoauthmanagemygrants": "Upravljanje dozvolama za OAuth",
+ "action-mwoauthmanageconsumer": "upravljanje potrošačima OAutha",
+ "action-mwoauthsuppress": "sakrivanje potrošača OAutha",
+ "action-mwoauthmanagemygrants": "upravljanje vlastitim dozvolama za OAuth",
+ "action-mwoauthproposeconsumer": "predlaganje novih potrošača OAutha",
+ "action-mwoauthupdateownconsumer": "izmjena potrošača OAutha",
+ "action-mwoauthviewprivate": "pregled osobnih podataka za OAuth",
+ "action-mwoauthviewsuppressed": "pregled skrivenih potrošača OAutha",
+ "mwoauth-tag-reserved": "Oznake što počinju s <code>OAuth CID:</code> rezervirane su za upotrebu od strane OAutha.",
+ "mwoauth-botpasswords-note": "<strong>Napomena:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> je sigurniji od botovskih lozinki i treba se koristiti kad god ga bot podržava.",
+ "mwoauth-api-module-disabled": "Modul \"$1\" nije dostupan s OAuthom.",
+ "echo-category-title-oauth-owner": "Razvoj OAutha"
+}
diff --git a/OAuth/i18n/si.json b/OAuth/i18n/si.json
new file mode 100644
index 00000000..9a40df36
--- /dev/null
+++ b/OAuth/i18n/si.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Indunil Chamara",
+ "Susith Chandira Gts"
+ ]
+ },
+ "mwoauthdatastore-bad-source-ip": "වලංගු නොවන IP address එකකින් ඉල්ලීම ලැබී ඇත"
+}
diff --git a/OAuth/i18n/sk.json b/OAuth/i18n/sk.json
new file mode 100644
index 00000000..97601fe8
--- /dev/null
+++ b/OAuth/i18n/sk.json
@@ -0,0 +1,84 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kusavica",
+ "Luky001",
+ "Robert Važan",
+ "Sudo77(new)",
+ "Teslaton"
+ ]
+ },
+ "mwoauth-desc": "Umožňuje použitie OAuth 1.0a a OAuth 2.0 pre povolenie prístupu k API",
+ "mwoauth-prefs-managegrants": "Pripojené aplikácie:",
+ "mwoauth-prefs-managegrantslink": "Spravovať {{PLURAL:$1|$1 pripojenú aplikáciu|$1 pripojené aplikácie|$1 pripojených aplikácií|0=pripojené aplikácie}}",
+ "mwoauth-consumer-allwikis": "Všetky projekty na tomto webe",
+ "mwoauth-consumer-name": "Názov aplikácie:",
+ "mwoauth-consumer-version": "Verzia konzumenta:",
+ "mwoauth-consumer-user": "Vydavateľ:",
+ "mwoauth-consumer-stage": "Aktuálny stav:",
+ "mwoauth-consumer-description": "Popis aplikácie:",
+ "mwoauth-consumer-callbackisprefix": "Umožniť konzumentovi, aby v požiadavkach uviedol callback a použil URL „callback“ vyššie ako povinný prefix.",
+ "mwoauth-consumer-grantsneeded": "Použiteľné oprávnenia:",
+ "mwoauth-consumer-wiki": "Použiteľný projekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Tento projekt ($1)",
+ "mwoauth-consumer-restrictions": "Obmedzenie použitia:",
+ "mwoauth-consumer-restrictions-json": "Obmedzenie použitia (JSON):",
+ "mwoauth-consumer-rsakey": "Verejný RSA kľúč (nepovinný):",
+ "mwoauth-consumer-reason": "Dôvod:",
+ "mwoauth-consumer-grantshelp": "Každé oprávnenie prideľuje prístup k uvedeným používateľským právam, ktoré príslušný používateľský účet už má. Viac informácií nájdete v [[Special:ListGrants|tabuľke oprávnení]].",
+ "mwoauth-consumer-stage-proposed": "navrhnuté",
+ "mwoauth-consumer-stage-rejected": "odmietnuté",
+ "mwoauth-consumer-stage-expired": "exspirované",
+ "mwoauth-consumer-stage-disabled": "zakázané",
+ "mwoauthconsumerregistration-navigation": "Navigácia:",
+ "mwoauthconsumerregistration-user": "Vydavateľ",
+ "mwoauthconsumerregistration-description": "Popis",
+ "mwoauthconsumerregistration-stage": "Stav",
+ "mwoauthmanageconsumers-user": "Vydavateľ",
+ "mwoauthmanageconsumers-description": "Popis",
+ "mwoauthmanageconsumers-action": "Zmeniť stav:",
+ "mwoauthmanageconsumers-approve": "Schválené",
+ "mwoauthmanageconsumers-reject": "Odmietnuté",
+ "mwoauthmanageconsumers-reenable": "Schválené",
+ "oauthlistconsumers": "Zoznam aplikácií OAuth",
+ "mwoauthlistconsumers-legend": "Prezerať aplikácie OAuth",
+ "mwoauthlistconsumers-none": "Nenájdená žiadna aplikácia zodpovedajúca týmto kritériám.",
+ "mwoauthlistconsumers-name": "Názov aplikácie",
+ "mwoauthlistconsumers-version": "Verzia konzumenta",
+ "mwoauthlistconsumers-user": "Vydavateľ",
+ "mwoauthlistconsumers-description": "Popis",
+ "mwoauthlistconsumers-wiki": "Použiteľný projekt",
+ "mwoauthlistconsumers-callbackurl": "URL pre OAuth „callback“",
+ "mwoauthlistconsumers-callbackisprefix": "Umožniť konzumentovi, aby v požiadavkach uviedol callback a použil URL „callback“ vyššie ako povinný prefix.",
+ "mwoauthlistconsumers-grants": "Použiteľné oprávnenia",
+ "mwoauthlistconsumers-status": "Stav",
+ "mwoauth-consumer-stage-any": "všetky",
+ "mwoauthlistconsumers-status-proposed": "navrhnuté",
+ "mwoauthlistconsumers-status-approved": "schválené",
+ "mwoauthlistconsumers-status-disabled": "zakázané",
+ "mwoauthlistconsumers-status-rejected": "odmietnuté",
+ "mwoauthlistconsumers-status-expired": "exspirované",
+ "oauthmanagemygrants": "Spravovanie pripojených aplikácií",
+ "mwoauthmanagemygrants-text": "Táto stránka obsahuje zoznam aplikácií, ktoré môžu využívať váš účet. Každá z aplikácii má rozsah prístupu obmedzený oprávneniami, ktoré ste aplikácii {{GENDER:|pridelil|pridelila|pridelili}} v okamihu, keď ste jej {{GENDER:|dovolil|dovolila|dovolili}} konať vo vašom mene. Ak ste aplikácii {{GENDER:|dovolil|dovolila|dovolili}} konať vo vašom mene nezávisle na rôznych sesterských projektoch, uvidíte nižšie oddelené konfigurácie pre každý taký projekt.\n\nPripojené aplikácie pristupujú k vášmu účtu pomocou protokolu OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth?uselang=sk Viac informácií o pripojených aplikáciách])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigácia:",
+ "mwoauthmanagemygrants-showlist": "Zoznam pripojených aplikácií",
+ "mwoauthmanagemygrants-user": "Vydavateľ:",
+ "mwoauthmanagemygrants-description": "Popis",
+ "mwoauthmanagemygrants-wikiallowed": "Povolené na projekte:",
+ "mwoauthmanagemygrants-grants": "Použiteľné oprávnenia",
+ "mwoauthmanagemygrants-review": "spravovať prístup",
+ "mwoauthmanagemygrants-revoke": "odobrať prístup",
+ "mwoauthmanagemygrants-update-text": "Pomocou nižšie zobrazeného formulára môžete zmeniť oprávnenie pridelené aplikácii, aby mohla konať vo vašom mene.",
+ "mwoauthmanagemygrants-revoke-text": "Pomocou nižšie zobrazeného formulára môžete odvolať oprávnenie aplikácii konať vo vašom mene.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Vaše}} úpravy vykonané touto aplikáciou",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Vaše}} činnosti vykonané touto aplikáciou",
+ "mwoauth-form-description-allwikis": "{{GENDER:$1|Užívateľ|Užívateľka}} $1,\n\npre dokončenie vašej požiadavky potrebuje '''$2''' povolenie vykonávať vo vašom mene nasledujúce úkony na všetkých projektoch tohoto webu:\n\n$4",
+ "mwoauth-form-description-onewiki": "{{GENDER:$1|Užívateľ|Užívateľka}} $1,\n\npre dokončenie vašej požiadavky potrebuje '''$2''' povolenie vykonávať vo vašom mene nasledujúce úkony na ''{{grammar:5sg|$4}}'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "{{GENDER:$1|Užívateľ|Užívateľka}} $1,\n\npre dokončenie vašej požiadavky potrebuje '''$2''' povolenie pristupovať vo vašom mene k informáciám na všetkých projektoch tohoto webu. Vo vašom mene nebudú vykonané žiadne zmeny.",
+ "mwoauth-form-description-onewiki-nogrants": "{{GENDER:$1|Užívateľ|Užívateľka}} $1,\n\npre dokončenie vašej požiadavky potrebuje '''$2''' povolenie pristupovať vo vašom mene k informáciám na ''{{grammar:5sg|$4}}''. Vo vašom mene nebudú vykonané žiadne zmeny.",
+ "mwoauth-form-description-allwikis-privateinfo": "{{GENDER:$1|Užívateľ|Užívateľka}} $1,\n\npre dokončenie vašej požiadavky potrebuje '''$2''' povolenie na prístup k informáciám o vás, vrátane vášho skutočného mena a emailovej adresy, na všetkých projektoch tohoto webu. Vo vašom mene nebudú vykonané žiadne zmeny.",
+ "mwoauth-form-description-onewiki-privateinfo": "{{GENDER:$1|Užívateľ|Užívateľka}} $1,\n\npre dokončenie vašej požiadavky potrebuje '''$2''' povolenie na prístup k informáciám o vás, vrátane vášho skutočného mena a emailovej adresy, na ''{{grammar:5sg|$4}}''. Vo vašom mene nebudú vykonané žiadne zmeny.",
+ "mwoauth-form-button-approve": "Povoliť",
+ "mwoauth-form-button-cancel": "Zrušiť",
+ "echo-pref-tooltip-oauth-owner": "Upozornite ma na udalosti týkajúce sa aplikácií OAuth, ktoré som {{GENDER:|vytvoril|vytvorila}}."
+}
diff --git a/OAuth/i18n/sl.json b/OAuth/i18n/sl.json
new file mode 100644
index 00000000..452726b7
--- /dev/null
+++ b/OAuth/i18n/sl.json
@@ -0,0 +1,37 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dbc334",
+ "Eleassar",
+ "Janezdrilc"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Priklopljene aplikacije:",
+ "mwoauth-prefs-managegrantslink": "Upravljaj {{PLURAL:$1|$1 povezano aplikacijo|$1 povezani aplikaciji|$1 povezane aplikacije|$1 povezanih aplikacij|0=povezane aplikacije}}",
+ "mwoauth-consumer-allwikis": "Vsi projekti na tem spletišču",
+ "mwoauth-consumer-user": "Avtor:",
+ "mwoauthconsumerregistration-navigation": "Navigacija:",
+ "mwoauthconsumerregistration-user": "Avtor",
+ "mwoauthmanageconsumers-user": "Avtor",
+ "mwoauthlistconsumers-user": "Avtor",
+ "oauthmanagemygrants": "Upravljaj priklopljene aplikacije",
+ "mwoauthmanagemygrants-text": "Na tem seznamu so navedene vse aplikacije, ki utegnejo uporabljati tvoj račun. Vsaka izmed njih deluje v okviru dovoljenj, ki si ji jih dodelil ob potrditvi delovanja. Če si delovanje potrdil ločeno za kak sorodni projekt, bodo prikazane ločene nastavitve za vsak projekt posebej.\n\nPovezane aplikacije dostopajo do tvojega računa s pomočjo protokola OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Več o povezanih aplikacijah])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigacija:",
+ "mwoauthmanagemygrants-showlist": "Seznam priklopljenih aplikacij",
+ "mwoauthmanagemygrants-user": "Avtor:",
+ "mwoauthmanagemygrants-wikiallowed": "Odobren na projektu:",
+ "mwoauthmanagemygrants-review": "upravljaj dostop",
+ "mwoauthmanagemygrants-revoke": "prekliči dostop",
+ "mwoauthmanagemygrants-grantaccept": "Donirano",
+ "mwoauthmanagemygrants-confirm-legend": "Upravljaj priklopljeno aplikacijo",
+ "mwoauthmanagemygrants-update": "Posodobitev donacij",
+ "mwoauthmanagemygrants-renounce": "Deavtoriziraj",
+ "mwoauth-form-description-allwikis": "Pozdravljeni, $1,\n\nza dokončanje prošnje potrebuje '''$2''' dovoljenje, da v vašem imenu v vseh projektih tega spletišča izvede naslednje dejanje:\n\n\n$4",
+ "mwoauth-form-description-onewiki": "Pozdravljeni, $1,\n\nza dokončanje prošnje potrebuje '''$2''' dovoljenje, da na projektu »$4« v vašem imenu izvede naslednje dejanje:\n\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Pozdravljeni, $1,\n\nza dokončanje prošnje potrebuje '''$2''' dovoljenje, da v vseh projektih tega spletišča dostopa do informacij v vašem imenu. Z vašim računom ne bo narejena nobena sprememba.",
+ "mwoauth-form-description-onewiki-nogrants": "Pozdravljeni, $1,\n\nza dokončanje prošnje potrebuje '''$2''' dovoljenje, da v projektu ''$4'' v vašem imenu dostopa do informacij. Z vašim računom ne bo narejena nobena sprememba.",
+ "mwoauth-form-button-approve": "Dovoli",
+ "mwoauth-form-button-cancel": "Prekliči",
+ "mwoauth-acceptance-cancelled": "Odločili ste se, da »$1« na dovolite dostopa do svojega računa. Oseba »$1« ne bo delovala, razen če ji dovolite dostop. Greste lahko nazaj na »$1« ali na[[Special:OAuthManageMyGrants|urejanje]] vaših povezanih aplikacij.",
+ "mwoauth-oauth-exception": "Napaka v protokolu OAuth: $1"
+}
diff --git a/OAuth/i18n/so.json b/OAuth/i18n/so.json
new file mode 100644
index 00000000..4182745b
--- /dev/null
+++ b/OAuth/i18n/so.json
@@ -0,0 +1,27 @@
+{
+ "@metadata": {
+ "authors": [
+ "Abshirdheere"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Adeegyada ku xidhan",
+ "mwoauth-prefs-managegrantslink": "Maamul $1 {{PLURAL:$1|adeega ku xiran|adeegyada ku xiran}}",
+ "mwoauth-consumer-allwikis": "Dhammaan mashaaliicda ka midka ah boggaan",
+ "mwoauth-consumer-name": "Magaca dhaqangalka:",
+ "mwoauth-consumer-user": "Faafinta",
+ "mwoauthconsumerregistration-navigation": "Gooshitaan",
+ "mwoauthconsumerregistration-user": "Faafinta",
+ "mwoauthmanageconsumers-user": "Faafinta",
+ "mwoauthlistconsumers-name": "Magaca dhaqangalka",
+ "mwoauthlistconsumers-user": "Faafinta",
+ "oauthmanagemygrants": "Maamul codsiyada xiriirka",
+ "mwoauthmanagemygrants-text": "Boggaan waxa ku taxan adeegsi kasta in aad u isticmaali karto akoonkaada. Adeegsi kasta oo noocaas ah, baaxadda ay u helaan waxaa xadiday rukhsadda lagu siiyey in ay codsiga markii la oggolaaday in ayagoo ku matalaya. Haddii aad si gooni ah u idman codsi ay u helaan mashaariicda kala duwan ee bahwadaagta adiga ku matalaya, markaa waxaad arki doontaa qaabeynta u gaar ah mashruuc kasta sida hoos ku qoran.\n\nAdeegsiga la xiriira si ay u helaan si ay iticmaalada adiga oo isticmaalaya hab maamuuska OAuth. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth sii ogoow waxyaaba dheeraad ah oo ku saabsan adeegsiga])</span>",
+ "mwoauthmanagemygrants-navigation": "Gooshitaan",
+ "mwoauthmanagemygrants-showlist": "Liiska codsiyada waa ku xiriirsan",
+ "mwoauthmanagemygrants-user": "Faafinta",
+ "mwoauthmanagemygrants-wikiallowed": "Ogolaashaha mashruuca:",
+ "mwoauthmanagemygrants-review": "Maamulka galista",
+ "mwoauthmanagemygrants-revoke": "Ka laabasho gaarista",
+ "mwoauthmanagemygrants-grantaccept": "Siismo",
+ "mwoauthmanagemygrants-update": "Cusbonaysii siismada"
+}
diff --git a/OAuth/i18n/sq.json b/OAuth/i18n/sq.json
new file mode 100644
index 00000000..a055525f
--- /dev/null
+++ b/OAuth/i18n/sq.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ammartivari",
+ "Kosovastar",
+ "Olsi"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplikacionet e lidhura:",
+ "mwoauth-prefs-managegrantslink": "Menaxho $1 {{PLURAL:$1|aplikacion të lidhur|aplikacione të lidhura}}",
+ "mwoauth-consumer-reason": "Arsyeja:",
+ "mwoauthmanageconsumers-reason": "Arsyeja:",
+ "oauthmanagemygrants": "Menaxho aplikacionet e lidhura"
+}
diff --git a/OAuth/i18n/sr-ec.json b/OAuth/i18n/sr-ec.json
new file mode 100644
index 00000000..02be0730
--- /dev/null
+++ b/OAuth/i18n/sr-ec.json
@@ -0,0 +1,117 @@
+{
+ "@metadata": {
+ "authors": [
+ "Acamicamacaraca",
+ "BadDog",
+ "Milicevic01",
+ "Obsuser",
+ "Zoranzoki21",
+ "Сербијана"
+ ]
+ },
+ "mwoauth-desc": "Дозвољава употребу OAuth 1.0a и OAuth 2.0 за АПИ ауторизацију",
+ "mwoauth-verified": "Апликацији је сада дозвољено да приступи Медијавикију у ваше име.\n\nДа бисте довршили процес, наведете ову верификациону вредност у апликацији: '''$1'''",
+ "mwoauth-db-readonly": "OAuth база података је привремено закључана. Покушајте поново за неколико минута.",
+ "mwoauth-missing-field": "Недостаје вредност за поље „$1”",
+ "mwoauth-field-hidden": "(ова информација је скривена)",
+ "mwoauth-field-private": "(ова информација је приватна)",
+ "mwoauth-prefs-managegrants": "Повезане апликације:",
+ "mwoauth-prefs-managegrantslink": "Управљајте {{PLURAL:$1|1=повезаном апликацијом|повезаним апликацијама ($1)|0=повезаним апликацијама}}",
+ "mwoauth-consumer-allwikis": "Сви пројекти на овом сајту",
+ "mwoauth-consumer-name": "Име апликације:",
+ "mwoauth-consumer-user": "Издавач:",
+ "mwoauth-consumer-stage": "Актуелни статус:",
+ "mwoauth-consumer-email": "Адреса е-поште контакта:",
+ "mwoauth-consumer-description": "Опис апликације:",
+ "mwoauth-consumer-wiki": "Применљиви пројекти:",
+ "mwoauth-consumer-wiki-thiswiki": "Тренутни пројекат ($1)",
+ "mwoauth-consumer-restrictions": "Ограничења употребе:",
+ "mwoauth-consumer-restrictions-json": "Ограничења употребе (JSON):",
+ "mwoauth-consumer-rsakey": "Јавни RSA кључ (опционално):",
+ "mwoauth-consumer-accesstoken": "Токен за приступ:",
+ "mwoauth-consumer-reason": "Разлог:",
+ "mwoauth-consumer-email-unconfirmed": "Е-адреса вашег налога још није потврђена.",
+ "mwoauth-consumer-email-mismatched": "Наведена е-адреса се мора подударати са оном на вашем налогу.",
+ "mwoauth-consumer-stage-proposed": "предложено",
+ "mwoauth-consumer-stage-rejected": "одбијено",
+ "mwoauth-consumer-stage-expired": "истекло",
+ "mwoauth-consumer-stage-approved": "одобрено",
+ "mwoauth-consumer-stage-disabled": "онемогућено",
+ "mwoauth-consumer-stage-suppressed": "сакривено",
+ "mwoauthconsumerregistration-navigation": "Навигација:",
+ "mwoauthconsumerregistration-user": "Издавач",
+ "mwoauthconsumerregistration-description": "Опис",
+ "mwoauthconsumerregistration-stage": "Статус",
+ "mwoauthconsumerregistration-lastchange": "Последња промена",
+ "mwoauthconsumerregistration-manage": "Управљај",
+ "mwoauthmanageconsumers-notloggedin": "Морате бити пријављени да бисте приступили овој страници.",
+ "mwoauthmanageconsumers-showrejected": "Одбијени захтеви",
+ "mwoauthmanageconsumers-showexpired": "Истекли захтеви",
+ "mwoauthmanageconsumers-user": "Издавач",
+ "mwoauthmanageconsumers-description": "Опис",
+ "mwoauthmanageconsumers-lastchange": "Последња промена",
+ "mwoauthmanageconsumers-action": "Промени статус:",
+ "mwoauthmanageconsumers-approve": "Одобрено",
+ "mwoauthmanageconsumers-reject": "Одбијено",
+ "mwoauthmanageconsumers-disable": "Онемогућено",
+ "mwoauthmanageconsumers-reenable": "Одобрено",
+ "mwoauthmanageconsumers-reason": "Разлог:",
+ "mwoauthmanageconsumers-success-approved": "Захтев је одобрен.",
+ "mwoauthmanageconsumers-success-rejected": "Захтев је одбијен.",
+ "oauthlistconsumers": "Списак OAuth апликација",
+ "mwoauthlistconsumers-legend": "Прегледај OAuth апликације",
+ "mwoauthlistconsumers-view": "детаљи",
+ "mwoauthlistconsumers-name": "Име апликације",
+ "mwoauthlistconsumers-user": "Издавач",
+ "mwoauthlistconsumers-description": "Опис",
+ "mwoauthlistconsumers-wiki": "Применљиви пројекат",
+ "mwoauthlistconsumers-basicgrantsonly": "(само основни приступ)",
+ "mwoauthlistconsumers-status": "Статус",
+ "mwoauth-consumer-stage-any": "било који",
+ "mwoauthlistconsumers-status-proposed": "предложено",
+ "mwoauthlistconsumers-status-approved": "одобрено",
+ "mwoauthlistconsumers-status-disabled": "онемогућено",
+ "mwoauthlistconsumers-status-rejected": "одбијено",
+ "mwoauthlistconsumers-status-expired": "истекло",
+ "oauthmanagemygrants": "Управљање повезаним апликацијама",
+ "mwoauthmanagemygrants-text": "Ова страница наводи све апликације које могу да користе ваш налог. За сваку такву апликацију, опсег њеног приступа ограничен је дозволама које сте доделили апликацији када сте је овластили да делује у ваше име. Ако сте засебно овластили апликацију да приступа другом сестринском пројекту у ваше име, онда ћете видети засебну конфигурацију за сваки такав пројекат испод.\n\nПовезане апликације приступају вашем налогу коришћењем OAuth протокола. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Сазнајте више о повезаним апликацијама])</span>",
+ "mwoauthmanagemygrants-navigation": "Навигација:",
+ "mwoauthmanagemygrants-showlist": "Списак повезаних аликација",
+ "mwoauthmanagemygrants-none": "Нема апликација повезаних са налогом.",
+ "mwoauthmanagemygrants-user": "Издавач:",
+ "mwoauthmanagemygrants-description": "Опис",
+ "mwoauthmanagemygrants-wikiallowed": "Дозвољено на пројекту:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Дозвољена следећа применљива додељења:",
+ "mwoauthmanagemygrants-review": "управљање приступом",
+ "mwoauthmanagemygrants-revoke": "опозивање приступа",
+ "mwoauthmanagemygrants-grantaccept": "Додељено",
+ "mwoauthmanagemygrants-update-text": "Користите образац испод да бисте изменили дозволе додељене апликацији да делује у ваше име.",
+ "mwoauthmanagemygrants-revoke-text": "Користите образац испод да бисте опозвали приступ апликацији да делује у ваше име.",
+ "mwoauthmanagemygrants-confirm-legend": "Управљање повезаним апликацијама",
+ "mwoauthmanagemygrants-update": "Ажурирај додељења",
+ "mwoauthmanagemygrants-renounce": "Скини овлашћења",
+ "mwoauthmanagemygrants-action": "Промени статус:",
+ "mwoauthmanagemygrants-basic-tooltip": "Зашто не могу да ажурирам ово додељење? Ово додељење даје повезаној апликацији основне дозволе које захтева да би исправно функционисала. Ако не желите да ова повезана апликација има ова права, треба да опозовете приступ апликације.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Ваше}} измене помоћу ове апликације",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Ваше}} радње помоћу ове апликације",
+ "log-action-filter-mwoauthconsumer": "Тип OAuth потрошачке радње:",
+ "log-action-filter-mwoauthconsumer-approve": "OAuth потрошачко одобравање",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "OAuth потрошачко прављење само за власника",
+ "log-action-filter-mwoauthconsumer-disable": "OAuth потрошачко онемогућавање",
+ "log-action-filter-mwoauthconsumer-propose": "OAuth потрошачки предлог",
+ "log-action-filter-mwoauthconsumer-reenable": "OAuth потрошачко поновно омогућавање",
+ "log-action-filter-mwoauthconsumer-reject": "OAuth потрошачко одбијање",
+ "log-action-filter-mwoauthconsumer-update": "OAuth потрошачко ажурирање",
+ "mwoauthconsumer-consumer-logpagetext": "Дневник одобравања, одбијања и онемогућавања регистрованих корисника OAuth-а.",
+ "mwoauthdatastore-request-token-already-used": "Овај захтев је већ завршен и не може се поново послати.\nВратите се на апликацију и покушајте поново да повежете свој налог или контактирајте аутора апликације.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth токен је већ искоришћен, $1</span>",
+ "mwoauth-form-description-allwikis-nogrants": "Здраво $1,\n\nУ циљу завршавања вашег захтева, апликацији '''$2''' је потребна дозвола за приступ информацијама на свим пројектима овог сајта у ваше име. Никакве промене неће бити направљене са вашим налогом.",
+ "mwoauth-form-button-approve": "Дозволи",
+ "mwoauth-form-button-cancel": "Откажи",
+ "mwoauth-grants-heading": "Тражене дозволе:",
+ "mwoauth-acceptance-cancelled": "Одлучили сте да не дозволите да \"$1\" приступи вашем налогу. \"$1\" неће радити ако му не дозволите приступ. Можете се вратити на \"$1\" или [[Special:OAuthManageMyGrants|управљати]] вашим повезаним апликацијама.",
+ "action-mwoauthmanageconsumer": "управљате потрошачима OAuth-а",
+ "echo-category-title-oauth-owner": "Развој OAuth-а",
+ "echo-category-title-oauth-admin": "Администратор OAuth-а",
+ "notification-oauth-app-body": "Разлог: $1",
+ "mwoauth-login-required-reason": "Морате се пријавити на налог на пројекту{{SITENAME}} да бисте овластили апликације да му приступе."
+}
diff --git a/OAuth/i18n/sr-el.json b/OAuth/i18n/sr-el.json
new file mode 100644
index 00000000..28f28a1e
--- /dev/null
+++ b/OAuth/i18n/sr-el.json
@@ -0,0 +1,78 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01",
+ "Obsuser",
+ "Srdjan m",
+ "Zoranzoki21"
+ ]
+ },
+ "mwoauth-field-hidden": "(ova informacija je skrivena)",
+ "mwoauth-field-private": "(ova informacija je privatna)",
+ "mwoauth-prefs-managegrants": "Povezane aplikacije:",
+ "mwoauth-prefs-managegrantslink": "Upravljaj prikačenim aplikacijama ({{PLURAL:$1|$1}})",
+ "mwoauth-consumer-allwikis": "Svim projektima na ovom sajtu",
+ "mwoauth-consumer-name": "Ime aplikacije:",
+ "mwoauth-consumer-user": "Izdavač:",
+ "mwoauth-consumer-stage": "Trenutni status:",
+ "mwoauth-consumer-email": "Imejl adresa za kontakt:",
+ "mwoauth-consumer-description": "Opis aplikacije:",
+ "mwoauth-consumer-wiki": "Primenjljivi projekti:",
+ "mwoauth-consumer-rsakey": "Javni RSA ključ:",
+ "mwoauth-consumer-reason": "Razlog:",
+ "mwoauth-consumer-stage-rejected": "odbijeno",
+ "mwoauth-consumer-stage-expired": "isteklo",
+ "mwoauth-consumer-stage-approved": "odobreno",
+ "mwoauth-consumer-stage-disabled": "onemogućeno",
+ "mwoauthconsumerregistration-navigation": "Navigacija:",
+ "mwoauthconsumerregistration-user": "Izdavač",
+ "mwoauthconsumerregistration-description": "Opis",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Poslednja izmena",
+ "mwoauthmanageconsumers-notloggedin": "Morate se prijaviti da bi pristupili ovoj stranici.",
+ "mwoauthmanageconsumers-showrejected": "Odbačeni zahtevi",
+ "mwoauthmanageconsumers-showexpired": "Istekli zahtevi",
+ "mwoauthmanageconsumers-user": "Izdavač",
+ "mwoauthmanageconsumers-description": "Opis",
+ "mwoauthmanageconsumers-lastchange": "Poslednja izmena",
+ "mwoauthmanageconsumers-action": "Izmeni status:",
+ "mwoauthmanageconsumers-approve": "Odobreno",
+ "mwoauthmanageconsumers-reject": "Odbijeno",
+ "mwoauthmanageconsumers-disable": "Onemogućeno",
+ "mwoauthmanageconsumers-reenable": "Odobreno",
+ "mwoauthmanageconsumers-reason": "Razlog:",
+ "mwoauthmanageconsumers-success-approved": "Zahtev je odobren.",
+ "mwoauthmanageconsumers-success-rejected": "Zahtev je odbijen.",
+ "oauthlistconsumers": "Spisak OAuth aplikacija",
+ "mwoauthlistconsumers-legend": "Pregledaj OAuth aplikacije",
+ "mwoauthlistconsumers-view": "detalji",
+ "mwoauthlistconsumers-name": "Ime aplikacije",
+ "mwoauthlistconsumers-user": "Izdavač",
+ "mwoauthlistconsumers-description": "Opis",
+ "mwoauthlistconsumers-wiki": "Dozvoljeno na",
+ "mwoauthlistconsumers-basicgrantsonly": "(samo osnovni pristup)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "bilo koji",
+ "mwoauthlistconsumers-status-approved": "odobreno",
+ "mwoauthlistconsumers-status-disabled": "onemogućeno",
+ "mwoauthlistconsumers-status-rejected": "odbijeno",
+ "mwoauthlistconsumers-status-expired": "isteklo",
+ "oauthmanagemygrants": "Upravljanje prikačenim aplikacijama",
+ "mwoauthmanagemygrants-navigation": "Navigacija:",
+ "mwoauthmanagemygrants-showlist": "Spisak povezanih alikacija",
+ "mwoauthmanagemygrants-none": "Nema aplikacija povezanih sa vašim nalogom.",
+ "mwoauthmanagemygrants-user": "Izdavač:",
+ "mwoauthmanagemygrants-description": "Opis",
+ "mwoauthmanagemygrants-wikiallowed": "Dozvoljeno na:",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Dozvoljena sledeća prava:",
+ "mwoauthmanagemygrants-grantaccept": "Dozvole",
+ "mwoauthmanagemygrants-confirm-legend": "Upravljanje prikačenim aplikacijama",
+ "mwoauthmanagemygrants-update": "Primeni",
+ "mwoauthmanagemygrants-renounce": "Skini ovlašćenja",
+ "mwoauthmanagemygrants-action": "Izmeni status:",
+ "mwoauthconsumer-consumer-logpagetext": "Dnevnik odobravanja, odbijanja i onemogućavanja registrovanih korisnika OAuth-a.",
+ "mwoauth-form-description-allwikis-nogrants": "Zdravo $1,\n\n'''$2''' želi da dobije osnovni pristup u vaše ime na svim projektima ovog sajta.",
+ "mwoauth-form-button-approve": "Dozvoli",
+ "mwoauth-form-button-cancel": "Otkaži",
+ "mwoauth-grants-heading": "Tražene dozvole:"
+}
diff --git a/OAuth/i18n/su.json b/OAuth/i18n/su.json
new file mode 100644
index 00000000..d6328e26
--- /dev/null
+++ b/OAuth/i18n/su.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Uchup19"
+ ]
+ },
+ "mwoauth-form-button-cancel": "Bolay"
+}
diff --git a/OAuth/i18n/sv.json b/OAuth/i18n/sv.json
new file mode 100644
index 00000000..581ebd7a
--- /dev/null
+++ b/OAuth/i18n/sv.json
@@ -0,0 +1,299 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ainali",
+ "Eihpossophie",
+ "Jkl",
+ "Jopparn",
+ "Lokal Profil",
+ "Mjälten",
+ "Skalman",
+ "WikiPhoenix"
+ ]
+ },
+ "mwoauth-desc": "Tillåter användning av OAuth 1.0a för API-tillstånd",
+ "mwoauth-verified": "Denna applikation kan nu komma åt MediaWiki för din räkning.\n\nFör att slutföra processen, ge detta verifikationsvärde till applikationen: '''$1'''",
+ "mwoauth-db-readonly": "OAuth-databasen är tillfälligt låst. Vänligen försök igen om några minuter.",
+ "mwoauth-missing-field": "Saknar värde för \"$1\"-fältet",
+ "mwoauth-invalid-field": "Ogiltigt värde angett för \"$1\"-fältet",
+ "mwoauth-invalid-field-generic": "Ogiltigt värde angivet",
+ "mwoauth-field-hidden": "(denna information är dold)",
+ "mwoauth-field-private": "(denna information är privata)",
+ "mwoauth-prefs-managegrants": "Anslutna appar:",
+ "mwoauth-prefs-managegrantslink": "Hantera {{PLURAL:$1|$1 ansluten applikation|$1 anslutna applikationer}}",
+ "mwoauth-consumer-allwikis": "Alla projekt på denna webbplats",
+ "mwoauth-consumer-key": "Konsumentnyckel:",
+ "mwoauth-consumer-name": "Applikationsnamn:",
+ "mwoauth-consumer-version": "Konsumentversion:",
+ "mwoauth-consumer-user": "Utgivare:",
+ "mwoauth-consumer-stage": "Aktuell status:",
+ "mwoauth-consumer-email": "E-postadress:",
+ "mwoauth-consumer-email-help": "Endast synlig för de som godkänner nya konsumenter",
+ "mwoauth-consumer-owner-only-label": "Endast ägaren:",
+ "mwoauth-consumer-owner-only": "Denna konsument används endast av $1.",
+ "mwoauth-consumer-owner-only-help": "Genom att välja detta alternativ kommer konsumenten automatiskt att godkännas och accepteras för användning av $1. Den kommer inte att kunna användas av någon annan användare, och det vanliga autentiseringsflödet kommer inte att fungera. Åtgärder som utgörs med denna konsument kommer inte att taggas.",
+ "mwoauth-consumer-description": "Beskrivning av applikationen",
+ "mwoauth-consumer-callbackurl": "OAuth \"callback\"-URL:",
+ "mwoauth-consumer-callbackisprefix": "Gör det möjligt för konsumenter att ange en callback i förfrågningar och använda \"callback\"-URL:en ovan som ett krävt prefix.",
+ "mwoauth-consumer-granttypes": "Behörighetstyper som efterfrågas:",
+ "mwoauth-consumer-grantsneeded": "Tillämpliga behörigheter:",
+ "mwoauth-consumer-required-grant": "Tillämplig för konsumenten",
+ "mwoauth-consumer-wiki": "Begränsa till projekt:",
+ "mwoauth-consumer-wiki-thiswiki": "Aktuellt projekt ($1)",
+ "mwoauth-consumer-restrictions": "Användningsbegränsningar:",
+ "mwoauth-consumer-restrictions-json": "Användningsbegränsningar (JSON):",
+ "mwoauth-consumer-rsakey": "Offentlig RSA-nyckel (valfritt):",
+ "mwoauth-consumer-rsakey-help": "Fyll i en publik nyckel för att använda signaturmetoden RSA-SHA1. Lämna tomt för att använda HMAC-SHA1 med en slumpmässig hemlighet. Om du är osäker, lämna fältet tomt.",
+ "mwoauth-consumer-secretkey": "Konsumentens hemliga nyckel:",
+ "mwoauth-consumer-accesstoken": "Åtkomstnyckel:",
+ "mwoauth-consumer-reason": "Orsak:",
+ "mwoauth-consumer-developer-agreement": "Genom att skicka denna programvara, bekräftar du att du förstår att vi förbehåller oss rätten att inaktivera din programvara, ta bort eller begränsa dig eller din programvaras tillgång till denna webbplats, och vidta övriga åtgärder som vi bedömer nödvändiga, om vi tror, enligt vår egen bedömning, att du eller din programvara bryter mot någon policy, riktlinje eller vägledande princip för denna webbplats. Vi kan ändra denna programvarupolicy när som helst utan förvarning, efter vårt eget gottfinnande och när vi bedömer det nödvändigt. Din fortsatta användning av OAuth utgör ett godkännande av de ändringarna.",
+ "mwoauth-consumer-email-unconfirmed": "Din e-postadress till kontot har ännu inte bekräftats.",
+ "mwoauth-consumer-email-mismatched": "Den angivna e-postadressen måste matcha den som är kopplad till ditt konto.",
+ "mwoauth-consumer-alreadyexists": "En konsument med denna kombination av namn/version/utgivare finns redan",
+ "mwoauth-consumer-alreadyexistsversion": "En konsument med denna kombination av namn/utgivare finns redan med en likvärdig eller högre version (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Kan inte uppdatera information för en väntande konsumentbegäran",
+ "mwoauth-consumer-not-proposed": "Konsumenten föreslås inte för närvarande",
+ "mwoauth-consumer-not-disabled": "Konsumenten är inte inaktiverad för närvarande",
+ "mwoauth-consumer-not-approved": "Konsumenten inte är godkänd (den kan ha inaktiverats)",
+ "mwoauth-missing-consumer-key": "Ingen konsumentnyckel angavs.",
+ "mwoauth-invalid-consumer-key": "Ingen konsument finns med den nyckel.",
+ "mwoauth-invalid-access-token": "Ingen åtkomst-nyckel finns med den nyckeln.",
+ "mwoauth-invalid-access-wrongwiki": "Konsumenten kan endast användas på projektet \"$1\".",
+ "mwoauth-consumer-conflict": "Någon ändrat attributen för denna konsument när du tittade på den. Vänligen försök igen. Du kanske vill ta en titt på ändringsloggen.",
+ "mwoauth-consumer-grantshelp": "Varje beviljande ger tillgång till listade användarbehörigheter som det användarkontot redan har. Se [[Special:ListGrants|tabell över behörigheter]] för mer information.",
+ "mwoauth-consumer-stage-proposed": "föreslagna",
+ "mwoauth-consumer-stage-rejected": "avvisade",
+ "mwoauth-consumer-stage-expired": "utgångna",
+ "mwoauth-consumer-stage-approved": "godkända",
+ "mwoauth-consumer-stage-disabled": "inaktiverade",
+ "mwoauth-consumer-stage-suppressed": "undertryckta",
+ "oauthconsumerregistration": "Registrering för OAuth-konsumenter",
+ "mwoauthconsumerregistration-navigation": "Navigering:",
+ "mwoauthconsumerregistration-propose": "Föreslå ny kund",
+ "mwoauthconsumerregistration-list": "Min konsumentlista",
+ "mwoauthconsumerregistration-main": "Huvudsida",
+ "mwoauthconsumerregistration-propose-text": "Utvecklare bör använda formuläret nedan för att föreslå en ny OAuth-konsument (se [//www.mediawiki.org/wiki/Extension:OAuth dokumentationen för tillägget] för mer detaljer). Efter att ha skickat in detta formulär, får du en nyckel som din applikation kan använda för att identifiera sig gentemot MediaWiki. En OAuth-administratör måste godkänna din ansökan innan den kan tillåtas av andra användare.\n\nNågra rekommendationer och anmärkningar:\n* Försök att använda så få behörigheter som möjligt. Undvik behörigheter som egentligen inte behövs nu.\n* Versioner är av formen \"huvudversion.underversion.release\" (de två sista är valfria) och ökar allt eftersom behörighetsförändringar behövs.\n* Vänligen ange en publik RSA-nyckel (i PEM-format) om möjligt; annars måste en (mindre säker) hemlig nyckel användas.\n* Du kan använda ett projekt-id för att begränsa konsumenten till ett enskilt projekt på denna webbplats (använd \"*\" för alla projekt).",
+ "mwoauthconsumerregistration-update-text": "Använd formuläret nedan för att uppdatera delar av en OAuth-konsument du styr.\n\nAlla värden här skriver över eventuella tidigare värden. Lämna inte tomma fält om du inte tänker ta bort dessa värden.",
+ "mwoauthconsumerregistration-maintext": "Denna sida är till för att låta utvecklare föreslå och uppdatera OAuth-konsumentapplikationer i webbplatsens register.\n\nHärifrån kan du:\n* [[Special:OAuthConsumerRegistration/propose|Begära en nyckel för en ny konsument]].\n* [[Special:OAuthConsumerRegistration/list|Hantera dina existerande konsumenter]].\n\nFör mer information om OAuth, se [//www.mediawiki.org/wiki/Extension:OAuth dokumentationen för tillägget].",
+ "mwoauthconsumerregistration-propose-legend": "Ny OAuth-konsumentapplikation",
+ "mwoauthconsumerregistration-update-legend": "Uppdatera OAuth-konsumentapplikation",
+ "mwoauthconsumerregistration-propose-submit": "Föreslå konsument",
+ "mwoauthconsumerregistration-update-submit": "Uppdatera konsument",
+ "mwoauthconsumerregistration-none": "Du kontrollerar inte några OAuth-konsumenter.",
+ "mwoauthconsumerregistration-name": "Konsument",
+ "mwoauthconsumerregistration-user": "Utgivare",
+ "mwoauthconsumerregistration-description": "Beskrivning",
+ "mwoauthconsumerregistration-email": "Kontakte-post",
+ "mwoauthconsumerregistration-consumerkey": "Konsumentnyckel",
+ "mwoauthconsumerregistration-stage": "Status",
+ "mwoauthconsumerregistration-lastchange": "Senaste ändringen",
+ "mwoauthconsumerregistration-manage": "hantera",
+ "mwoauthconsumerregistration-resetsecretkey": "Återställ den hemliga nyckeln till ett nytt värde",
+ "mwoauthconsumerregistration-proposed": "Du har tilldelats konsument-nyckeln av '''$1''' och en hemlig nyckel av '''$2'''. ''Vänligen anteckna dessa för framtida bruk.''",
+ "mwoauthconsumerregistration-created-owner-only": "Din OAuth-konsument har skapats.\n\nDina nycklar är:\n; Konsumentnyckel: $1\n; Konsumenthemlighet: $2\n; Accessnyckel: $3\n; Accesshemlighet: $4\n<em>Vänligen anteckna dessa för framtida användning.</em>",
+ "mwoauthconsumerregistration-updated": "Ditt OAuth-konsumentregister uppdaterades.",
+ "mwoauthconsumerregistration-secretreset": "Du har blivit tilldelad en konsumenttoken av '''$1'''. ''Vänligen anteckna detta för framtida bruk.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Dina konsumentnycklar för OAuth har återställts. De nya nycklarna är:\n; Konsumentnyckel: $1\n; Konsumenthemlighet: $2\n; Accessnyckel: $3\n; Accesshemlighet: $4\n<em>Vänligen anteckna dessa för framtida användning.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Du måste bekräfta din e-postadress innan du kan skapa OAuth-applikationer. Var vänlig ställ in och validera din e-postadress genom dina [[Special:Preferences|användarinställningar]].",
+ "oauthmanageconsumers": "Hantera OAuth-konsumenter",
+ "mwoauthmanageconsumers-notloggedin": "Du behöver vara inloggad för att komma åt denna sida.",
+ "mwoauthmanageconsumers-type": "Köer:",
+ "mwoauthmanageconsumers-showproposed": "Föreslagna ansökningar",
+ "mwoauthmanageconsumers-showrejected": "Avslagna ansökningar",
+ "mwoauthmanageconsumers-showexpired": "Utgångna ansökningar",
+ "mwoauthmanageconsumers-linkproposed": "Föreslagna ansökningar",
+ "mwoauthmanageconsumers-linkrejected": "avvisade förfrågningar",
+ "mwoauthmanageconsumers-linkexpired": "utgångna förfrågningar",
+ "mwoauthmanageconsumers-linkapproved": "godkända förfrågningar",
+ "mwoauthmanageconsumers-linkdisabled": "inaktiverade förfrågningar",
+ "mwoauthmanageconsumers-main": "Huvudsida",
+ "mwoauthmanageconsumers-maintext": "Denna sida är ämnad för hanteringen av OAuths (se http://oauth.net) konsumentapplikationsförfrågningar samt hanteringen av etablerade OAuth konsumenter.",
+ "mwoauthmanageconsumers-queues": "Välj konsumentbekräftelsekö nedan:",
+ "mwoauthmanageconsumers-q-proposed": "Kö med föreslagna konsumentförfrågningar",
+ "mwoauthmanageconsumers-q-rejected": "Kö med avslagna konsumentförfrågningar",
+ "mwoauthmanageconsumers-q-expired": "Kö med utgånga konsumentförfrågningar",
+ "mwoauthmanageconsumers-lists": "Välj en konsumentstatuslista nedan:",
+ "mwoauthmanageconsumers-l-approved": "Lista över nyligen godkända konsumenter",
+ "mwoauthmanageconsumers-l-disabled": "Lista över nyligen avaktiverade konsumenter",
+ "mwoauthmanageconsumers-none-proposed": "Inga föreslagna konsumenter i denna lista.",
+ "mwoauthmanageconsumers-none-rejected": "Inga föreslagna konsumenter i denna lista.",
+ "mwoauthmanageconsumers-none-expired": "Inga föreslagna konsumenter i denna lista.",
+ "mwoauthmanageconsumers-none-approved": "Inga konsumenter möter detta kriterium.",
+ "mwoauthmanageconsumers-none-disabled": "Inga konsumenter möter detta kriterium.",
+ "mwoauthmanageconsumers-name": "Konsument",
+ "mwoauthmanageconsumers-user": "Utgivare",
+ "mwoauthmanageconsumers-description": "Beskrivning",
+ "mwoauthmanageconsumers-email": "E-post kontakt",
+ "mwoauthmanageconsumers-consumerkey": "Konsumentnyckel",
+ "mwoauthmanageconsumers-lastchange": "Senaste ändringen",
+ "mwoauthmanageconsumers-review": "granska/hantera",
+ "mwoauthmanageconsumers-confirm-text": "Använd detta formulär för att godkänna, avvisa, inaktivera eller återaktivera denna konsument.",
+ "mwoauthmanageconsumers-confirm-legend": "Hantera OAuthkonsument",
+ "mwoauthmanageconsumers-action": "Ändra status:",
+ "mwoauthmanageconsumers-approve": "Godkänd",
+ "mwoauthmanageconsumers-reject": "Avvisad",
+ "mwoauthmanageconsumers-rsuppress": "Avvisade och undertryckta",
+ "mwoauthmanageconsumers-disable": "Inaktiverad",
+ "mwoauthmanageconsumers-dsuppress": "Inaktiverade och undertryckta",
+ "mwoauthmanageconsumers-reenable": "Godkänd",
+ "mwoauthmanageconsumers-reason": "Orsak:",
+ "mwoauthmanageconsumers-confirm-submit": "Uppdatera konsumentstatus",
+ "mwoauthmanageconsumers-success-approved": "Begäran har godkänts.",
+ "mwoauthmanageconsumers-success-rejected": "Begäran har avslagits.",
+ "mwoauthmanageconsumers-success-disabled": "Konsumenten har inaktiverats.",
+ "mwoauthmanageconsumers-success-reanable": "Konsumenten har återaktiverats.",
+ "mwoauthmanageconsumers-search-name": "Konsument med detta namn",
+ "mwoauthmanageconsumers-search-publisher": "Konsumenter av denna användare",
+ "oauthlistconsumers": "Lista OAuth-applikationer",
+ "mwoauthlistconsumers-legend": "Sök efter OAuth-applikationer",
+ "mwoauthlistconsumers-view": "detaljer",
+ "mwoauthlistconsumers-none": "Inga applikationer hittades som uppfyller detta kriterium.",
+ "mwoauthlistconsumers-name": "Applikationsnamn",
+ "mwoauthlistconsumers-version": "Konsumentversion",
+ "mwoauthlistconsumers-user": "Utgivare",
+ "mwoauthlistconsumers-description": "Beskrivning",
+ "mwoauthlistconsumers-wiki": "Begränsa till projekt",
+ "mwoauthlistconsumers-callbackurl": "OAuth \"callback URL\"",
+ "mwoauthlistconsumers-callbackisprefix": "Gör det möjligt för konsumenter att ange en callback i förfrågningar och använda \"callback\"-URL:en ovan som ett krävt prefix.",
+ "mwoauthlistconsumers-grants": "Tillämpliga behörigheter",
+ "mwoauthlistconsumers-basicgrantsonly": "(endast grundläggande tillgång)",
+ "mwoauthlistconsumers-status": "Status",
+ "mwoauth-consumer-stage-any": "alla",
+ "mwoauthlistconsumers-status-proposed": "föreslagna",
+ "mwoauthlistconsumers-status-approved": "godkända",
+ "mwoauthlistconsumers-status-disabled": "inaktiverade",
+ "mwoauthlistconsumers-status-rejected": "avvisad",
+ "mwoauthlistconsumers-status-expired": "utgången",
+ "mwoauthlistconsumers-rclink": "Senaste ändringarna från denna applikation",
+ "oauthmanagemygrants": "Hantera anslutna applikationer",
+ "mwoauthmanagemygrants-text": "Denna sida listar alla applikationer som kan använda ditt konto. För varje sådan applikation är dess tillträde begränsat av de behörigheter vilka du auktoriserade när du valde att låta den agera åt dina vägnar. Om du separat auktoriserar en applikation att tillgå olika systerprojekt åt dina vägnar kommer du se separat konfiguration för varje sådant projekt nedan.\n\nAnsluta applikationer kan få tillgång till ditt konto via OAuth-protokollet. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Läs mer om ansluta applikationer])</span>",
+ "mwoauthmanagemygrants-navigation": "Navigering:",
+ "mwoauthmanagemygrants-showlist": "Ansluten applikationslista",
+ "mwoauthmanagemygrants-none": "Det finns inga applikationer anslutna till ditt konto.",
+ "mwoauthmanagemygrants-user": "Utgivare:",
+ "mwoauthmanagemygrants-description": "Beskrivning",
+ "mwoauthmanagemygrants-wikiallowed": "Tillåten på projekt:",
+ "mwoauthmanagemygrants-grants": "Tillämpliga behörigheter",
+ "mwoauthmanagemygrants-grantsallowed": "Stipendier tillåtna",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Tillämpliga behörigheter som tillåts:",
+ "mwoauthmanagemygrants-review": "hantera åtkomst",
+ "mwoauthmanagemygrants-revoke": "återkalla åtkomst",
+ "mwoauthmanagemygrants-grantaccept": "Beviljas",
+ "mwoauthmanagemygrants-update-text": "Använd formuläret nedan för att ändra de behörigheter som beviljats för en applikation att agera åt dina vägnar.",
+ "mwoauthmanagemygrants-revoke-text": "Använd formuläret nedan för att återkalla åtkomst för en applikation att agera åt dina vägnar.",
+ "mwoauthmanagemygrants-confirm-legend": "Hantera ansluten applikation",
+ "mwoauthmanagemygrants-update": "Uppdatera behörigheter",
+ "mwoauthmanagemygrants-renounce": "Avauktorisera",
+ "mwoauthmanagemygrants-action": "Ändra status:",
+ "mwoauthmanagemygrants-confirm-submit": "Uppdatera åtkomsttokenstatus",
+ "mwoauthmanagemygrants-success-update": "Dina inställningar för denna applikation har blivit uppdaterade.",
+ "mwoauthmanagemygrants-success-renounce": "Denna applikations åtkomst till ditt konto har återkallats.",
+ "mwoauthmanagemygrants-basic-tooltip": "Varför kan jag inte uppdatera denna behörighet? Denna behörighet ger din anslutna applikation grundläggande behörighet vilket den kräver för att kunna fungera korrekt. Om du inte vill att den anslutna applikationen ska ha dessa rättigheter bör du återkalla applikationens åtkomst.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Varför kan jag inte uppdatera denna behörighet? Om du inte vill att denna anslutna applikation ska ha denna rättighet, bör du återkalla applikationens tillgång.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Dina}} redigeringar med denna applikation",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Dina}} åtgärder med denna applikation",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|Föreslog}} en OAuthkonsument (konsumentnyckel $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|uppdaterade}} en OAuthkonsument (konsumentnyckel $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|Godkände}} en OAuthkonsument av $3 (konsumentnyckel $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|Avslog}} en OAuthkonsument av $3 (konsumentnyckel $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|Inaktiverade}} en OAuthkonsument av $3 (konsumentnyckel $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|Återaktiverade}} en OAuthkonsument av $3 (konsumentnyckel $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|skapade}} en OAuth-konsument endast för ägaren (konsumentnyckel $4)",
+ "log-action-filter-mwoauthconsumer": "Typ av OAuth-konsumentåtgärd:",
+ "log-action-filter-mwoauthconsumer-approve": "Godkännande av OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Skapande av OAuth-konsument endast för ägaren",
+ "log-action-filter-mwoauthconsumer-disable": "Inaktivering av OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-propose": "Förslag på OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-reenable": "Återaktivering av OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-reject": "Avslag på OAuth-konsument",
+ "log-action-filter-mwoauthconsumer-update": "Uppdatering av OAuth-konsument",
+ "mwoauthconsumer-consumer-logpage": "OAuthkonsumentslogg",
+ "mwoauthconsumer-consumer-logpagetext": "Logg över godkännanden, avslag och inaktivering av registrerade OAuthkonsumenter.",
+ "mwoauth-bad-request-missing-params": "Tyvärr gick något fel under konfigurationen av denna anslutna applikation. Kontakta utvecklaren.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth saknar parametrar, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Någonting gick tyvärr fel. Du måste kontakta applikationens skapare för att få hjälp med detta.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Okänd webbadress, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Någonting gick tyvärr fel. Du måste [$1 kontakta] applikationens skapare för att få hjälp med detta.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Okänd webbadress, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Inget godkänt behörigheter har hittats för den auktoriseringstoken.",
+ "mwoauthdatastore-request-token-not-found": "Tyvärr uppstod ett fel vid anslutning till denna applikation.\nGå tillbaka och försök att ansluta igen, eller kontakta applikationens skapare.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ingen OAuth-nyckel hittades, $1</span>",
+ "mwoauthdatastore-callback-not-found": "Url för OAuth-callback hittades inte i cachen. Detta är antagligen ett fel i hur applikationen för förfrågningar till servern.",
+ "mwoauthdatastore-request-token-already-used": "Denna förfrågan har redan färdigställts och kan inte skickas igen.\nGå tillbaka till applikationen och försök att ansluta ditt konto igen, eller kontakta applikationens skapare.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth-nyckel har redan använts, $1</span>",
+ "mwoauthdatastore-bad-token": "Ingen nyckel hittades som matchade din begäran.",
+ "mwoauthdatastore-bad-source-ip": "Begäran kom från en ogiltig IP-adress.",
+ "mwoauthdatastore-bad-verifier": "Verifikationskoden som givits var inte giltig.",
+ "mwoauthdatastore-invalid-token-type": "Den begärda tokentypen är ogiltig.",
+ "mwoauthgrants-general-error": "Det uppstod ett fel i din OAuthbegäran.",
+ "mwoauthserver-bad-consumer": "\"$1\" är inte godkänd som en Ansluten App. [$2 Kontakta] applikationens skapare för hjälp.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ansluten OAuth-app inte godkänd, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Någonting gick tyvärr fel under anslutningen till denna applikationen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Okänd OAuth-nyckel, $1</span>",
+ "mwoauthserver-insufficient-rights": "Ditt konto kan inte använda Anslutna Appar, kontakta webbplatsens administratör för att få reda på varför.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Otillräckliga OAuth-användarrättigheter, $1</span>",
+ "mwoauthserver-invalid-request-token": "Ogiltig nyckel i din begäran.",
+ "mwoauthserver-invalid-user": "För att använda Anslutna Appar på denna webbplats måste du ha ett konto på alla projekt. När du har ett konto på alla projekt kan du försöka ansluta till \"$1\" igen.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Gemensam inloggning behövs, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Någonting gick tyvärr fel vid anslutningen av denna applikation.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konsumenten har ingen hemlig nyckel, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" är en ansluten applikation endast för ägaren. För att hämta accessnyckeln, se [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Konsumenten är endast för ägaren, $3</span>",
+ "mwoauth-invalid-authorization-title": "OAuth auktoriseringsfel",
+ "mwoauth-invalid-authorization": "Auktoriseringsrubriker i din begäran är inte giltiga: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "De auktoriserade rubrikerna i din begäran är inte giltiga för $1",
+ "mwoauth-invalid-authorization-invalid-user": "Auktoriseringsrubrikerna i din begäran är för användare som inte existerar här",
+ "mwoauth-invalid-authorization-wrong-user": "Auktoriseringsrubrikerna i din begäran är för en annan användare",
+ "mwoauth-invalid-authorization-not-approved": "Appen du försöker ansluta verkar vara felaktigt inställd. Kontakta skaparen av \"$1\" för hjälp.",
+ "mwoauth-invalid-authorization-blocked-user": "Auktoriseringsrubrikerna i din begäran är för en användare som är blockerad",
+ "mwoauth-form-description-allwikis": "Hej $1,\nFör att slutföra din begäran behöver '''$2''' behörighet att utföra följande åtgärder åt dina vägnar på alla projekt på denna sida:\n\n$4",
+ "mwoauth-form-description-onewiki": "Hej $1,\n\nFör att slutföra din begäran behöver '''$2''' behörighet att utföra följande åtgärder åt dina vägnar på '''$4''':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Hej $1,\n\nFör att slutföra din begäran behöver '''$2''' behörighet till åtkomst för alla projekt på denna sida åt dina vägnar. Inga ändringar kommer att göras på ditt konto.",
+ "mwoauth-form-description-onewiki-nogrants": "Hej $1,\n\nFör att slutföra din begäran behöver '''$2''' behörighet för att komma åt information på ''$4'' åt dina vägnar. Inga ändringar kommer att göras på ditt konto.",
+ "mwoauth-form-description-allwikis-privateinfo": "Hej $1,\n\nFör att slutföra din begäran, behöver \"'$2\"' tillstånd att få tillgång till information om dig, inklusive ditt verkliga namn och e-postadress, i alla projekt på denna webbplats. Inga ändringar kommer att göras med ditt konto.",
+ "mwoauth-form-description-onewiki-privateinfo": "Hej $1,\n\nFör att slutföra din begäran behöver '''$2''' behörighet att komma åt information, däribland ditt verkliga namn och mailadress, på ''$4''. Inga ändringar kommer att göras med ditt konto.",
+ "mwoauth-form-button-approve": "Tillåt",
+ "mwoauth-form-button-cancel": "Avbryt",
+ "mwoauth-error": "Anslutningsfel för applikationen",
+ "mwoauth-grants-heading": "Begärda tillstånd:",
+ "mwoauth-grants-nogrants": "Ansökan har inte begärt något tillstånd.",
+ "mwoauth-acceptance-cancelled": "Du har valt att inte tillåta \"$1\" åtkomst till ditt konto. \"$1\" kommer inte att fungera om du inte ger den åtkomst. Du kan gå tillbaka till \"$1\" eller [[Special:OAuthManageMyGrants|hantera]] dina anslutna appar.",
+ "mwoauth-granttype-normal": "Be om autentisering för särskilda behörigheter.",
+ "grant-mwoauth-authonly": "Endast verifiering av användarens identitet, ingen möjlighet att läsa sidor eller utföra åtgärder för användarens räkning.",
+ "grant-mwoauth-authonlyprivate": "Endast verifiering av användarens identitet med tillgång till riktigt namn och e-postadress, ingen möjlighet att läsa sidor eller utföra åtgärder för användarens räkning.",
+ "mwoauth-listgrants-extra-summary": "== OAuth-specifika behörigheter ==\n\nDessa ytterligare behörigheter är tillämpliga på OAuth-konsumenter.",
+ "mwoauth-oauth-exception": "Ett fel uppstod i OAuth-protokollet: $1",
+ "mwoauth-callback-not-oob": "oauth_callback måste anges och måste ställas in på \"oob\" (skiftlägeskänslig)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback måste anges, och måste sättas till \"oob\" (skiftlägeskänsligt), eller så måste den konfigurerade callbacken vara ett prefix för den tllhandahållna callbacken.",
+ "right-mwoauthproposeconsumer": "Föreslå nya OAuth-konsumenter",
+ "right-mwoauthupdateownconsumer": "Uppdatera OAuth-konsumenter du styr",
+ "right-mwoauthmanageconsumer": "Hantera OAuth-konsumenter",
+ "right-mwoauthsuppress": "Undertryck OAuth-konsumenter",
+ "right-mwoauthviewsuppressed": "Visa undertryckta OAuth-konsumenter",
+ "right-mwoauthviewprivate": "Visa privat OAuth-data",
+ "right-mwoauthmanagemygrants": "Hantera OAuth-behörigheter",
+ "action-mwoauthmanageconsumer": "Hantera OAuthkonsument",
+ "action-mwoauthsuppress": "undertrycka OAuth-konsumenter",
+ "action-mwoauthmanagemygrants": "Hantera dina OAuth-behörigheter",
+ "action-mwoauthproposeconsumer": "Föreslå nya OAuthkonsument",
+ "action-mwoauthupdateownconsumer": "Uppdatera OAuthkonsumenter du kontrollerar",
+ "action-mwoauthviewprivate": "visa privat OAuth-data",
+ "action-mwoauthviewsuppressed": "Visa upphävda OAuthkonsumenter",
+ "mwoauth-tag-reserved": "Taggar som börjar med <code>OAuth CID:</code> är reserverade för OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Observera:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> är säkrare än bot-lösenord och bör föredras i de fall då boten stöder det.",
+ "mwoauth-api-module-disabled": "\"$1\"-modulen är inte tillgänglig med OAuth.",
+ "echo-category-title-oauth-owner": "OAuth-utveckling",
+ "echo-pref-tooltip-oauth-owner": "Meddela mig om händelser relaterade till OAuth-applikationer som jag har skapat.",
+ "echo-category-title-oauth-admin": "OAuth-admin",
+ "echo-pref-tooltip-oauth-admin": "Meddela mig om händelser relaterade till granskning av OAuth-ansökningar.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|föreslog}} en ny OAuth-applikation: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|uppdaterade}} OAuth-applikationen $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|godkände}} {{GENDER:$3|din}} OAuth-applikation ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|avvisade}} {{GENDER:$3|din}} OAuth-applikation ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|inaktiverade}} {{GENDER:$3|din}} OAuth-applikation ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|återaktiverade}} {{GENDER:$3|din}} OAuth-applikation ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|föreslog}} en ny OAuth-applikation på {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|uppdaterade}} en OAuth-applikation på {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|godkände}} {{GENDER:$3|din}} OAuth-applikation på {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|avvisade}} {{GENDER:$3|din}} OAuth-applikation på {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|inaktiverade}} {{GENDER:$3|din}} OAuth-applikation på {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|återaktiverade}} {{GENDER:$3|din}} OAuth-applikation på {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Granska applikation",
+ "notification-oauth-app-update-primary-link": "Granska applikation",
+ "notification-oauth-app-approve-primary-link": "Visa app",
+ "notification-oauth-app-reject-primary-link": "Visa app",
+ "notification-oauth-app-disable-primary-link": "Visa app",
+ "notification-oauth-app-reenable-primary-link": "Visa app",
+ "notification-oauth-app-body": "Anledning: $1",
+ "mwoauth-oauth2-granttype-refresh-token": "Uppdatera nyckel"
+}
diff --git a/OAuth/i18n/sw.json b/OAuth/i18n/sw.json
new file mode 100644
index 00000000..a4d33f35
--- /dev/null
+++ b/OAuth/i18n/sw.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Yasen igra"
+ ]
+ },
+ "mwoauthconsumerregistration-navigation": "Urambazaji:",
+ "mwoauthmanagemygrants-navigation": "Urambazaji:"
+}
diff --git a/OAuth/i18n/szl.json b/OAuth/i18n/szl.json
new file mode 100644
index 00000000..86a507d9
--- /dev/null
+++ b/OAuth/i18n/szl.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Uostofchuodnego"
+ ]
+ },
+ "oauthlistconsumers": "Lista aplikacyji OAuth",
+ "oauthmanagemygrants": "Mynedżer prziłōnczōnych aplikacyji"
+}
diff --git a/OAuth/i18n/ta.json b/OAuth/i18n/ta.json
new file mode 100644
index 00000000..bde32d0a
--- /dev/null
+++ b/OAuth/i18n/ta.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "ElangoRamanujam",
+ "Kalyanasundar"
+ ]
+ },
+ "notification-oauth-app-approve-primary-link": "செயலியைக் காண்",
+ "notification-oauth-app-reject-primary-link": "செயலியைக் காண்",
+ "notification-oauth-app-disable-primary-link": "செயலியைக் காண்",
+ "notification-oauth-app-reenable-primary-link": "செயலியைக் காண்",
+ "notification-oauth-app-body": "காரணம்: $1"
+}
diff --git a/OAuth/i18n/tcy.json b/OAuth/i18n/tcy.json
new file mode 100644
index 00000000..d6a13d74
--- /dev/null
+++ b/OAuth/i18n/tcy.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ravi Mundkur"
+ ]
+ },
+ "mwoauth-prefs-managegrantslink": "ನಡಪಾಲೆ {{PLURAL:$1|$1 connected application|$1 connected applications|0=connected applications}}"
+}
diff --git a/OAuth/i18n/te.json b/OAuth/i18n/te.json
new file mode 100644
index 00000000..b9fb0bf3
--- /dev/null
+++ b/OAuth/i18n/te.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chaduvari",
+ "Ravichandra",
+ "Veeven"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "అనుసంధానిత అనువర్తనాలు:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|$1 అనుసంధానిత అనువర్తనాన్ని|$1 అనుసంధానిత అనువర్తనాలను|0=అనుసంధానిత అనువర్తనాలను}} నిర్వహించుకోండి",
+ "mwoauth-consumer-owner-only-label": "యజమాని-మాత్రమే:",
+ "notification-oauth-app-body": "కారణం: $1"
+}
diff --git a/OAuth/i18n/tg-cyrl.json b/OAuth/i18n/tg-cyrl.json
new file mode 100644
index 00000000..d5c50716
--- /dev/null
+++ b/OAuth/i18n/tg-cyrl.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "ToJack"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Барномаҳои фаъол:",
+ "mwoauth-consumer-name": "Номи барнома:",
+ "mwoauth-consumer-description": "Тавзеҳоти барнома:",
+ "mwoauthlistconsumers-name": "Номи барнома",
+ "mwoauthmanagemygrants-success-update": "Танзимоти ин барнома азнавсозӣ шудаанд.",
+ "mwoauth-invalid-authorization-not-approved": "Ҳамёне, ки шумо мехоҳед истифода баред, мумкин нодуруст кор кунад. Барои иттило бо $1 тамос гиред."
+}
diff --git a/OAuth/i18n/th.json b/OAuth/i18n/th.json
new file mode 100644
index 00000000..04dd7ed6
--- /dev/null
+++ b/OAuth/i18n/th.json
@@ -0,0 +1,178 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aefgh39622"
+ ]
+ },
+ "mwoauth-desc": "อนุญาตการใช้ OAuth 1.0a สำหรับการยืนยันตัวบุคคล API",
+ "mwoauth-verified": "ขณะนี้แอปพลิเคชันได้รับอนุญาตให้เข้าถึง MediaWiki ในนามของคุณแล้ว\n\nเพื่อดำเนินการให้เสร็จสมบูรณ์ โปรดกรอกโทเค็นการยืนยันตัวบุคคลลงในแอปพลิเคชัน: '''$1'''",
+ "mwoauth-db-readonly": "ฐานข้อมูล OAuth ถูกล็อกชั่วคราว โปรดลองอีกครั้งในภายหลัง",
+ "mwoauth-missing-field": "ไม่ได้กรอกค่าไว้ลงในเขตข้อมูล \"$1\"",
+ "mwoauth-invalid-field": "ค่าที่กรอกลงในเขตข้อมูล \"$1\" ไม่ถูกต้อง",
+ "mwoauth-invalid-field-generic": "ค่าที่กรอกไม่ถูกต้อง",
+ "mwoauth-field-hidden": "(ข้อมูลนี้ถูกซ่อนไว้)",
+ "mwoauth-field-private": "(ข้อมูลนี้เป็นข้อมูลส่วนตัว)",
+ "mwoauth-prefs-managegrants": "แอปที่เชื่อมต่อ:",
+ "mwoauth-prefs-managegrantslink": "จัดการ $1 แอปพลิเคชันที่เชื่อมต่อ",
+ "mwoauth-consumer-allwikis": "โครงการทั้งหมดบนไซต์นี้",
+ "mwoauth-consumer-key": "คีย์ผู้บริโภค:",
+ "mwoauth-consumer-name": "ชื่อแอปพลิเคชัน:",
+ "mwoauth-consumer-version": "รุ่นผู้บริโภค:",
+ "mwoauth-consumer-user": "ผู้เผยแพร่:",
+ "mwoauth-consumer-stage": "สถานะปัจจุบัน:",
+ "mwoauth-consumer-email": "ที่อยู่อีเมลติดต่อ:",
+ "mwoauth-consumer-description": "คำอธิบายแอปพลิเคชัน:",
+ "mwoauth-consumer-callbackurl": "URL การเรียกกลับของ OAuth:",
+ "mwoauth-consumer-wiki-thiswiki": "โครงการปัจจุบัน ($1)",
+ "mwoauth-consumer-restrictions": "การจำกัดการใช้งาน:",
+ "mwoauth-consumer-restrictions-json": "การจำกัดการใช้งาน (JSON):",
+ "mwoauth-consumer-secretkey": "โทเค็นลับผู้บริโภค:",
+ "mwoauth-consumer-accesstoken": "โทเค็นการเข้าถึง:",
+ "mwoauth-consumer-reason": "สาเหตุ:",
+ "mwoauth-consumer-email-unconfirmed": "อีเมลของคุณยังไม่ได้รับการยืนยัน",
+ "mwoauth-consumer-email-mismatched": "ที่อยู่อีเมลที่กรอกต้องตรงกับที่ระบุในบัญชีของคุณ",
+ "mwoauth-consumer-alreadyexists": "ผู้บริโภคที่มีชุดข้อมูลชื่อ/รุ่น/ผู้เผยแพร่นี้มีอยู่แล้ว",
+ "mwoauth-consumer-not-proposed": "ไม่ได้เสนอผู้บริโภคในขณะนี้",
+ "mwoauth-consumer-not-disabled": "ไม่ได้ปิดใช้งานผู้บริโภคในขณะนี้",
+ "mwoauth-consumer-not-approved": "ไม่ได้อนุมัติผู้บริโภค (อาจถูกปิดใช้งานไปแล้ว)",
+ "mwoauth-missing-consumer-key": "ไม่ได้กรอกคีย์ผู้บริโภค",
+ "mwoauth-invalid-consumer-key": "ผู้บริโภคที่มีคีย์ที่ระบุไม่มีอยู่",
+ "mwoauth-invalid-access-token": "โทเค็นการเข้าถึงที่มีคีย์ที่ระบุไม่มีอยู่",
+ "mwoauth-invalid-access-wrongwiki": "ผู้บริโภคสามารถใช้ได้เฉพาะบนโครงการ \"$1\" เท่านั้น",
+ "mwoauth-consumer-stage-proposed": "เสนอแล้ว",
+ "mwoauth-consumer-stage-rejected": "ปฏิเสธแล้ว",
+ "mwoauth-consumer-stage-expired": "หมดอายุแล้ว",
+ "mwoauth-consumer-stage-approved": "อนุมัติแล้ว",
+ "mwoauth-consumer-stage-disabled": "ปิดใช้งานแล้ว",
+ "oauthconsumerregistration": "การลงทะเบียนผู้บริโภค OAuth",
+ "mwoauthconsumerregistration-navigation": "การนำทาง:",
+ "mwoauthconsumerregistration-propose": "เสนอผู้บริโภคใหม่",
+ "mwoauthconsumerregistration-list": "รายชื่อผู้บริโภคของฉัน",
+ "mwoauthconsumerregistration-main": "หลัก",
+ "mwoauthconsumerregistration-update-legend": "อัปเดตแอปพลิเคชันผู้บริโภค OAuth",
+ "mwoauthconsumerregistration-update-submit": "อัปเดตผู้บริโภค",
+ "mwoauthconsumerregistration-none": "คุณไม่ได้ควบควมผู้บริโภค OAuth ใด ๆ",
+ "mwoauthconsumerregistration-name": "ผู้บริโภค",
+ "mwoauthconsumerregistration-user": "ผู้เผยแพร่",
+ "mwoauthconsumerregistration-description": "คำอธิบาย",
+ "mwoauthconsumerregistration-email": "อีเมลติดต่อ",
+ "mwoauthconsumerregistration-consumerkey": "คีย์ผู้บริโภค",
+ "mwoauthconsumerregistration-stage": "สถานะ",
+ "mwoauthconsumerregistration-lastchange": "การเปลี่ยนแปลงล่าสุด",
+ "mwoauthconsumerregistration-manage": "จัดการ",
+ "mwoauthconsumerregistration-created-owner-only": "สร้างผู้บริโภค OAuth ของคุณแล้ว\n\nโทเค็นของคุณคือ:\n; โทเค็นผู้บริโภค: $1\n; ข้อมูลลับผู้บริโภค: $2\n; โทเค็นการเข้าถึง: $3\n; ข้อมูลลับการเข้าถึง: $4\n<em>โปรดจดบันทึกข้อมูลเหล่านี้ไว้เพื่อใช้อ้างอิงในภายหลัง</em>",
+ "mwoauthconsumerregistration-updated": "อัปเดตรีจิสทรีผู้บริโภค OAuth ของคุณแล้ว",
+ "mwoauthconsumerregistration-secretreset": "คุณได้กำหนดโทเค็นข้อมูลลับผู้บริโภคของ '''$1''' แล้ว ''โปรดจดบันทึกข้อมูลเหล่านี้ไว้เพื่อใช้อ้างอิงในภายหลัง''",
+ "oauthmanageconsumers": "จัดการผู้บริโภค OAuth",
+ "mwoauthmanageconsumers-notloggedin": "คุณจำเป็นต้องล็อกอินเพื่อเข้าถึงหน้านี้",
+ "mwoauthmanageconsumers-type": "คิว:",
+ "mwoauthmanageconsumers-main": "หลัก",
+ "mwoauthmanageconsumers-queues": "เลือกคิวการยืนยันผู้บริโภคจากด้านล่าง:",
+ "mwoauthmanageconsumers-q-proposed": "คิวคำขอผู้บริโภคที่เสนอ",
+ "mwoauthmanageconsumers-q-rejected": "คิวคำขอผู้บริโภคที่ปฏิเสธ",
+ "mwoauthmanageconsumers-q-expired": "คิวคำขอผู้บริโภคที่หมดอายุ",
+ "mwoauthmanageconsumers-lists": "เลือกรายการสถานะผู้บริโภคจากด้านล่าง:",
+ "mwoauthmanageconsumers-l-approved": "รายชื่อผู้บริโภคที่ถูกอนุมัติ",
+ "mwoauthmanageconsumers-l-disabled": "รายชื่อผู้บริโภคที่ถูกปิดใช้งาน",
+ "mwoauthmanageconsumers-none-approved": "ไม่มีผู้บริโภคที่ตรงกับเกณฑ์",
+ "mwoauthmanageconsumers-none-disabled": "ไม่มีผู้บริโภคที่ตรงกับเกณฑ์",
+ "mwoauthmanageconsumers-name": "ผู้บริโภค",
+ "mwoauthmanageconsumers-user": "ผู้เผยแพร่",
+ "mwoauthmanageconsumers-description": "คำอธิบาย",
+ "mwoauthmanageconsumers-email": "อีเมลติดต่อ",
+ "mwoauthmanageconsumers-consumerkey": "คีย์ผู้บริโภค",
+ "mwoauthmanageconsumers-lastchange": "การเปลี่ยนแปลงล่าสุด",
+ "mwoauthmanageconsumers-confirm-text": "ใช้แบบฟอร์มนี้เพื่ออนุมัติ ปฏิเสธ ปิดใช้งาน หรือเปิดใช้งานผู้บริโภคนี้ใหม่",
+ "mwoauthmanageconsumers-confirm-legend": "จัดการผู้บริโภค OAuth",
+ "mwoauthmanageconsumers-action": "เปลี่ยนสถานะ:",
+ "mwoauthmanageconsumers-approve": "อนุมัติแล้ว",
+ "mwoauthmanageconsumers-reject": "ปฏิเสธแล้ว",
+ "mwoauthmanageconsumers-disable": "ปิดใช้งานแล้ว",
+ "mwoauthmanageconsumers-reenable": "อนุมัติแล้ว",
+ "mwoauthmanageconsumers-reason": "สาเหตุ:",
+ "mwoauthmanageconsumers-confirm-submit": "อัปเดตสถานะผู้บริโภค",
+ "mwoauthmanageconsumers-success-approved": "คำขอได้รับการอนุมัติแล้ว",
+ "mwoauthmanageconsumers-success-rejected": "คำขอได้รับการปฏิเสธแล้ว",
+ "mwoauthmanageconsumers-success-disabled": "ผู้บริโภคได้ถูกปิดใช้งานแล้ว",
+ "mwoauthmanageconsumers-success-reanable": "ผู้บริโภคได้ถูกเปิดใช้งานใหม่แล้ว",
+ "mwoauthmanageconsumers-search-name": "ผู้บริโภคที่มีชื่อนี้",
+ "mwoauthmanageconsumers-search-publisher": "ผู้บริโภคของผู้ใช้นี้",
+ "oauthlistconsumers": "แสดงรายชื่อแอปพลิเคชัน OAuth",
+ "mwoauthlistconsumers-legend": "เรียกดูแอปพลิเคชัน OAuth",
+ "mwoauthlistconsumers-view": "รายละเอียด",
+ "mwoauthlistconsumers-none": "ไม่พบแอปพลิเคชันที่ตรงกับเกณฑ์นี้",
+ "mwoauthlistconsumers-name": "ชื่อแอปพลิเคชัน",
+ "mwoauthlistconsumers-version": "รุ่นผู้บริโภค",
+ "mwoauthlistconsumers-user": "ผู้เผยแพร่",
+ "mwoauthlistconsumers-description": "คำอธิบาย",
+ "mwoauthlistconsumers-callbackurl": "URL การเรียกกลับของ OAuth",
+ "mwoauthlistconsumers-basicgrantsonly": "(การเข้าถึงพื้นฐานเท่านั้น)",
+ "mwoauthlistconsumers-status": "สถานะ",
+ "mwoauth-consumer-stage-any": "ใด ๆ",
+ "mwoauthlistconsumers-status-approved": "อนุมัติแล้ว",
+ "mwoauthlistconsumers-status-disabled": "ปิดใช้งานแล้ว",
+ "mwoauthlistconsumers-status-rejected": "ปฏิเสธแล้ว",
+ "mwoauthlistconsumers-status-expired": "หมดอายุแล้ว",
+ "oauthmanagemygrants": "จัดการแอปพลิเคชันที่เชื่อมต่อ",
+ "mwoauthmanagemygrants-text": "หน้านี้แสดงรายชื่อแอปพลิเคชันที่สามารถใช้บัญชีของคุณได้ สำหรับแอปพลิเคชันบางตัว ขอบเขตของการเข้าถึงอาจถูกจำกัดโดยสิทธิ์ที่คุณได้อนุญาตให้กับแอปพลิเคชันเมื่อคุณยืนยันตัวบุคคลในชื่อของคุณ ถ้าคุณยืนยันตัวบุคคลแอปพลิเคชันเพื่อเข้าถึงโครงการพี่น้องอื่น ๆ ต่างหากในชื่อของคุณ คุณจะเห็นการกำหนดค่าที่แยกกันสำหรับโครงการแต่ละโครงการด้านล่างนี้\n\nแอปพลิเคชันที่เชื่อมต่อจะเข้าถึงบัญชีของคุณโดยใช้โปรโตคอล OAuth <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth เรียนรู้เพิ่มเติมเกี่ยวกับแอปพลิเคชันที่เชื่อมต่อ])</span>",
+ "mwoauthmanagemygrants-navigation": "การนำทาง:",
+ "mwoauthmanagemygrants-showlist": "รายชื่อแอปพลิเคชันที่เชื่อมต่อ",
+ "mwoauthmanagemygrants-none": "ไม่มีแอปพลิเคชันที่เชื่อมต่อกับบัญชีของคุณ",
+ "mwoauthmanagemygrants-user": "ผู้เผยแพร่:",
+ "mwoauthmanagemygrants-description": "คำอธิบาย",
+ "mwoauthmanagemygrants-review": "จัดการการเข้าถึง",
+ "mwoauthmanagemygrants-revoke": "เพิกถอนการเข้าถึง",
+ "mwoauthmanagemygrants-confirm-legend": "จัดการแอปพลิเคชันที่เชื่อมต่อ",
+ "mwoauthmanagemygrants-renounce": "ยกเลิกการยืนยันตัวบุคคล",
+ "mwoauthmanagemygrants-action": "เปลี่ยนสถานะ:",
+ "mwoauthmanagemygrants-confirm-submit": "อัปเดตสถานะโทเค็นการเข้าถึง",
+ "logentry-mwoauthconsumer-propose": "$1 ได้เสนอผู้บริโภค OAuth (คีย์ผู้บริโภค $4)",
+ "logentry-mwoauthconsumer-update": "$1 ได้อัปเดตผู้บริโภค OAuth (คีย์ผู้บริโภค $4)",
+ "logentry-mwoauthconsumer-approve": "$1 ได้อนุมัติผู้บริโภค OAuth โดย $3 (คีย์ผู้บริโภค $4)",
+ "logentry-mwoauthconsumer-reject": "$1 ได้ปฏิเสธผู้บริโภค OAuth โดย $3 (คีย์ผู้บริโภค $4)",
+ "logentry-mwoauthconsumer-disable": "$1 ได้ปิดใช้งานผู้บริโภค OAuth โดย $3 (คีย์ผู้บริโภค $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 ได้เปิดใช้งานผู้บริโภค OAuth โดย $3 ใหม่ (คีย์ผู้บริโภค $4)",
+ "mwoauthconsumer-consumer-logpage": "ปูมผู้บริโภค OAuth",
+ "mwoauthdatastore-bad-token": "ไม่พบโทเค็นที่ตรงกับคำขอของคุณ",
+ "mwoauthdatastore-bad-verifier": "รหัสการยืนยันที่กรอกไม่ถูกต้อง",
+ "mwoauthgrants-general-error": "มีข้อผิดพลาดในคำขอ OAuth ของคุณ",
+ "mwoauthserver-invalid-request-token": "โทเค็นไม่ถูกต้องในคำขอของคุณ",
+ "mwoauth-invalid-authorization-title": "ข้อผิดพลาดการยืนยันตัวบุคคล OAuth",
+ "mwoauth-form-button-approve": "อนุญาต",
+ "mwoauth-form-button-cancel": "ยกเลิก",
+ "mwoauth-error": "การเชื่อมต่อแอปพลิเคชันผิดพลาด",
+ "mwoauth-oauth-exception": "เกิดข้อผิดพลาดในโปรโตคอล OAuth: $1",
+ "mwoauth-callback-not-oob": "ต้องกำหนดค่าใน oauth_callback และต้องกำหนดค่าเป็น \"oob\" (ตรงตามตัวพิมพ์ใหญ่-เล็ก)",
+ "right-mwoauthproposeconsumer": "เสนอผู้บริโภค OAuth ใหม่",
+ "right-mwoauthupdateownconsumer": "อัปเดตผู้บริโภค OAuth ที่คุณควบคุม",
+ "right-mwoauthmanageconsumer": "จัดการผู้บริโภค OAuth",
+ "right-mwoauthsuppress": "ไม่แสดงผู้บริโภค OAuth",
+ "right-mwoauthviewsuppressed": "ดูผู้บริโภค OAuth ที่ไม่แสดง",
+ "right-mwoauthviewprivate": "ดูข้อมูล OAuth ส่วนตัว",
+ "right-mwoauthmanagemygrants": "ดูการอนุญาต OAuth",
+ "action-mwoauthmanageconsumer": "จัดการผู้บริโภค OAuth",
+ "action-mwoauthmanagemygrants": "จัดการการอนุญาต OAuth ของคุณ",
+ "action-mwoauthproposeconsumer": "เสนอผู้บริโภค OAuth ใหม่",
+ "action-mwoauthupdateownconsumer": "อัปเดตผู้บริโภค OAuth ที่คุณควบคุม",
+ "action-mwoauthviewsuppressed": "ดูผู้บริโภค OAuth ที่ไม่แสดง",
+ "echo-category-title-oauth-owner": "การพัฒนา OAuth",
+ "echo-pref-tooltip-oauth-owner": "เตือนฉันเกี่ยวกับเหตุการณ์ที่เกี่ยวข้องกับแอปพลิเคชัน OAuth ที่ฉันสร้าง",
+ "echo-pref-tooltip-oauth-admin": "เตือนฉันเกี่ยวกับเหตุการณ์ที่เกี่ยวข้องกับการตรวจสอบแอปพลิเคชัน OAuth",
+ "notification-oauth-app-propose-title": "$1 ได้เสนอแอป OAuth ใหม่: $2",
+ "notification-oauth-app-update-title": "$1 ได้อัปเดตแอป OAuth $2",
+ "notification-oauth-app-approve-title": "$1 ได้อนุมัติแอป OAuth ของคุณ ($2)",
+ "notification-oauth-app-reject-title": "$1 ได้ปฏิเสธแอป OAuth ของคุณ ($2)",
+ "notification-oauth-app-disable-title": "$1 ได้ปิดใช้งานแอป OAuth ของคุณ ($2)",
+ "notification-oauth-app-reenable-title": "$1 ได้เปิดใช้งานแอป OAuth ของคุณใหม่ ($2)",
+ "notification-oauth-app-propose-subject": "$1 ได้เสนอแอป OAuth ใหม่บน {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 ได้อัปเดตแอป OAuth บน {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 ได้อนุมัติแอป OAuth ของคุณบน {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 ได้ปฏิเสธแอป OAuth ของคุณบน {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 ได้ปิดใช้งานแอป OAuth ของคุณบน {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 ได้เปิดใช้งานแอป OAuth ของคุณใหม่บน {{SITENAME}}",
+ "notification-oauth-app-approve-primary-link": "แสดงแอป",
+ "notification-oauth-app-reject-primary-link": "แสดงแอป",
+ "notification-oauth-app-disable-primary-link": "แสดงแอป",
+ "notification-oauth-app-reenable-primary-link": "แสดงแอป",
+ "notification-oauth-app-body": "สาเหตุ: $1"
+}
diff --git a/OAuth/i18n/tl.json b/OAuth/i18n/tl.json
new file mode 100644
index 00000000..def856c3
--- /dev/null
+++ b/OAuth/i18n/tl.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "BaRaN6161 TURK",
+ "Emem.calist"
+ ]
+ },
+ "mwoauth-consumer-email-help": "Kilala lang sa mga aprubado't baguhang suki",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|aprobado}} {{GENDER:$3|iyong}} OMaya app ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|maykapansanan}} {{GENDER:$3|iyong}} OMaya app ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|👍}} {{GENDER:$3|iyong}} OMaya app ($2)"
+}
diff --git a/OAuth/i18n/tly.json b/OAuth/i18n/tly.json
new file mode 100644
index 00000000..7f09b3bb
--- /dev/null
+++ b/OAuth/i18n/tly.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "Patriot Kur"
+ ]
+ },
+ "mwoauthconsumerregistration-description": "Təsvir",
+ "mwoauthmanageconsumers-description": "Təsvir",
+ "mwoauthlistconsumers-description": "Təsvir",
+ "mwoauthmanagemygrants-description": "Təsvir",
+ "mwoauth-form-button-cancel": "Ləğv karde"
+}
diff --git a/OAuth/i18n/tr.json b/OAuth/i18n/tr.json
new file mode 100644
index 00000000..e921f2ed
--- /dev/null
+++ b/OAuth/i18n/tr.json
@@ -0,0 +1,338 @@
+{
+ "@metadata": {
+ "authors": [
+ "BaRaN6161 TURK",
+ "Demircimehmed",
+ "Grkn gll",
+ "Incelemeelemani",
+ "McAang",
+ "Meelo",
+ "Rapsar",
+ "Sayginer",
+ "SwornToTranslate29",
+ "TmY e12",
+ "ToprakM",
+ "Vito Genovese"
+ ]
+ },
+ "mwoauth-desc": "API yetkilendirmesi için OAuth 1.0a ve OAuth 2.0 kullanımına izin verir",
+ "mwoauth-nosubpage-explanation": "OAuth, harici uygulamaların bu kullanıcıdan izin aldıktan sonra {{SITENAME}} kullanıcısını tanımasına veya onlar adına taşıma olanak tanıyan bir mekanizmadır.\n\nBu sayfanın bir şey yapması için daha fazla parametre gerekir. Buraya harici bir uygulamadan gönderildiyseniz, bunun nedeni büyük olasılıkla o uygulamadaki bir hatadır; yazarla iletişime geçmelisiniz.",
+ "mwoauth-verified": "Uygulamanın artık sizin adınıza MediaWiki'ye erişmesine izin verildi.\n\nİşlemi tamamlamak için bu doğrulama değerini uygulamaya sağlayın: '''$1'''",
+ "mwoauth-db-readonly": "OAuth veritabanı geçici olarak kilitlendi. Lütfen birkaç dakika içinde tekrar deneyin.",
+ "mwoauth-missing-field": "\"$1\" alanı için eksik değer",
+ "mwoauth-invalid-field": "\"$1\" alanı için geçersiz değer sağlandı",
+ "mwoauth-invalid-field-generic": "Geçersiz değer sağlandı",
+ "mwoauth-field-hidden": "(bu bilgiler gizlidir)",
+ "mwoauth-field-private": "(bu bilgi gizlidir)",
+ "mwoauth-prefs-managegrants": "Bağlı uygulamalar:",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|$1 bağlı uygulamayı|$1 bağlı uygulamayı|0=bağlı uygulamayı}} yönet",
+ "mwoauth-consumer-allwikis": "Bu sitedeki bütün projeler",
+ "mwoauth-consumer-key": "Tüketici anahtarı:",
+ "mwoauth-consumer-name": "Uygulama adı:",
+ "mwoauth-consumer-version": "Tüketici sürümü:",
+ "mwoauth-consumer-user": "Yayıncı:",
+ "mwoauth-consumer-stage": "Geçerli durumu:",
+ "mwoauth-consumer-email": "İletişim e-posta adresi:",
+ "mwoauth-consumer-email-help": "Yalnızca yeni tüketicileri onaylayanlar görebilir",
+ "mwoauth-consumer-owner-only-label": "Sahibi tarafından okunur:",
+ "mwoauth-consumer-owner-only": "Bu tüketici yalnızca $1 kullanım içindir.",
+ "mwoauth-consumer-owner-only-help": "Bu seçeneğin belirlenmesi, tüketicinin otomatik olarak $1 ile onaylanmasına ve kabul edilmesine neden olur. Başka bir kullanıcı tarafından kullanılamaz ve olağan yetkilendirme akışı çalışmaz. Bu tüketici kullanılarak yapılan işlemler etiketlenmez.",
+ "mwoauth-consumer-description": "Uygulama açıklaması:",
+ "mwoauth-consumer-callbackurl": "OAuth \"geri arama\" URL'si:",
+ "mwoauth-consumer-callbackisprefix": "Tüketicinin isteklerde geri arama yapmasına izin verin ve yukarıdaki \"geri arama\" URL'sini gerekli önek olarak kullanın.",
+ "mwoauth-consumer-granttypes": "İstenilen hibe türleri:",
+ "mwoauth-consumer-grantsneeded": "Geçerli ayrıcalıklar:",
+ "mwoauth-consumer-required-grant": "Tüketiciye uygulanabilir",
+ "mwoauth-consumer-wiki": "Uygulanabilir proje:",
+ "mwoauth-consumer-wiki-thiswiki": "Mevcut proje ($1)",
+ "mwoauth-consumer-restrictions": "Kullanım sınırları:",
+ "mwoauth-consumer-restrictions-json": "Kullanım sınırları (JSON):",
+ "mwoauth-consumer-rsakey": "Genel RSA anahtarı (isteğe bağlı):",
+ "mwoauth-consumer-rsakey-help": "RSA-SHA1 imza yöntemini kullanmak için bir ortak anahtar girin. HMAC-SHA1'i rastgele bir sır ile kullanmak için boş bırakın. Hangisinden emin değilseniz, boş bırakın.",
+ "mwoauth-consumer-secretkey": "Tüketici gizli anahtarı:",
+ "mwoauth-consumer-accesstoken": "Erişim anahtarı:",
+ "mwoauth-consumer-reason": "Sebep:",
+ "mwoauth-consumer-developer-agreement": "Bu başvuruyu göndererek, başvurunuzu devre dışı bırakma, sizi veya uygulamanızın bu siteye erişimini kaldırma veya kısıtlama hakkımızı saklı tuttuğumuzu ve münhasır yargımızda, veya başvurunuz bu sitenin herhangi bir politikasını, yönergesini ve yol gösterici ilkesini ihlal ediyorsa. Bu Uygulama Politikasını önceden haber vermeksizin, tamamen kendi takdirimize bağlı olarak ve gerekli gördüğümüz şekilde değiştirebiliriz. OAuth'u kullanmaya devam etmeniz bu değişikliklerin kabul edildiğini gösterir.",
+ "mwoauth-consumer-email-unconfirmed": "Hesap e-posta adresiniz henüz onaylanmadı.",
+ "mwoauth-consumer-email-mismatched": "Sağlanan e-posta adresinin hesabınızın adresiyle eşleşmesi gerekir.",
+ "mwoauth-consumer-alreadyexists": "Bu ad/sürüm/yayıncı kombinasyonuna sahip bir tüketici zaten var",
+ "mwoauth-consumer-alreadyexistsversion": "Bu ada / yayıncı kombinasyonuna sahip bir tüketici zaten eşit veya daha yüksek bir sürüme sahip (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Bekleyen bir tüketici isteği için bilgiler güncellenemiyor",
+ "mwoauth-consumer-not-proposed": "Tüketici şu anda önerilmiyor",
+ "mwoauth-consumer-not-disabled": "Tüketici şu anda devre dışı değil",
+ "mwoauth-consumer-not-approved": "Tüketici onaylanmadı (devre dışı bırakılmış olabilir)",
+ "mwoauth-missing-consumer-key": "Tüketici anahtarı sağlanmadı.",
+ "mwoauth-invalid-consumer-key": "Verilen anahtarda tüketici yok.",
+ "mwoauth-invalid-access-token": "Verilen anahtarla erişim belirteç yok.",
+ "mwoauth-invalid-access-wrongwiki": "Tüketici yalnızca \"$1\" projesinde kullanılabilir.",
+ "mwoauth-consumer-conflict": "Birisi bu tüketicinin özelliklerini görüntülediğinizde değiştirdi. Lütfen tekrar deneyin. Değişiklik günlüğünü kontrol etmek isteyebilirsiniz.",
+ "mwoauth-consumer-grantshelp": "Her hibe, bir kullanıcı hesabının sahip olduğu listelenen kullanıcı haklarına erişim sağlar. Daha fazla bilgi için [[Special:ListGrants|hibe tablosuna]] bakın.",
+ "mwoauth-consumer-stage-proposed": "önerilen",
+ "mwoauth-consumer-stage-rejected": "reddedilen",
+ "mwoauth-consumer-stage-expired": "süresi dolmuş",
+ "mwoauth-consumer-stage-approved": "onaylı",
+ "mwoauth-consumer-stage-disabled": "devre dışı",
+ "mwoauth-consumer-stage-suppressed": "bastırılmış",
+ "oauthconsumerregistration": "OAuth tüketici kaydı",
+ "mwoauthconsumerregistration-navigation": "Gezinti:",
+ "mwoauthconsumerregistration-propose": "Yeni tüketici öner",
+ "mwoauthconsumerregistration-list": "Tüketici listem",
+ "mwoauthconsumerregistration-main": "Ana",
+ "mwoauthconsumerregistration-propose-text": "Geliştiriciler yeni bir OAuth tüketicisi önermek için aşağıdaki formu kullanmalıdır (daha fazla ayrıntı için [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth uzantı belgelerine] bakın). Bu formu gönderdikten sonra, başvurunuzun kendisini MediaWiki'ye tanıtmak için kullanacağı bir belirteç alacaksınız. Bir OAuth yöneticisinin başvurunuzu diğer kullanıcılar tarafından yetkilendirilebilmesi için onaylaması gerekir.\n\nBirkaç öneri ve açıklama:\n* Mümkün olduğunca az sayıda hibe kullanmaya çalışın. Şu anda gerçekten gerekli olmayan hibelerden kaçının.\n* Sürümler \"major.minor.release\" biçimindedir (son ikisi isteğe bağlıdır) ve hibe değişikliklerine ihtiyaç duyuldukça artar.\n* Mümkünse lütfen genel bir RSA anahtarı (PEM biçiminde) sağlayın; aksi takdirde (daha az güvenli) bir gizli belirteç kullanılmalıdır.\n* Tüketiciyi bu sitedeki tek bir projeyle sınırlamak için bir proje kimliği kullanabilirsiniz (tüm projeler için \"*\" kullanın).",
+ "mwoauthconsumerregistration-update-text": "Kontrol ettiğiniz bir OAuth tüketicisinin özelliklerini güncellemek için aşağıdaki formu kullanın.\n\nBuradaki tüm değerler önceki değerlerin üzerine yazılır. Bu değerleri temizlemeyi düşünmüyorsanız boş alanlar bırakmayın.",
+ "mwoauthconsumerregistration-maintext": "Bu sayfa, geliştiricilerin bu sitenin kayıt defterinde OAuth tüketici uygulamaları önermesine ve güncellemesine izin vermek içindir.\n\nBuradan şunları yapabilirsiniz:\n* [[Special:OAuthConsumerRegistration/propose|Yeni bir tüketici için jeton isteyin]].\n* [[Special:OAuthConsumerRegistration/list|Mevcut tüketicilerinizi yönetin]].\n\nOAuth hakkında daha fazla bilgi için lütfen [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth uzantı belgelerine] bakın.",
+ "mwoauthconsumerregistration-propose-legend": "Yeni OAuth tüketici uygulaması",
+ "mwoauthconsumerregistration-update-legend": "OAuth tüketici uygulamasını güncelle",
+ "mwoauthconsumerregistration-propose-submit": "Tüketici öner",
+ "mwoauthconsumerregistration-update-submit": "Tüketiciyi güncelle",
+ "mwoauthconsumerregistration-none": "Hiçbir OAuth tüketicisini kontrol etmezsiniz.",
+ "mwoauthconsumerregistration-name": "Tüketici",
+ "mwoauthconsumerregistration-user": "Yayımcı",
+ "mwoauthconsumerregistration-description": "Açıklama",
+ "mwoauthconsumerregistration-email": "İletişim e-postası",
+ "mwoauthconsumerregistration-consumerkey": "Tüketici anahtarı",
+ "mwoauthconsumerregistration-stage": "Durum",
+ "mwoauthconsumerregistration-lastchange": "Son değişiklik",
+ "mwoauthconsumerregistration-manage": "yönet",
+ "mwoauthconsumerregistration-resetsecretkey": "Gizli anahtarı yeni bir değere sıfırlayın",
+ "mwoauthconsumerregistration-proposed": "OAuth tüketici isteğiniz alındı.\n\nSize '''$1''' tüketici anahtarı ve '''$2''' gizli anahtarı verildi. ''Lütfen bunları ileride başvurmak üzere kaydedin.''",
+ "mwoauthconsumerregistration-created-owner-only": "OAuth tüketiciniz oluşturuldu.\n\nAnahtarlarınız\n; Tüketici anahtar: $1\n; Tüketici sırrı: $2\n; Erişim anahtarı: $3\n; Erişim sırrı: $4\n<em>Lütfen bunları ileride başvurmak üzere kaydedin.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "OAuth 2.0 istemciniz oluşturuldu.\n\nAnahtarlarınız:\n; İstemci uygulama anahtarı: $1\n; İstemci uygulama sırrı: $2\n; Erişim anahtarı: $3\n;<em>Lütfen bunları ileride başvurmak üzere kaydedin.</em>",
+ "mwoauthconsumerregistration-updated": "OAuth tüketici kayıt defteriniz güncellendi.",
+ "mwoauthconsumerregistration-secretreset": "'''$1''' bir tüketici sırrı anahtarı atandı. ''Lütfen ileride başvurmak üzere kaydedin.''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "OAuth tüketici simgeleriniz sıfırlandı. Yeni anahtarlar:\n; Tüketici anahtarı: $1\n; Tüketici sırrı: $2\n; Erişim anahtarı: $3\n; Erişim sırrı: $4\n<em>Lütfen bunları ileride başvurmak üzere kaydedin.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "OAuth 2.0 tüketici simgeleriniz sıfırlandı. Yeni simgeler:\n; Tüketici belirteci: $1\n; Tüketici sırrı: $2\n; Erişim belirteci: $3\n<em>Lütfen bunları ileride başvurmak üzere kaydedin.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "OAuth uygulamaları oluşturmadan önce e-posta adresinizi onaylamanız gerekir.\nLütfen e-posta adresinizi [[Special:Preferences|kullanıcı tercihleri]] aracılığıyla ayarlayın ve doğrulayın.",
+ "oauthmanageconsumers": "OAuth tüketicilerini yönet",
+ "mwoauthmanageconsumers-notloggedin": "Bu sayfaya erişmek için giriş yapmalısınız.",
+ "mwoauthmanageconsumers-type": "Sıralar:",
+ "mwoauthmanageconsumers-showproposed": "Önerilen istekler",
+ "mwoauthmanageconsumers-showrejected": "Reddedilen istekler",
+ "mwoauthmanageconsumers-showexpired": "Süresi dolmuş istekler",
+ "mwoauthmanageconsumers-linkproposed": "önerilen istekler",
+ "mwoauthmanageconsumers-linkrejected": "reddedilen istekler",
+ "mwoauthmanageconsumers-linkexpired": "süresi dolmuş istekler",
+ "mwoauthmanageconsumers-linkapproved": "onaylanmış istekler",
+ "mwoauthmanageconsumers-linkdisabled": "devre dışı bırakılmış istekler",
+ "mwoauthmanageconsumers-main": "Ana",
+ "mwoauthmanageconsumers-maintext": "Bu sayfa OAuth (https://oauth.net/ bağlantıya bakın) tüketici uygulaması taleplerini ele almak ve yerleşik OAuth tüketicilerini yönetmek içindir.",
+ "mwoauthmanageconsumers-queues": "Aşağıdan bir tüketici onay kuyruğu seçin:",
+ "mwoauthmanageconsumers-q-proposed": "Önerilen tüketici istekleri kuyruğu",
+ "mwoauthmanageconsumers-q-rejected": "Reddedilen tüketici istekleri kuyruğu",
+ "mwoauthmanageconsumers-q-expired": "Süresi dolmuş tüketici istekleri kuyruğu",
+ "mwoauthmanageconsumers-lists": "Aşağıdan bir tüketici durumu listesi seçin:",
+ "mwoauthmanageconsumers-l-approved": "Şu anda onaylanmış tüketicilerin listesi",
+ "mwoauthmanageconsumers-l-disabled": "Şu anda devre dışı bırakılan tüketicilerin listesi",
+ "mwoauthmanageconsumers-none-proposed": "Bu listede önerilen tüketici yok.",
+ "mwoauthmanageconsumers-none-rejected": "Bu listede önerilen tüketici yok.",
+ "mwoauthmanageconsumers-none-expired": "Bu listede önerilen tüketici yok.",
+ "mwoauthmanageconsumers-none-approved": "Hiçbir tüketici bu kriterleri karşılamamaktadır.",
+ "mwoauthmanageconsumers-none-disabled": "Hiçbir tüketici bu kriterleri karşılamamaktadır.",
+ "mwoauthmanageconsumers-name": "Tüketici",
+ "mwoauthmanageconsumers-user": "Yayıncı",
+ "mwoauthmanageconsumers-description": "Açıklama",
+ "mwoauthmanageconsumers-email": "İletişim e-postası",
+ "mwoauthmanageconsumers-consumerkey": "Tüketici anahtarı",
+ "mwoauthmanageconsumers-lastchange": "Son değişiklik",
+ "mwoauthmanageconsumers-review": "incele/yönet",
+ "mwoauthmanageconsumers-confirm-text": "Bu tüketiciyi onaylamak, reddetmek, devre dışı bırakmak veya yeniden etkinleştirmek için bu formu kullanın.",
+ "mwoauthmanageconsumers-confirm-legend": "OAuth tüketiciyi yönet",
+ "mwoauthmanageconsumers-action": "Değişiklik durumu:",
+ "mwoauthmanageconsumers-approve": "Onaylandı",
+ "mwoauthmanageconsumers-reject": "Reddedildi",
+ "mwoauthmanageconsumers-rsuppress": "Reddedildi ve gözetlendi",
+ "mwoauthmanageconsumers-disable": "Devre dışı",
+ "mwoauthmanageconsumers-dsuppress": "Devre dışı ve gözetlendi",
+ "mwoauthmanageconsumers-reenable": "Onaylandı",
+ "mwoauthmanageconsumers-reason": "Sebep:",
+ "mwoauthmanageconsumers-confirm-submit": "Tüketici durumunu güncelle",
+ "mwoauthmanageconsumers-success-approved": "İstek onaylandı.",
+ "mwoauthmanageconsumers-success-rejected": "İstek reddedildi.",
+ "mwoauthmanageconsumers-success-disabled": "Tüketici devre dışı bırakıldı.",
+ "mwoauthmanageconsumers-success-reanable": "Tüketici yeniden etkinleştirildi.",
+ "mwoauthmanageconsumers-search-name": "bu isimde tüketiciler",
+ "mwoauthmanageconsumers-search-publisher": "bu kullanıcının tüketicileri",
+ "oauthlistconsumers": "Yetkilendirilen uygulamalar listesi",
+ "mwoauthlistconsumers-legend": "Yetkilendirilen uygulamalara göz atın",
+ "mwoauthlistconsumers-view": "detaylar",
+ "mwoauthlistconsumers-none": "Bu kriterleri karşılayan uygulama bulunamadı.",
+ "mwoauthlistconsumers-name": "Uygulama adı",
+ "mwoauthlistconsumers-version": "Tüketici sürümü",
+ "mwoauthlistconsumers-user": "Yayımcı",
+ "mwoauthlistconsumers-description": "Açıklama",
+ "mwoauthlistconsumers-wiki": "Uygulanabilir proje",
+ "mwoauthlistconsumers-callbackurl": "OAuth \"geri arama URL'si\"",
+ "mwoauthlistconsumers-callbackisprefix": "Tüketicinin isteklerde geri arama yapmasına izin verin ve yukarıdaki \"geri arama\" URL'sini gerekli önek olarak kullanın.",
+ "mwoauthlistconsumers-grants": "Geçerli hibeler",
+ "mwoauthlistconsumers-basicgrantsonly": "(yalnızca temel erişim)",
+ "mwoauthlistconsumers-status": "Durum",
+ "mwoauth-consumer-stage-any": "herhangi",
+ "mwoauthlistconsumers-status-proposed": "önerildi",
+ "mwoauthlistconsumers-status-approved": "onaylı",
+ "mwoauthlistconsumers-status-disabled": "devre dışı",
+ "mwoauthlistconsumers-status-rejected": "reddedildi",
+ "mwoauthlistconsumers-status-expired": "süresi doldu",
+ "mwoauthlistconsumers-navigation": "Gezinti:",
+ "mwoauthlistconsumers-update-link": "Tüketiciyi güncelle",
+ "mwoauthlistconsumers-manage-link": "Tüketiciyi yönet",
+ "mwoauthlistconsumers-grants-link": "Hibeleri yönet",
+ "mwoauthlistconsumers-rclink": "Bu uygulamanın son değişiklikleri",
+ "oauthmanagemygrants": "Bağlı uygulamaları yönet",
+ "mwoauthmanagemygrants-text": "Bu sayfada hesabınızda kullanılan uygulamaların listesi bulunmaktadır. Bu tür uygulamalar, söz konusu uygulamaya izin verdiğiniz ölçüde sizin adınıza hareket etmeye yetkilidir. Eğer bir uygulama sizin adınıza farklı kardeş projelere erişmek için yetkilendirildiği taktirde, uygulama ile ilgili bölümün altında diğer projeler için yetkilendirme ayarlarını da görebilirsiniz.\n\nBağlı uygulamalar OAuth protokolünü kullanarak, hesaplarınıza erişim sağlar. <span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth Bağlı uygulamalar hakkında daha fazla bilgi alın])</span>",
+ "mwoauthmanagemygrants-navigation": "Gezinti:",
+ "mwoauthmanagemygrants-showlist": "Bağlı uygulama listesi",
+ "mwoauthmanagemygrants-none": "Hesabınıza bağlı herhangi bir uygulama yoktur.",
+ "mwoauthmanagemygrants-user": "Yayıncı:",
+ "mwoauthmanagemygrants-description": "Açıklama",
+ "mwoauthmanagemygrants-wikiallowed": "İzin verilen projeler:",
+ "mwoauthmanagemygrants-grants": "Geçerli hibeler",
+ "mwoauthmanagemygrants-grantsallowed": "İzin verilen hibeler",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "İzin verilen geçerli hibeler:",
+ "mwoauthmanagemygrants-review": "erişimi yönet",
+ "mwoauthmanagemygrants-revoke": "erişimi kaldır",
+ "mwoauthmanagemygrants-grantaccept": "Verildi",
+ "mwoauthmanagemygrants-update-text": "Bir uygulamaya sizin adınıza işlem yapmak için verilen izinleri değiştirmek için aşağıdaki formu kullanın.",
+ "mwoauthmanagemygrants-revoke-text": "Bir uygulamanın sizin adınıza hareket etmesi için erişimi iptal etmek üzere aşağıdaki formu kullanın.",
+ "mwoauthmanagemygrants-confirm-legend": "Bağlı uygulama yönetimi",
+ "mwoauthmanagemygrants-update": "Hibeleri güncelle",
+ "mwoauthmanagemygrants-renounce": "Yetkisini kaldır",
+ "mwoauthmanagemygrants-action": "Değişiklik durumu:",
+ "mwoauthmanagemygrants-confirm-submit": "Erişim anahtarı durumunu güncelle",
+ "mwoauthmanagemygrants-success-update": "Bu uygulama için tercihleriniz güncellendi.",
+ "mwoauthmanagemygrants-success-renounce": "Uygulamanın hesabınıza erişimi iptal edildi.",
+ "mwoauthmanagemygrants-basic-tooltip": "Bu hibeyi neden güncelleyemiyorum? Bu hibe bağlı uygulamanıza düzgün çalışması için gereken temel izinleri verir. Bu bağlı uygulamanın bu haklara sahip olmasını istemiyorsanız, uygulamanın erişimini iptal etmelisiniz.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Bu hibeyi neden güncelleyemiyorum? Eğer bu bağlı uygulamanın bu haklara sahip olmasını istemiyorsanız, uygulamanının erişimini iptal etmelisiniz.",
+ "mwoauthmanagemygrants-editslink": "Bu uygulamadaki {{GENDER:$1|düzenlemeleriniz}}",
+ "mwoauthmanagemygrants-actionslink": "Bu uygulamadaki {{GENDER:$1|eylemleriniz}}",
+ "logentry-mwoauthconsumer-propose": "$1 OAuth tüketici {{GENDER:$2|önerdi}} (tüketici anahtarı $4)",
+ "logentry-mwoauthconsumer-update": "$1 OAuth tüketici {{GENDER:$2|güncelledi}} (tüketici anahtarı $4)",
+ "logentry-mwoauthconsumer-approve": "$1, OAuth tüketicisini $3 sayfası {{GENDER:$2|önerdi}} (tüketici anahtarı $4)",
+ "logentry-mwoauthconsumer-reject": "$1, OAuth tüketicisini $3 sayfası {{GENDER:$2|reddetti}} (tüketici anahtarı $4)",
+ "logentry-mwoauthconsumer-disable": "$1, OAuth tüketicisini $3 sayfası {{GENDER:$2|devre dışı bıraktı}} (tüketici anahtarı $4)",
+ "logentry-mwoauthconsumer-reenable": "$1, OAuth tüketicisini $3 sayfası {{GENDER:$2|yeniden etkinleştirdi}} (tüketici anahtarı $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 yalnızca sahip OAuth tüketicisi {{GENDER:$2|oluşturdu}} (tüketici anahtarı $4)",
+ "log-action-filter-mwoauthconsumer": "OAuth tüketici işleminin türü:",
+ "log-action-filter-mwoauthconsumer-approve": "OAuth tüketici onayı",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Yalnızca sahip OAuth tüketici oluşturma",
+ "log-action-filter-mwoauthconsumer-disable": "OAuth tüketici devre dışı bırakma",
+ "log-action-filter-mwoauthconsumer-propose": "OAuth tüketici teklifi",
+ "log-action-filter-mwoauthconsumer-reenable": "OAuth tüketici çözümlemesi",
+ "log-action-filter-mwoauthconsumer-reject": "OAuth tüketici reddi",
+ "log-action-filter-mwoauthconsumer-update": "OAuth tüketici güncellemesi",
+ "mwoauthconsumer-consumer-logpage": "OAuth tüketici günlüğü",
+ "mwoauthconsumer-consumer-logpagetext": "Kayıtlı OAuth tüketicilerinin onay, ret ve devre dışı bırakma günlüğü.",
+ "mwoauth-bad-request-missing-params": "Maalesef, bu bağlı uygulamayı yapılandırırken bir şeyler ters gitti. Uygulamanın geliştiricisine başvurun.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth eksik parametreleri, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Maalesef bir şeyler ters gitti, bu konuda yardım almak için uygulama yazarına başvurmanız gerekir.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Bilinmeyen URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Maalesef, bir şeyler ters gitti. Bu konuda yardım almak için uygulama yazarına [$1 kişi] ulaşmanız gerekir.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Bilinmeyen URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Bu yetki belirteci için onaylanmış bir hibe bulunamadı.",
+ "mwoauthdatastore-request-token-not-found": "Maalesef, bu uygulamayı bağlarken bir şeyler ters gitti.\nGeri dönün ve hesabınızı tekrar bağlamayı deneyin veya uygulama yazarıyla iletişim kurun.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth anahtarı bulunamadı, $1</span>",
+ "mwoauthdatastore-callback-not-found": "OAuth geri arama URL'si önbellekte bulunamadı. Bu muhtemelen uygulamanın sunucuya nasıl istekte bulunduğuna dair bir hatadır.",
+ "mwoauthdatastore-request-token-already-used": "Bu istek zaten tamamlandı ve yeniden gönderilemez.\nUygulamaya geri dönün ve hesabınızı tekrar bağlamayı deneyin veya uygulama yazarıyla iletişim kurun.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth anahtarı zaten kullanılmış, $1</span>",
+ "mwoauthdatastore-bad-token": "İsteğinizle eşleşen anahtar bulunamadı.",
+ "mwoauthdatastore-bad-source-ip": "İstek geçersiz bir IP adresinden geldi.",
+ "mwoauthdatastore-bad-verifier": "Sağlanan doğrulama kodu geçerli değil.",
+ "mwoauthdatastore-invalid-token-type": "İstenen anahtar türü geçersiz.",
+ "mwoauthgrants-general-error": "OAuth isteğinizde bir hata oluştu.",
+ "mwoauthserver-bad-consumer": "\"$1\", Bağlı Uygulama olarak onaylanmadı. Yardım için uygulama yazarından [$2 iletişime] geçin.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Bağlı OAuth uygulaması onaylanmadı, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Maalesef, bu uygulamayı bağlarken bir şeyler ters gitti.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Bilinmeyen OAuth anahtarı, $1</span>",
+ "mwoauthserver-insufficient-rights": "Hesabınızın Bağlı Uygulamaları kullanmasına izin verilmiyor, nedenini öğrenmek için site yöneticinize başvurun.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Yetersiz OAuth kullanıcı hakları, $1</span>",
+ "mwoauthserver-invalid-request-token": "İsteğinizde geçersiz anahtar.",
+ "mwoauthserver-invalid-user": "Bağlı Siteleri bu sitede kullanmak için tüm projelerde bir hesabınızın olması gerekir. Tüm projelerde bir hesabınız olduğunda, tekrar \"$1\" bağlamayı deneyebilirsiniz.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Birleşik giriş gerekli, $2</span>",
+ "mwoauthserver-consumer-no-secret": "Maalesef, bu uygulamayı bağlarken bir şeyler ters gitti.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Tüketicinin gizli anahtarı yok, $1</span>",
+ "mwoauthserver-consumer-owner-only": "\"$1\" yalnızca sahiplere bağlı bir Bağlı Uygulama. Erişim anahtarı almak için, [[$2]] bakınız.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Tüketici yalnızca sahip, $3</span>",
+ "mwoauth-invalid-authorization-title": "OAuth yetkilendirme hatası",
+ "mwoauth-invalid-authorization": "İsteğinizdeki yetkilendirme başlıkları geçerli değil: $1",
+ "mwoauth-invalid-authorization-wrong-wiki": "İsteğinizdeki yetkilendirme başlıkları $1 için geçerli değil",
+ "mwoauth-invalid-authorization-invalid-user": "İsteğinizdeki yetkilendirme başlıkları burada bulunmayan bir kullanıcı içindir",
+ "mwoauth-invalid-authorization-wrong-user": "İsteğinizdeki yetkilendirme başlıkları farklı bir kullanıcı için",
+ "mwoauth-invalid-authorization-not-approved": "Bağlanmaya çalıştığınız uygulama yanlış ayarlanmış gibi görünüyor. Yardım için \"$1\" yazarı ile iletişim kurun.",
+ "mwoauth-invalid-authorization-blocked-user": "İsteğinizdeki yetkilendirme başlıkları engellenen bir kullanıcı içindir",
+ "mwoauth-form-description-allwikis": "Merahaba $1,\n\nİsteğinizi tamamlamak için '''$2''', bu sitenin tüm projelerinde sizin adınıza aşağıdaki işlemleri gerçekleştirmek için izne ihtiyaç duyuyor:\n\n$4",
+ "mwoauth-form-description-onewiki": "Merhaba $1,\n\nİsteğinizi tamamlamak için '''$2''' sizin adınıza ''$4'' üzerinde aşağıdaki işlemleri gerçekleştirmek için izne ihtiyaç duyuyor:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Merhaba $1,\n\nİsteğinizi tamamlamak için '''$2''' adının bu sitenin tüm projeleriyle ilgili bilgilere erişmek için izne ihtiyacı var. Hesabınızda hiçbir değişiklik yapılmayacak.",
+ "mwoauth-form-description-onewiki-nogrants": "Merhaba $1,\n\nTalebinizi tamamlamak için '''$2''' sizin adınıza ''$4'' ile ilgili bilgilere erişmek için izne ihtiyaç duyuyor. Hesabınızda hiçbir değişiklik yapılmayacak.",
+ "mwoauth-form-description-allwikis-privateinfo": "Merhaba $1,\n\nİsteğinizi tamamlamak için, sitenin tüm projelerinde gerçek adınız ve e-posta adresiniz de dahil olmak üzere sizinle ilgili bilgilere erişmek için '''$2''' iznine ihtiyaç vardır. Hesabınızda hiçbir değişiklik yapılmayacak.",
+ "mwoauth-form-description-onewiki-privateinfo": "Merhaba $1,\n\nİsteğinizi tamamlamak için, '''$2''', gerçek adınız ve e-posta adresiniz de dahil olmak üzere, ''$4'' üzerindeki bilgilere erişmek için izne ihtiyaç duyuyor. Hesabınızda hiçbir değişiklik yapılmayacak.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Merhaba $1,\n\nİsteğinizi tamamlamak için, '''$2''' e-posta adresiniz de dahil olmak üzere bu sitenin tüm projelerinde sizin hakkınızdaki bilgilere erişmek için izin almalıdır. Hesabınızda hiçbir değişiklik yapılmayacak.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Merhaba $1,\n\nİsteğinizi tamamlamak için, '''$2''', ''$4'' üzerindeki e-posta adresiniz de dahil olmak üzere bilgilere erişmek için izin almalıdır. Hesabınızda hiçbir değişiklik yapılmayacak.",
+ "mwoauth-form-button-approve": "İzin ver",
+ "mwoauth-form-button-cancel": "İptal",
+ "mwoauth-error": "Uygulama Bağlantı Hatası",
+ "mwoauth-grants-heading": "İstenen izinler:",
+ "mwoauth-grants-nogrants": "Uygulama herhangi bir izin istemedi.",
+ "mwoauth-acceptance-cancelled": "\"$1\" hesabınızın hesabınıza erişmesine izin vermemeyi seçtiniz. Erişimine izin vermediğiniz sürece \"$1\" çalışmaz. Bağlı uygulamalarınıza \"$1\" veya [[Special:OAuthManageMyGrants|yönete]] geri dönebilirsiniz.",
+ "mwoauth-granttype-normal": "Özel izinler için yetki talep et.",
+ "grant-mwoauth-authonly": "Yalnızca kullanıcı kimliği doğrulaması, sayfaları okuma veya kullanıcının adına işlem yapma yeteneği yoktur.",
+ "grant-mwoauth-authonlyprivate": "Kullanıcı kimliği doğrulaması yalnızca gerçek ad ve e-posta adresine erişimle, sayfaları okuyamaz veya kullanıcının adına hareket edemez.",
+ "mwoauth-listgrants-extra-summary": "== OAuth'a özel hibeler ==\n\nBu ek hibeler OAuth tüketicileri için geçerlidir.",
+ "mwoauth-oauth-exception": "OAuth protokolünde bir hata oluştu: $1",
+ "mwoauth-callback-not-oob": "oauth_callback ayarlanmalı ve \"oob\" olarak ayarlanmalıdır (büyük/küçük harfe duyarlı)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback ayarlanmalı ve \"oob\" (büyük/küçük harfe duyarlı) olarak ayarlanmalıdır veya yapılandırılmış geri arama verilen geri aramanın öneki olmalıdır.",
+ "right-mwoauthproposeconsumer": "Yeni OAuth tüketicileri önerin",
+ "right-mwoauthupdateownconsumer": "Kontrol ettiğiniz OAuth tüketicilerini güncelleyin",
+ "right-mwoauthmanageconsumer": "OAuth tüketicilerini yönet",
+ "right-mwoauthsuppress": "OAuth tüketicilerini gözetleyin",
+ "right-mwoauthviewsuppressed": "Gözetlenmiş OAuth tüketicilerini görüntüle",
+ "right-mwoauthviewprivate": "Özel OAuth verilerini görüntüle",
+ "right-mwoauthmanagemygrants": "OAuth bağışlarını yönet",
+ "action-mwoauthmanageconsumer": "OAuth tüketicilerini yönet",
+ "action-mwoauthsuppress": "OAuth tüketicilerini gözetle",
+ "action-mwoauthmanagemygrants": "OAuth hibelerinizi yönetin",
+ "action-mwoauthproposeconsumer": "yeni OAuth tüketicileri önerin",
+ "action-mwoauthupdateownconsumer": "kontrol ettiğiniz OAuth tüketicilerini güncelleyin",
+ "action-mwoauthviewprivate": "özel OAuth verilerini görüntüle",
+ "action-mwoauthviewsuppressed": "Bastırılmış OAuth kullanıcılarını görüntüle",
+ "mwoauth-tag-reserved": "<code>OAuth CID:</code> ile başlayan etiketler OAuth tarafından kullanılmak üzere ayrılmıştır.",
+ "mwoauth-botpasswords-note": "<strong>Not:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> bot parolalarından daha güvenlidir ve bot her desteklediğinde tercih edilmelidir.",
+ "mwoauth-api-module-disabled": "\"$1\" modülü OAuth ile kullanılamaz.",
+ "echo-category-title-oauth-owner": "OAuth geliştirme",
+ "echo-pref-tooltip-oauth-owner": "Oluşturduğum OAuth uygulamarıyla ilgili olaylar hakkında bana bildirim gönder.",
+ "echo-category-title-oauth-admin": "OAuth yönetimi",
+ "echo-pref-tooltip-oauth-admin": "OAuth uygulamalarını inceleme ile ilgili olayları bana bildir.",
+ "notification-oauth-app-propose-title": "$1 yeni bir OAuth uygulaması önerdi: $2",
+ "notification-oauth-app-update-title": "$1 yeni bir OAuth uygulaması güncelledi: $2",
+ "notification-oauth-app-approve-title": "$1 OAuth uygulamanızı {{GENDER:$3|onayladı}} ($2)",
+ "notification-oauth-app-reject-title": "$1 OAuth uygulamanızı {{GENDER:$3|reddetti}} ($2)",
+ "notification-oauth-app-disable-title": "$1 OAuth uygulamanızı {{GENDER:$3|devre dışı bıraktı}} ($2)",
+ "notification-oauth-app-reenable-title": "$1 OAuth uygulamanızı {{GENDER:$3|yeniden etkinleştirdi}} ($2)",
+ "notification-oauth-app-propose-subject": "$1, {{SITENAME}} için yeni bir OAuth uygulaması önerdi",
+ "notification-oauth-app-update-subject": "$1, {{SITENAME}} için yeni bir OAuth uygulaması güncelledi",
+ "notification-oauth-app-approve-subject": "$1, {{SITENAME}} üzerindeki OAuth uygulamanızı {{GENDER:$3|onayladı}}",
+ "notification-oauth-app-reject-subject": "$1, {{SITENAME}} üzerindeki OAuth uygulamanızı {{GENDER:$3|reddetti}}",
+ "notification-oauth-app-disable-subject": "$1, {{SITENAME}} üzerindeki OAuth uygulamanızı {{GENDER:$3|devre dışı bıraktı}}",
+ "notification-oauth-app-reenable-subject": "$1, {{SITENAME}} üzerindeki OAuth uygulamanızı {{GENDER:$3|yeniden etkinleştirdi}}",
+ "notification-oauth-app-propose-primary-link": "Uygulamayı incele",
+ "notification-oauth-app-update-primary-link": "Uygulamayı incele",
+ "notification-oauth-app-approve-primary-link": "Uygulamayı görüntüle",
+ "notification-oauth-app-reject-primary-link": "Uygulamayı görüntüle",
+ "notification-oauth-app-disable-primary-link": "Uygulamayı görüntüle",
+ "notification-oauth-app-reenable-primary-link": "Uygulamayı görüntüle",
+ "notification-oauth-app-body": "Sebep: $1",
+ "mwoauth-oauth-version": "OAuth protokol sürümü",
+ "mwoauth-oauth2-is-confidential": "Müşteri gizlidir",
+ "mwoauth-oauth2-is-confidential-help": "Güvenilir bir müşteri, kullanıcı parolalarını dünyaya karşı gizli olarak tutabilen bir uygulamadır. Gizli olmayan müşteriler daha az güvenli haldedir",
+ "mwoauth-oauth2-granttypes": "İzin verilen OAuth2 hibe türleri",
+ "mwoauth-oauth2-granttype-auth-code": "Yetki kodu",
+ "mwoauth-oauth2-granttype-refresh-token": "Simgeyi yenile",
+ "mwoauth-oauth2-granttype-client-credentials": "Müşteri kimlik bilgileri",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Giriş jetonu oluşturulamıyor, kullanıcı bu giriş jetonunun kullanımını onaylamadı",
+ "mwoauth-oauth2-error-user-approval-deny": "Kullanıcı müşteri uygulamasından gelen talebi geri çevirdi",
+ "mwoauth-oauth-unsupported-version": "$1 OAuth sürümü için bu bitiş noktasına izin verilmiyor",
+ "mwoauth-oauth2-error-unauthorized-scope": "Bu uygulama için \"$1\" kapsamına izin verilmiyor",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Yalnızca sahip istemcilerin client_credentials kullanmalarına izin verilmelidir",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Erişim anahtarı alınamadı: $1",
+ "mwoauth-oauth2-error-server-error": "Doğrulama sunucusu talebin yerine getirilmesini engelleyen beklenmedik bir durumla karşılaştı.",
+ "mwoauth-oauth2-error-invalid-request": "İstekte gerekli bir parametre eksik, geçersiz bir parametre değeri içeriyor, bir kereden fazla bir parametre içeriyor veya aksi halde hatalı biçimlendirilmiş.",
+ "mwoauth-oauth2-error-unauthorized-client": "İstemcinin bu yöntemi kullanarak bir yetkilendirme kodu isteme yetkisi yok.",
+ "mwoauth-oauth2-error-access-denied": "Kaynak sahibi ya da doğrulama sunucusu talebi reddetti.",
+ "mwoauth-oauth2-error-unsupported-response-type": "Doğrulama sunucusu bu yöntemi kullanarak elde edilen bir doğrulama kodunu desteklemiyor.",
+ "mwoauth-oauth2-error-invalid-scope": "İstenen kapsam geçersiz, bilinmiyor veya hatalı biçimlendirilmiş.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "Yetkilendirme sunucusu şu anda sunucunun geçici olarak aşırı yüklenmesi veya bakımı nedeniyle isteği işleyemiyor.",
+ "mwoauth-oauth2-error-invalid-client": "İstemci kimlik doğrulaması başarısız oldu (ör. Bilinmeyen istemci, istemci kimlik doğrulaması dahil değil veya desteklenmeyen kimlik doğrulama yöntemi)",
+ "mwoauth-oauth2-error-request-not-verified": "Doğrulanmış özellikleri talebin doğrulanmasından önce kurtarma girişimi",
+ "mwoauth-oauth2-invalid-access-token": "Geçersiz giriş jetonu",
+ "mwoauth-consumer-access-no-user": "Tüketici onayı geçerli bir kullanıcıya bağlı olmalı, kimlik numarası 0 olan kullanıcı verilmiş olmalıdır",
+ "mwoauth-login-required-reason": "Uygulamalara erişme yetkisi vermek için {{SITENAME}} hesabınıza giriş yapmanız gerekiyor.",
+ "mwoauthconsumer-consumer-view": "Bu tüketiciyi görüntüle",
+ "mwoauthconsumer-application-view": "Bu uygulamayı görüntüle"
+}
diff --git a/OAuth/i18n/tt-cyrl.json b/OAuth/i18n/tt-cyrl.json
new file mode 100644
index 00000000..fa69f235
--- /dev/null
+++ b/OAuth/i18n/tt-cyrl.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ерней",
+ "Ильнар"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Өстәмә кушымталар",
+ "mwoauth-prefs-managegrantslink": "{{PLURAL:$1|$1 тоташтырган кушымта|0=тоташтырган кушымта}} белән идарә итү",
+ "mwoauth-consumer-name": "Кушымта исеме:",
+ "mwoauth-consumer-stage-rejected": "кире кагылды",
+ "mwoauthmanageconsumers-reject": "Кире кагылды",
+ "mwoauthlistconsumers-status-rejected": "кире кагылды",
+ "oauthmanagemygrants": "Кушымталар белән эшләү"
+}
diff --git a/OAuth/i18n/uk.json b/OAuth/i18n/uk.json
new file mode 100644
index 00000000..39887e7a
--- /dev/null
+++ b/OAuth/i18n/uk.json
@@ -0,0 +1,335 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ahonc",
+ "Andriykopanytsia",
+ "Base",
+ "Macofe",
+ "Movses",
+ "Olion",
+ "Piramidion",
+ "Vlad5250",
+ "Ата"
+ ]
+ },
+ "mwoauth-desc": "Дозволяє використовувати OAuth 1.0a та OAuth 2.0 для API авторизації",
+ "mwoauth-nosubpage-explanation": "OAuth — це механізм, який дозволяє зовнішнім програмам ідентифікувати користувача сайту {{SITENAME}} чи робити якісь дії від імені такого користувача після отримання від нього дозволу на це.\n\nЩоб ця сторінка могла щось зробити, потрібно більше параметрів. Якщо ви потрапили сюди із зовнішньої програми, це, найімовірніше, сталося через якусь помилку в такій програмі; вам варто зв'язатися з її автором.",
+ "mwoauth-verified": "Цій програмі зараз дозволений доступ до Медіавікі від вашого імені.\n\nДля завершення процесу надайте це перевірене значення програмі: '''$1'''",
+ "mwoauth-db-readonly": "Бази даних OAuth тимчасово заблокована. Будь ласка, спробуйте знову за кілька хвилин.",
+ "mwoauth-missing-field": "Відсутнє значення для поля \"$1\"",
+ "mwoauth-invalid-field": "Неприпустиме значення для поля \"$1\"",
+ "mwoauth-invalid-field-generic": "Надано неприпустиме значення",
+ "mwoauth-field-hidden": "(ця інформація прихована)",
+ "mwoauth-field-private": "(ця інформація є конфіденційною)",
+ "mwoauth-prefs-managegrants": "Підключені додатки:",
+ "mwoauth-prefs-managegrantslink": "Управляти {{PLURAL:$1|$1 з'єднаною програмою|$1 з'єднаними програмами|0=з'єднаними програмами}}",
+ "mwoauth-consumer-allwikis": "Всі проекти на цьому сайті",
+ "mwoauth-consumer-key": "Ключ споживача:",
+ "mwoauth-consumer-name": "Назва програми:",
+ "mwoauth-consumer-version": "Споживча версія:",
+ "mwoauth-consumer-user": "Видавець:",
+ "mwoauth-consumer-stage": "Поточний статус:",
+ "mwoauth-consumer-email": "Контактна адреса електронної пошти:",
+ "mwoauth-consumer-email-help": "Видимо лише для тих, хто затверджує нових споживачів",
+ "mwoauth-consumer-owner-only-label": "Лише для власника:",
+ "mwoauth-consumer-owner-only": "Цього споживача може використовувати тільки $1.",
+ "mwoauth-consumer-owner-only-help": "Вибір цієї опції зробить так, що споживач буде автоматично прийнятий для використання та затверджений за $1. Ним не зможе користуватися будь-який інший користувач, і звична процедура авторизації не працюватиме. Дії, виконані цим споживачем, не позначатимуться тегами.",
+ "mwoauth-consumer-description": "Опис програми:",
+ "mwoauth-consumer-callbackurl": "URL «зворотного виклику» OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Дозволяти споживачеві вказати зворотний виклик у запиті використовувати URL «зворотного виклику» вище як обов'язковий префікс.",
+ "mwoauth-consumer-granttypes": "Типи доступу, які запитуються:",
+ "mwoauth-consumer-grantsneeded": "Придатні гранти:",
+ "mwoauth-consumer-required-grant": "Застосовні до споживача",
+ "mwoauth-consumer-wiki": "Придатний проект:",
+ "mwoauth-consumer-wiki-thiswiki": "Поточний проект ($1)",
+ "mwoauth-consumer-restrictions": "Обмеження на використання:",
+ "mwoauth-consumer-restrictions-json": "Обмеження на користування (JSON):",
+ "mwoauth-consumer-rsakey": "Відкритий ключ RSA (необов'язково):",
+ "mwoauth-consumer-rsakey-help": "Введіть відкритий ключ для використання методу сигнатури RSA-SHA1. Залиште порожнім, щоб використовувати HMAC-SHA1 з випадковим секретом. Якщо Ви не впевнені, що краще використати, залиште порожнім.",
+ "mwoauth-consumer-secretkey": "Секретний маркер споживача:",
+ "mwoauth-consumer-accesstoken": "Маркер доступу:",
+ "mwoauth-consumer-reason": "Причина:",
+ "mwoauth-consumer-developer-agreement": "Надсилаючи даний додаток, Ви погоджуєтеся, що ми залишаємо за собою право вимкнути Ваш додаток, вилучити або обмежити доступ до цього сайту Вам чи Вашому додатку, і здійснювати будь-які інші, на нашу думку, необхідні дії, якщо ми вважаємо, за власним судження, що Ви або Ваш додаток порушуєте будь-які політики, правила чи настанови даного сайту. Ми можемо змінити цю політику додатків у будь-який час без попереднього повідомлення, за власним розсудом і так, як ми вважатимемо за необхідне. Ваше подальше використання OAuth означає прийняття цих змін.",
+ "mwoauth-consumer-email-unconfirmed": "Ваша адреса електронної пошти облікового запису ще не підтверджена.",
+ "mwoauth-consumer-email-mismatched": "Вказана адреса електронної пошти повинна відповідати вашому обліковому запису.",
+ "mwoauth-consumer-alreadyexists": "Споживач з таким поєднанням імен/версії/видавця вже існує",
+ "mwoauth-consumer-alreadyexistsversion": "Споживач з таким поєднанням імені/видавця вже існує з тою самою чи новішою версією (\"$1\")",
+ "mwoauth-consumer-not-accepted": "Не вдалося оновити відомості для запиту на очікуваного споживача",
+ "mwoauth-consumer-not-proposed": "Споживач не пропонується в даний час",
+ "mwoauth-consumer-not-disabled": "Споживач в даний час невимкнений",
+ "mwoauth-consumer-not-approved": "Споживач незатверджений (це може було вимкнено)",
+ "mwoauth-missing-consumer-key": "Немає наданого ключа споживача.",
+ "mwoauth-invalid-consumer-key": "Не існує споживача з даним ключем.",
+ "mwoauth-invalid-access-token": "Немає маркера доступу з даним ключем.",
+ "mwoauth-invalid-access-wrongwiki": "Споживач може використовуватися тільки на проекті \"$1\".",
+ "mwoauth-consumer-conflict": "Хтось змінив параметри даного споживача, якого ви дивилися. Будь ласка, спробуйте ще раз. Ви можете перевірити журнал змін.",
+ "mwoauth-consumer-grantshelp": "Кожен грант надає доступ до перерахованих прав користувача, які вже має обліковий запис користувача. Подивіться на [[Special:ListGrants|таблицю грантів]] для отримання додаткової інформації.",
+ "mwoauth-consumer-stage-proposed": "запропоновано",
+ "mwoauth-consumer-stage-rejected": "відхилено",
+ "mwoauth-consumer-stage-expired": "застаріле",
+ "mwoauth-consumer-stage-approved": "затверджено",
+ "mwoauth-consumer-stage-disabled": "вимкнено",
+ "mwoauth-consumer-stage-suppressed": "пригнічено",
+ "oauthconsumerregistration": "Реєстрація споживача OAuth",
+ "mwoauthconsumerregistration-navigation": "Навігація:",
+ "mwoauthconsumerregistration-propose": "Запропонувати нового споживача",
+ "mwoauthconsumerregistration-list": "Мій список споживачів",
+ "mwoauthconsumerregistration-main": "Головна",
+ "mwoauthconsumerregistration-propose-text": "Розробники можуть використовувати форму нижче, щоб запропонувати нового споживача OAuth (див. [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth extension documentation] за подробицями). Після надсилання цієї форми Ви отримаєте маркер для Вашої програми, який буде використовуватися нею при ідентифікації на MediaWiki. Адміністратор OAuth повинен затвердити Вашу програму, перш ніж вона буде авторизована іншими користувачами.\n\nКілька рекомендацій і зауважень:\n* Постарайтеся використовувати якомога менше дозволів за можливості. Уникайте дозволів, які насправді не потрібні зараз.\n* Версії мають форму «major.minor.release» (останні дві необов'язкові) і вони збільшуються, якщо необхідні зміни дозволів.\n* Будь ласка, вкажіть відкритий ключ RSA (у форматі PEM), якщо можливо; в іншому випадку (менш безпечно) таємний маркер повинен використовуватися.\n* Ви можете використовувати ідентифікатор проекту, аби обмежувати споживача в одному проекті на цьому сайті (використовуйте «*» для всіх проектів).",
+ "mwoauthconsumerregistration-update-text": "Використовуйте форму нижче, щоб оновити аспекти споживача OAuth, які ви контролюєте.\n\nВсі значення тут будуть переписувати будь-які попередні. Не залишайте порожні поля, якщо ви не маєте наміру вилучити ці значення.",
+ "mwoauthconsumerregistration-maintext": "Ця сторінка дозволяє розробникам пропонувати та оновлювати клієнтські програми OAuth у реєстрі сайту.\n\nЗвідси ви можете:\n* [[Special:OAuthConsumerRegistration/propose|Запитувати маркер для нового клієнта]].\n* [[Special:OAuthConsumerRegistration/list|Управляти вашими наявними клієнтами]].\n\nFor more information about OAuth, please see the [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension: документація про розширення OAuth].",
+ "mwoauthconsumerregistration-propose-legend": "Нова програма OAuth споживача",
+ "mwoauthconsumerregistration-update-legend": "Оновити програму споживача OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Запропонувати споживача",
+ "mwoauthconsumerregistration-update-submit": "Оновити споживача",
+ "mwoauthconsumerregistration-none": "Ви не контролюєте жодного споживача OAuth.",
+ "mwoauthconsumerregistration-name": "Споживач",
+ "mwoauthconsumerregistration-user": "Видавець",
+ "mwoauthconsumerregistration-description": "Опис",
+ "mwoauthconsumerregistration-email": "Адреса ел. пошти",
+ "mwoauthconsumerregistration-consumerkey": "Ключ споживача",
+ "mwoauthconsumerregistration-stage": "Статус",
+ "mwoauthconsumerregistration-lastchange": "Остання зміна",
+ "mwoauthconsumerregistration-manage": "керування",
+ "mwoauthconsumerregistration-resetsecretkey": "Очистити секретний ключ до нового значення",
+ "mwoauthconsumerregistration-proposed": "Ваш запит споживача OAuth був отриманий.\n\nВам призначений маркер споживача ''$1''' і таємний маркер '''$2'''. \"Будь ласка, запишіть їх для використання в майбутньому.\"",
+ "mwoauthconsumerregistration-created-owner-only": "Вашого споживача OAuth було створено.\n\nОсь Ваші маркери:\n; Маркер споживача: $1\n; Секрет споживача: $2\n; Маркер доступу: $3\n; Секрет доступу: $4\n<em>Будь ласка, запишіть їх для майбутнього використання.</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "Ваш клієнт OAuth 2.0 створено.\n\nВаші маркери:\n; Ключ клієнтської програми: $1\n; Секрет клієнтської програми: $2\n; Маркери доступу: $3\n;<em>Будь ласка, запишіть їх для майбутнього використання.</em>",
+ "mwoauthconsumerregistration-updated": "Ваш реєстр споживача OAuth оновлено.",
+ "mwoauthconsumerregistration-secretreset": "Вам призначений секретний маркер споживача '''$1'''. „Будь ласка, запишіть це на майбутнє“.",
+ "mwoauthconsumerregistration-secretreset-owner-only": "Ваші маркери споживача OAuth було скинуто. Ось нові маркери:\n; Маркер споживача: $1\n; Секрет споживача: $2\n; Маркер доступу: $3\n; Секрет доступу: $4\n<em>Будь ласка, запишіть їх для майбутнього використання.</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "Ваші маркери споживача OAuth 2.0 було скинуто. Ось нові маркери:\n; Маркер споживача: $1\n; Секрет споживача: $2\n; Маркер доступу: $3\n<em>Будь ласка, запишіть їх для майбутнього використання.</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "Ви повинні підтвердити свою електронну адресу, перш ніж створювати програми OAuth.\nБудь ласка, встановіть і підтвердіть свою електронну адресу через свої [[Special:Preferences|налаштування]].",
+ "oauthmanageconsumers": "Управління споживачами OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Ви повинні увійти в систему для доступу до цієї сторінки.",
+ "mwoauthmanageconsumers-type": "Черги:",
+ "mwoauthmanageconsumers-showproposed": "Запропоновані запити",
+ "mwoauthmanageconsumers-showrejected": "Відхилені запити",
+ "mwoauthmanageconsumers-showexpired": "Застарілі запити",
+ "mwoauthmanageconsumers-linkproposed": "пропоновані запити",
+ "mwoauthmanageconsumers-linkrejected": "відхилені запити",
+ "mwoauthmanageconsumers-linkexpired": "застарілі запити",
+ "mwoauthmanageconsumers-linkapproved": "схвалені запити",
+ "mwoauthmanageconsumers-linkdisabled": "відключені запити",
+ "mwoauthmanageconsumers-main": "Головна",
+ "mwoauthmanageconsumers-maintext": "Ця сторінка призначена для обробки запитів на створення OAuth-споживачів (див. http://oauth.net) і управління дійсними OAuth-споживачами.",
+ "mwoauthmanageconsumers-queues": "Оберіть чергу підтвердження споживачів нижче:",
+ "mwoauthmanageconsumers-q-proposed": "Черга пропонованих запитів споживачів",
+ "mwoauthmanageconsumers-q-rejected": "Черга відхилених запитів споживачів",
+ "mwoauthmanageconsumers-q-expired": "Черга протермінованих запитів споживачів",
+ "mwoauthmanageconsumers-lists": "Виберіть статус споживача з списку нижче:",
+ "mwoauthmanageconsumers-l-approved": "Список на даний час затверджених споживачів",
+ "mwoauthmanageconsumers-l-disabled": "Перелік наразі вимкнених споживачів",
+ "mwoauthmanageconsumers-none-proposed": "Немає запропонованих споживачів в цьому списку.",
+ "mwoauthmanageconsumers-none-rejected": "Немає запропонованих споживачів в цьому списку.",
+ "mwoauthmanageconsumers-none-expired": "Немає запропонованих споживачів в цьому списку.",
+ "mwoauthmanageconsumers-none-approved": "Жодний споживач не відповідає цьому критерію.",
+ "mwoauthmanageconsumers-none-disabled": "Жодний споживач не відповідає цьому критерію.",
+ "mwoauthmanageconsumers-name": "Споживач",
+ "mwoauthmanageconsumers-user": "Видавець",
+ "mwoauthmanageconsumers-description": "Опис",
+ "mwoauthmanageconsumers-email": "Контактна адреса електронної пошти",
+ "mwoauthmanageconsumers-consumerkey": "Ключ споживача",
+ "mwoauthmanageconsumers-lastchange": "Остання зміна",
+ "mwoauthmanageconsumers-review": "перевірка/керування",
+ "mwoauthmanageconsumers-confirm-text": "Використовуйте цю форму, щоб затвердити, відхилити, вимкнути або повторно увімкнути цього споживача.",
+ "mwoauthmanageconsumers-confirm-legend": "Управління споживачем OAuth",
+ "mwoauthmanageconsumers-action": "Змінити статус:",
+ "mwoauthmanageconsumers-approve": "Затверджено",
+ "mwoauthmanageconsumers-reject": "Відхилено",
+ "mwoauthmanageconsumers-rsuppress": "Відхилено і пригнічено",
+ "mwoauthmanageconsumers-disable": "Вимкнено",
+ "mwoauthmanageconsumers-dsuppress": "Вимкнено і пригнічено",
+ "mwoauthmanageconsumers-reenable": "Затверджено",
+ "mwoauthmanageconsumers-reason": "Причина:",
+ "mwoauthmanageconsumers-confirm-submit": "Оновити статус споживача",
+ "mwoauthmanageconsumers-success-approved": "Запит був схвалений.",
+ "mwoauthmanageconsumers-success-rejected": "Запит був відхилений.",
+ "mwoauthmanageconsumers-success-disabled": "Споживач вже вимкнений.",
+ "mwoauthmanageconsumers-success-reanable": "Споживач вже повторно увімкнений.",
+ "mwoauthmanageconsumers-search-name": "споживачі з цим іменем",
+ "mwoauthmanageconsumers-search-publisher": "споживачі даного користувача",
+ "oauthlistconsumers": "Список програм OAuth",
+ "mwoauthlistconsumers-legend": "Перегляд програм OAuth",
+ "mwoauthlistconsumers-view": "подробиці",
+ "mwoauthlistconsumers-none": "Не знайдено жодної програми за цим критерієм.",
+ "mwoauthlistconsumers-name": "Назва програми",
+ "mwoauthlistconsumers-version": "Споживча версія",
+ "mwoauthlistconsumers-user": "Видавець",
+ "mwoauthlistconsumers-description": "Опис",
+ "mwoauthlistconsumers-wiki": "Придатний проект",
+ "mwoauthlistconsumers-callbackurl": "URL «зворотного виклику» OAuth",
+ "mwoauthlistconsumers-callbackisprefix": "Дозволяти споживачеві вказати зворотний виклик у запиті використовувати URL «зворотного виклику» вище як обов'язковий префікс.",
+ "mwoauthlistconsumers-grants": "Застосовні ґранти",
+ "mwoauthlistconsumers-basicgrantsonly": "(тільки для базового доступу)",
+ "mwoauthlistconsumers-status": "Статус",
+ "mwoauth-consumer-stage-any": "будь-яка",
+ "mwoauthlistconsumers-status-proposed": "запропоновано",
+ "mwoauthlistconsumers-status-approved": "затверджено",
+ "mwoauthlistconsumers-status-disabled": "вимкнено",
+ "mwoauthlistconsumers-status-rejected": "відхилено",
+ "mwoauthlistconsumers-status-expired": "застаріле",
+ "mwoauthlistconsumers-navigation": "Навігація:",
+ "mwoauthlistconsumers-update-link": "Оновити споживача",
+ "mwoauthlistconsumers-manage-link": "Керувати споживачем",
+ "mwoauthlistconsumers-grants-link": "Керувати грантами",
+ "mwoauthlistconsumers-rclink": "Останні зміни, зроблені цим застосунком",
+ "oauthmanagemygrants": "Управління підключеними програмами",
+ "mwoauthmanagemygrants-text": "На цій сторінці перераховані всі програми, які можуть використовувати ваш обліковий запис. Для будь-яких таких програм сфера їхнього доступу обмежена дозволами, наданими програмі, коли ви уповноважили її діяти від вашого імені. Якщо ви окремо уповноважили програму для доступу до різних сестринських проектів від вашого імені, то ви побачите окремі налаштування для кожного такого проекту, нижче.",
+ "mwoauthmanagemygrants-navigation": "Навігація:",
+ "mwoauthmanagemygrants-showlist": "Список підключених програм",
+ "mwoauthmanagemygrants-none": "Жодна програма не підключена до вашого облікового запису.",
+ "mwoauthmanagemygrants-user": "Видавець:",
+ "mwoauthmanagemygrants-description": "Опис",
+ "mwoauthmanagemygrants-wikiallowed": "Дозволено на проекті:",
+ "mwoauthmanagemygrants-grants": "Застосовні ґранти",
+ "mwoauthmanagemygrants-grantsallowed": "Ґранти, які дозволили",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Застосовні гранти дозволено:",
+ "mwoauthmanagemygrants-review": "управління доступом",
+ "mwoauthmanagemygrants-revoke": "скасувати доступ",
+ "mwoauthmanagemygrants-grantaccept": "Надано",
+ "mwoauthmanagemygrants-update-text": "Використовуйте форму нижче, щоб змінювати дозволи надані програмі діяти від Вашого імені.",
+ "mwoauthmanagemygrants-revoke-text": "Використовуйте форму нижче, щоб скасувати доступ для програми, щоб діяти від вашого імені.",
+ "mwoauthmanagemygrants-confirm-legend": "Управління підключеною програмою",
+ "mwoauthmanagemygrants-update": "Оновити ґранти",
+ "mwoauthmanagemygrants-renounce": "Скасувати авторизацію",
+ "mwoauthmanagemygrants-action": "Змінити статус:",
+ "mwoauthmanagemygrants-confirm-submit": "Оновити стан маркера доступу",
+ "mwoauthmanagemygrants-success-update": "Ваші налаштування цього застосунку було оновлено.",
+ "mwoauthmanagemygrants-success-renounce": "Доступ програми до Вашого облікового запису був відкликаний.",
+ "mwoauthmanagemygrants-basic-tooltip": "Чому я не можу оновити цей грант? Він надає базові права під'єднаній програмі, які вимагаються для належної роботи. Якщо Ви не хочете, щоб ця під'єднана програма мала ці права, Ви повинні відкликати її доступ.",
+ "mwoauthmanagemygrants-authonly-tooltip": "Чому я не можу оновити цей доступ? Якщо Ви хочете, щоб цей під'єднаний додаток мав це право, Вам треба відкликати доступ додатка.",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|Ваші}} редагування через цю програму",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|Ваші}} дії через цю програму",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|запропонував|запропонувала}} споживача OAuth (ключ споживача $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|оновив|оновила}} споживача OAuth (ключ споживача $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|схвалив|схвалила}} споживача OAuth на $3 (ключ споживача $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|відхилив|відхилила}} споживача OAuth на $3 (ключ споживача $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|вимкнув|вимкнула}} споживача OAuth на $3 (ключ споживача $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|повторно увімкнув|повторно увімкнула}} споживача OAuth на $3 (ключ споживача $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|створив|створила}} споживача лише для власника (ключ споживача $4)",
+ "log-action-filter-mwoauthconsumer": "Тип дії споживача OAuth:",
+ "log-action-filter-mwoauthconsumer-approve": "Схвалення споживача OAuth",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "Створення споживача OAuth лише для власника",
+ "log-action-filter-mwoauthconsumer-disable": "Вимкнення споживача OAuth",
+ "log-action-filter-mwoauthconsumer-propose": "Пропозиція споживача OAuth",
+ "log-action-filter-mwoauthconsumer-reenable": "Повторне увімкнення споживача OAuth",
+ "log-action-filter-mwoauthconsumer-reject": "Відхилення споживача OAuth",
+ "log-action-filter-mwoauthconsumer-update": "Оновлення споживача OAuth",
+ "mwoauthconsumer-consumer-logpage": "Журнал споживача OAuth",
+ "mwoauthconsumer-consumer-logpagetext": "Журнал затвердження, відхилення і вимкнення зареєстрованих споживачів OAuth.",
+ "mwoauth-bad-request-missing-params": "На жаль, щось пішло не так при налаштуванні цієї підключеної програми. Зв'яжіться з розробником програми.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Пропущені параметри OAuth, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Вибачте, щось пішло не так. Вам потрібно зв'язатися з автором програми для допомоги з цього питання.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Невідомий URL, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Вибачте, щось пішло не так. Вам потрібно зв'язатися з автором програми [$1] для допомоги з цього питання.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Невідомий URL, $2</span>",
+ "mwoauthdatastore-access-token-not-found": "Не знайдено схваленого ґранту для цього маркера авторизації.",
+ "mwoauthdatastore-request-token-not-found": "На жаль, щось пішло невірно при підключенні цієї програми. Поверніться назад та спробуйте підключити ваш профіль знову або зв'яжіться з автором програми.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Маркер OAuth не знайдений, $1</span>",
+ "mwoauthdatastore-callback-not-found": "URL зворотного виклику OAuth не знайдено у кеші. Це, ймовірно, помилка того, як програма здійснює запити до сервера.",
+ "mwoauthdatastore-request-token-already-used": "Цей запит уже завершено і не може бути подано ще раз.\nПоверніться до програми і спробуйте з'єднатися зі своїм обліковим запитом ще раз або зверніться до автора програми.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Маркер OAuth уже використано, $1</span>",
+ "mwoauthdatastore-bad-token": "Не знайдено маркера, відповідного вашому запиту.",
+ "mwoauthdatastore-bad-source-ip": "Запит надійшов з неприпустимої IP-адреси.",
+ "mwoauthdatastore-bad-verifier": "Наданий код підтвердження недійсний.",
+ "mwoauthdatastore-invalid-token-type": "Неприпустимий маркер запитаного типу.",
+ "mwoauthgrants-general-error": "Помилка у вашому запиті на OAuth.",
+ "mwoauthserver-bad-consumer": "«$1» не затверджена як підключена програма. [$2 Зв'яжіться] з автором програми по допомогу.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Підключена програма OAuth не затверджена, $3</span>",
+ "mwoauthserver-bad-consumer-key": "На жаль, щось пішло не так при підключенні цієї програми.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Невідомий ключ OAuth, $1</span>",
+ "mwoauthserver-insufficient-rights": "Вашому профілю не дозволено використовувати підключені програми, зв'яжіться з адміністратором вашого сайту для з'ясування причин.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Недостатні права користувача OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Неприпустимий маркер у вашому запиті.",
+ "mwoauthserver-invalid-user": "Для використання підключених програм на сайті вам потрібно мати профіль на всі проекти. Коли у вас є обліковий запис на усі проекти, можете спробувати знову підключити «$1».\n\n<span class=\"plainlinks mw-mwoautherror-details\">Необхідний єдиний вхід, $2</span>",
+ "mwoauthserver-consumer-no-secret": "На жаль, щось пішло не так при спробі з'єднання з цією програмою.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Споживач не має секретного ключа, $1</span>",
+ "mwoauthserver-consumer-owner-only": "«$1» — це пов'язаний додаток виключно для власника. Аби отримати маркер доступу, див. [[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Цей споживач лише для власника, $3</span>",
+ "mwoauth-invalid-authorization-title": "Помилка авторизації OAuth",
+ "mwoauth-invalid-authorization": "Неприпустимі заголовки авторизації у вашому запиті:$1",
+ "mwoauth-invalid-authorization-wrong-wiki": "Неприпустимі заголовки авторизації у вашому запиті для $1",
+ "mwoauth-invalid-authorization-invalid-user": "Не існують заголовки авторизації у вашому запиті для користувача",
+ "mwoauth-invalid-authorization-wrong-user": "Заголовки авторизації у вашому запиті призначені для іншого користувача",
+ "mwoauth-invalid-authorization-not-approved": "Програма, якою ви намагаєтеся з'єднатися, схоже, налаштована неправильно, зверніться до автора $1.",
+ "mwoauth-invalid-authorization-blocked-user": "Заголовки авторизації у вашому запитів призначені для заблокованого користувача",
+ "mwoauth-form-description-allwikis": "Привіт, $1!\n\nЩоб завершити Ваш запит, '''$2''' потребує дозволу на такі дії від Вашого імені у всіх проектах цього сайту:\n\n\n$4",
+ "mwoauth-form-description-onewiki": "Привіт, $1!\n\nЩоб завершити Ваш запит, '''$2''' потребує дозволу на виконання таких дій від Вашого імені на ''$4'':\n\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Привіт, $1!\n\nЩоб завершити Ваш запит, '''$2''' потребує дозвіл на доступ до інформації усіх проектів цього сайту від Вашого імені. Жодних змін Вашого облікового запису не буде.",
+ "mwoauth-form-description-onewiki-nogrants": "Привіт, $1!\n\nЩоб завершити Ваш запит, '''$2''' потребує дозволу на доступ до інформації на ''$4'' від Вашого імені. Жодних змін до Вашого облікового запису не буде.",
+ "mwoauth-form-description-allwikis-privateinfo": "Привіт, $1!\n\nЩоб виконати Ваш запит, '''$2''' потребує дозволу на доступ до інформації про Вас, включно з Вашим справжнім ім'ям та адресою електронної пошти, в усіх проектах цього сайту. Жодних змін не буде здійснено від імені Вашого облікового запису.",
+ "mwoauth-form-description-onewiki-privateinfo": "Привіт, $1!\n\nЩоб виконати Ваш запит, '''$2''' потребує дозволу на доступ до інформації про Вас, включно з Вашим справжнім ім'ям та адресою електронної пошти, на сайті ''$4''. Жодних змін не буде здійснено від імені Вашого облікового запису.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "Привіт, $1!\n\nЩоб виконати Ваш запит, '''$2''' потребує дозволу на доступ до інформації про Вас, в тому числі адресу електронної пошти в усіх проектах цього сайту. Жодних змін не буде здійснено від імені Вашого облікового запису.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "Привіт, $1!\n\nЩоб виконати Ваш запит, '''$2''' потребує дозволу на доступ до інформації про Вас, в тому числі адресу електронної пошти в проекті ''$4''. Жодних змін не буде здійснено від імені Вашого облікового запису.",
+ "mwoauth-form-button-approve": "Дозволити",
+ "mwoauth-form-button-cancel": "Скасувати",
+ "mwoauth-error": "Помилка підключення застосунку",
+ "mwoauth-grants-heading": "Потрібні дозволи:",
+ "mwoauth-grants-nogrants": "Програма не вимагає жодних дозволів.",
+ "mwoauth-acceptance-cancelled": "Ви вже вибрали не надавати $1 доступу до вашого профілю. $1 не буде працювати без вашого дозволу. Ви можете повернутися до $1 або до [[Special:OAuthManageMyGrants|управління]] вашими підключеними програмами.",
+ "mwoauth-granttype-normal": "Запит авторизації для конкретних дозволів.",
+ "grant-mwoauth-authonly": "Лише перевірка ідентичності користувача, без можливості читати сторінки чи здійснювати якісь дії від імені користувача.",
+ "grant-mwoauth-authonlyprivate": "Лише перевірка ідентичності користувача, з доступом до справжнього імені та електронної адреси, без можливості читати сторінки чи здійснювати якісь дії від імені користувача.",
+ "mwoauth-listgrants-extra-summary": "== Гранти, специфічні для OAuth ==\n\nЦі додаткові гранти можуть бути доцільними для споживачів OAuth.",
+ "mwoauth-oauth-exception": "Сталася помилка у протоколі OAuth:$1",
+ "mwoauth-callback-not-oob": "oauth_callback мусить бути заданим і слід задати \"oob\" (з урахуванням регістру)",
+ "mwoauth-callback-not-oob-or-prefix": "Має бути вказано oauth_callback і встановлено як «oob» (з урахуванням регістру) або налаштований колбек повинен бути префіксом наданого колбеку.",
+ "right-mwoauthproposeconsumer": "Пропонування нових споживачів OAuth",
+ "right-mwoauthupdateownconsumer": "Оновлення споживачів OAuth, яких ви контролюєте",
+ "right-mwoauthmanageconsumer": "Керування споживачами OAuth",
+ "right-mwoauthsuppress": "Приховування споживачів OAuth",
+ "right-mwoauthviewsuppressed": "Перегляд прихованих споживачів OAuth",
+ "right-mwoauthviewprivate": "Перегляд приватних даних OAuth",
+ "right-mwoauthmanagemygrants": "Керування грантами OAuth",
+ "action-mwoauthmanageconsumer": "керування споживачами OAuth",
+ "action-mwoauthsuppress": "приховування споживачів OAuth",
+ "action-mwoauthmanagemygrants": "керування вашими грантами OAuth",
+ "action-mwoauthproposeconsumer": "пропонування нових споживачів OAuth",
+ "action-mwoauthupdateownconsumer": "оновлення споживачів OAuth, яких ви контролюєте",
+ "action-mwoauthviewprivate": "перегляд приватних даних OAuth",
+ "action-mwoauthviewsuppressed": "перегляд прихованих споживачів OAuth",
+ "mwoauth-tag-reserved": "Теґи, що починаються з <code>OAuth CID:</code> зарезервовані для використання OAuth.",
+ "mwoauth-botpasswords-note": "<strong>Примітка:</strong> <span class=\"plainlinks\">[$1 OAuth]</span> — безпечніший, ніж паролі для ботів, і саме цій системі треба віддавати перевагу, якщо бот її підтримує.",
+ "mwoauth-api-module-disabled": "Модуль «$1» недоступний з OAuth.",
+ "echo-category-title-oauth-owner": "Розробка OAuth",
+ "echo-pref-tooltip-oauth-owner": "Повідомляти мене про події, пов'язані зі створеними мною додатками OAuth.",
+ "echo-category-title-oauth-admin": "Адміністратор OAuth",
+ "echo-pref-tooltip-oauth-admin": "Повідомляти мене про події, пов'язані з оглядом додатків OAuth.",
+ "notification-oauth-app-propose-title": "$1 {{GENDER:$1|запропонував|запропонувала}} новий додаток OAuth: $2",
+ "notification-oauth-app-update-title": "$1 {{GENDER:$1|оновив|оновила}} додаток OAuth $2",
+ "notification-oauth-app-approve-title": "$1 {{GENDER:$1|затвердив|затвердила}} {{GENDER:$3|Ваш}} додаток OAuth ($2)",
+ "notification-oauth-app-reject-title": "$1 {{GENDER:$1|відхилив|відхилила}} {{GENDER:$3|Ваш}} додаток OAuth ($2)",
+ "notification-oauth-app-disable-title": "$1 {{GENDER:$1|вимкнув|вимкнула}} {{GENDER:$3|Ваш}} додаток OAuth ($2)",
+ "notification-oauth-app-reenable-title": "$1 {{GENDER:$1|повторно увімкнув|повторно увімкнула}} {{GENDER:$3|Ваш}} додаток OAuth ($2)",
+ "notification-oauth-app-propose-subject": "$1 {{GENDER:$1|запропонував|запропонувала}} новий додаток OAuth на сайті {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 {{GENDER:$1|оновив|оновила}} додаток OAuth на сайті {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 {{GENDER:$1|затвердив|затвердила}} {{GENDER:$3|Ваш}} додаток OAuth на сайті {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 {{GENDER:$1|відхилив|відхилила}} {{GENDER:$3|Ваш}} додаток OAuth на сайті {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 {{GENDER:$1|вимкнув|вимкнула}} {{GENDER:$3|Ваш}} додаток OAuth на сайті {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 {{GENDER:$1|повторно увімкнув|повторно увімкнула}} {{GENDER:$3|Ваш}} додаток OAuth на сайті {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Перевірити додаток",
+ "notification-oauth-app-update-primary-link": "Перевірити додаток",
+ "notification-oauth-app-approve-primary-link": "Переглянути додаток",
+ "notification-oauth-app-reject-primary-link": "Переглянути додаток",
+ "notification-oauth-app-disable-primary-link": "Переглянути додаток",
+ "notification-oauth-app-reenable-primary-link": "Переглянути додаток",
+ "notification-oauth-app-body": "Причина: $1",
+ "mwoauth-oauth-version": "Версія протоколу OAuth",
+ "mwoauth-oauth2-is-confidential": "Клієнт є конфіденційним",
+ "mwoauth-oauth2-is-confidential-help": "Конфіденційний клієнт — це програма, здатна зберігати пароль клієнта конфіденційним для світу. Неконфіденційні клієнти є менш безпечними",
+ "mwoauth-oauth2-granttypes": "Дозволені типи доступу OAuth2",
+ "mwoauth-oauth2-granttype-auth-code": "Код авторизації",
+ "mwoauth-oauth2-granttype-refresh-token": "Оновити маркер",
+ "mwoauth-oauth2-granttype-client-credentials": "Свідоцтво клієнта",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "Не вдалося створити маркер доступу: користувач не підтвердив видання цього маркера доступу",
+ "mwoauth-oauth2-error-user-approval-deny": "Користувач відхилив запит від клієнтської програми",
+ "mwoauth-oauth-unsupported-version": "Ця кінцева точка недозволена для версії OAuth $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "Сфера доступу «$1» не дозволена для цієї програми",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "Клієнти, розраховані лише на власника, повинні отримувати дозвіл на використання client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "Не вдалося отримати маркер доступу: $1",
+ "mwoauth-oauth2-error-server-error": "Сервер авторизації натрапив на неочікувану умову, яка завадила йому виконати запит.",
+ "mwoauth-oauth2-error-invalid-request": "У запиті бракує обов'язкового параметра, значення параметра є недійсним, один і той же параметр вказано більш ніж один раз, або форма запиту є неправильною з якоїсь іншої причини.",
+ "mwoauth-oauth2-error-unauthorized-client": "Клієнт не авторизований для надсилання запитів на код авторизації за допомогою цього методу.",
+ "mwoauth-oauth2-error-access-denied": "Власник ресурсу або сервер авторизації відхилив запит.",
+ "mwoauth-oauth2-error-unsupported-response-type": "Сервер авторизації не підтримує отримання коду авторизації за допомогою цього методу.",
+ "mwoauth-oauth2-error-invalid-scope": "Запитувана сфера доступу є недійсною, невідомою, чи має неправильну форму.",
+ "mwoauth-oauth2-error-temporarily-unavailable": "Сервер авторизації наразі не може опрацювати запит або через тимчасове перевантаження, або через технічне обслуговування сервера.",
+ "mwoauth-oauth2-error-invalid-client": "Автентифікація клієнта зазнала невдачі (напр., невідомий клієнт, не включено автентифікацію клієнта, чи непідтримуваний метод автентифікації)",
+ "mwoauth-oauth2-error-request-not-verified": "Спроба отримати підтверджену властивість перед підтвердженням запиту",
+ "mwoauth-oauth2-invalid-access-token": "Недійсний маркер доступу",
+ "mwoauth-consumer-access-no-user": "Підтвердження споживача має бути прив'язане до дійсного користувача, тоді як подано користувача з ID 0",
+ "mwoauth-login-required-reason": "Щоб дозволяти програмам отримувати доступ до вашого облікового запису, вам треба спершу увійти в систему на сайті {{SITENAME}}.",
+ "mwoauthconsumer-consumer-view": "Переглянути цього споживача",
+ "mwoauthconsumer-application-view": "Переглянути цю заявку"
+}
diff --git a/OAuth/i18n/ur.json b/OAuth/i18n/ur.json
new file mode 100644
index 00000000..6e81db28
--- /dev/null
+++ b/OAuth/i18n/ur.json
@@ -0,0 +1,30 @@
+{
+ "@metadata": {
+ "authors": [
+ "BukhariSaeed",
+ "Muhammad Shuaib"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "منسلکہ اطلاقیے:",
+ "mwoauth-prefs-managegrantslink": "$1 منسلک {{PLURAL:$1|اطلاقیہ|اطلاقیوں}} کا انتظام",
+ "mwoauth-consumer-name": "ایپلیکیشن کا نام:",
+ "mwoauth-consumer-version": "کنزیومر ورژن",
+ "mwoauth-consumer-user": "ناشر:",
+ "mwoauth-consumer-stage": "موجودہ حال",
+ "mwoauth-consumer-description": "اطلاق کی وضاحت",
+ "mwoauth-consumer-callbackurl": "OAuth \"کال بیک\" یو آر ایل:",
+ "mwoauth-consumer-wiki": "قابل اطلاق منصوبہ",
+ "mwoauth-consumer-stage-proposed": "تجویز شدہ",
+ "mwoauth-consumer-stage-rejected": "رد شدہ",
+ "mwoauth-consumer-stage-expired": "منقضی شدہ",
+ "mwoauth-consumer-stage-approved": "منظورہ شدہ",
+ "mwoauth-consumer-stage-disabled": "معطل شد",
+ "mwoauth-consumer-stage-suppressed": "توقیف شد",
+ "oauthlistconsumers": "OAuth اطلاقیوں کی فہرست",
+ "mwoauthlistconsumers-legend": "OAuth ایپلیکیشنز براؤز کریں",
+ "mwoauth-consumer-stage-any": "کوئی بھی",
+ "mwoauth-form-description-allwikis": "آداب $1!\n\nآپ کی درخواست پر عمل کرنے کے لیے '''$2''' کو اس امر کی اجازت درکار ہے کہ وہ آپ کی جانب سے اس سائٹ کے تمام منصوبوں پر درج ذیل اقدامات کر سکے: \n\n$4",
+ "mwoauth-form-description-allwikis-nogrants": "آداب $1!\n\nآپ کی درخواست پر عمل کرنے کے لیے '''$2''' کو اس امر کی اجازت درکار ہے کہ وہ آپ کی جانب سے اس سائٹ کے تمام منصوبوں پر موجود معلومات تک رسائی حاصل کر سکے۔ آپ کے کھاتے سے کوئی تبدیلی نہیں کی جائے گی۔",
+ "mwoauth-form-description-allwikis-privateinfo": "آداب $1!\n\nآپ کی درخواست پر عمل کرنے کے لیے '''$2''' کو اس امر کی اجازت درکار ہے کہ وہ آپ کی جانب سے اس سائٹ کے تمام منصوبوں پر موجود آپ کے متعلق جملہ معلومات بشمول آپ کا حقیقی نام و برقی ڈاک پتا تک رسائی حاصل کر سکے۔ آپ کے کھاتے سے کوئی تبدیلی نہیں کی جائے گی۔",
+ "right-mwoauthmanagemygrants": "OAuth کے عطیوں کا انتظام"
+}
diff --git a/OAuth/i18n/uz.json b/OAuth/i18n/uz.json
new file mode 100644
index 00000000..6a53cd9f
--- /dev/null
+++ b/OAuth/i18n/uz.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sociologist"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Yoqilgan dasturlar:"
+}
diff --git a/OAuth/i18n/vec.json b/OAuth/i18n/vec.json
new file mode 100644
index 00000000..18145ff0
--- /dev/null
+++ b/OAuth/i18n/vec.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fierodelveneto"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "Aplicasion coneteste:",
+ "mwoauth-prefs-managegrantslink": "Xestionare {{PLURAL:$1|$1 aplicasion conetesta|$1 aplicasion coneteste|0=aplicasion coneteste}}",
+ "mwoauthconsumerregistration-manage": "xestiona",
+ "mwoauth-form-description-allwikis-nogrants": "S-ciao $1,\n\nPar terminar ła to domanda, '''$2''' el neçesita de l'autorixasion par ndar drento a łe informasion in tuti i projeti de sto sito par conto too. No vegnarà fata alguna modifega al to account.",
+ "mwoauth-form-description-allwikis-privateinfo": "S-ciao $1,\n\nPar terminar ła to domanda, '''$2''' el neçesita de l'autorixasion par ndar drento a łe to informasion e intrà ste cuà anca del to vero nome e del to indiriso e-mail, in tuti i projeti de sto sito. No vegnarà fata alguna modifega al to account.",
+ "mwoauth-form-description-onewiki-privateinfo": "S-ciao $1,\n\nPar terminar ła to domanda, '''$2''' el neçesita de l'autorixasion par ndar drento a łe to informasion e intrà ste cuà anca del to vero nome e el to indiriso e-mail, in $4. No vegnarà fata alguna modifega al to account.",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "S-ciao $1,\n\nPar terminar ła to domanda, '''$2''' el neçesita de l'autorixasion par ndar drento a łe to informasion e intrà ste cuà anca del to indiriso e-mail, in tuti i projeti de sto sito. No vegnarà fata alguna modifega al to account.",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "S-ciao $1,\n\nPar terminar ła to domanda, '''$2''' el neçesita de l'autorixasion par ndar drento a łe to informasion e intrà ste cuà anca del to indiriso e-mail so $4. No vegnarà fata alguna modifega al to account.",
+ "mwoauth-acceptance-cancelled": "Te ghè prefario no consentirghe a \"$1\" de ndar drento a ła to utensa. \"$1\" no ła funsionarà inmanco che no te ghe parmeti de vegnere drento. Te połi tornare a \"$1\" o in te ła [[Special:OAuthManageMyGrants|jestion]] de łe to aplicasion coneteste."
+}
diff --git a/OAuth/i18n/vi.json b/OAuth/i18n/vi.json
new file mode 100644
index 00000000..39d42bf2
--- /dev/null
+++ b/OAuth/i18n/vi.json
@@ -0,0 +1,195 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dinhxuanduyet",
+ "KhangND",
+ "Macofe",
+ "Minh Nguyen",
+ "Phjtieudoc",
+ "Withoutaname"
+ ]
+ },
+ "mwoauth-desc": "Cho phép sử dụng OAuth 1.0a để xác minh khi truy cập API",
+ "mwoauth-verified": "Ứng dụng hiện được phép truy cập MediaWiki thay mặt bạn.\n\nĐể hoàn thành quá trình này, xin hãy nhập giá trị xác minh này vào ứng dụng: '''$1'''",
+ "mwoauth-db-readonly": "Cơ sở dữ liệu OAuth hiện đang bị khóa tạm thời. Xin vui lòng thử lại vài phút nữa.",
+ "mwoauth-missing-field": "Thiếu giá trị cho trường “$1”",
+ "mwoauth-invalid-field": "Giá trị không hợp lệ được đưa vào trường “$1”",
+ "mwoauth-invalid-field-generic": "Giá trị không hợp lệ được cung cấp",
+ "mwoauth-field-hidden": "(thông tin này bị ẩn)",
+ "mwoauth-field-private": "(thông tin này là bí mật)",
+ "mwoauth-prefs-managegrants": "Ứng dụng kết nối:",
+ "mwoauth-prefs-managegrantslink": "Quản lý $1 ứng dụng kết nối",
+ "mwoauth-consumer-allwikis": "Tất cả các dự án trên mạng này",
+ "mwoauth-consumer-key": "Từ khóa tiêu dùng:",
+ "mwoauth-consumer-name": "Tên ứng dụng:",
+ "mwoauth-consumer-version": "Phiên bản tiêu dùng:",
+ "mwoauth-consumer-user": "Nhà xuất bản:",
+ "mwoauth-consumer-stage": "Trạng thái hiện tại:",
+ "mwoauth-consumer-email": "Địa chỉ thư điện tử liên lạc:",
+ "mwoauth-consumer-owner-only-label": "Chỉ bởi chủ:",
+ "mwoauth-consumer-description": "Miêu tả ứng dụng:",
+ "mwoauth-consumer-callbackurl": "URL “gọi lại” OAuth:",
+ "mwoauth-consumer-callbackisprefix": "Cho phép trình tiêu dùng định rõ một hàm gọi lại và sử dụng địa chỉ URL “gọi lại” bên trên là một tiền tố yêu cầu.",
+ "mwoauth-consumer-granttypes": "Các phân quyền được yêu cầu:",
+ "mwoauth-consumer-grantsneeded": "Các quyền có liên quan:",
+ "mwoauth-consumer-required-grant": "Thích hợp với tiêu dùng",
+ "mwoauth-consumer-wiki": "Dự án có liên quan:",
+ "mwoauth-consumer-wiki-thiswiki": "Dự án hiện tại ($1)",
+ "mwoauth-consumer-restrictions": "Hạn chế sử dụng:",
+ "mwoauth-consumer-restrictions-json": "Hạn chế sử dụng (JSON):",
+ "mwoauth-consumer-rsakey": "Chìa khóa RSA công cộng (tùy chọn):",
+ "mwoauth-consumer-secretkey": "Dấu hiệu tiêu dùng bí mật:",
+ "mwoauth-consumer-accesstoken": "Dấu hiệu truy cập:",
+ "mwoauth-consumer-reason": "Lý do:",
+ "mwoauth-consumer-email-unconfirmed": "Địa chỉ thư điện tử tài khoản của bạn chưa được xác nhận.",
+ "mwoauth-consumer-email-mismatched": "Địa chỉ thư điện tử được cung cấp phải cũng là địa chỉ thư điện tử của tài khoản của bạn.",
+ "mwoauth-invalid-access-token": "Không có dấu hiệu truy cập với chìa khóa được cung cấp.",
+ "mwoauth-consumer-stage-proposed": "đề xuất",
+ "mwoauth-consumer-stage-rejected": "từ chối",
+ "mwoauth-consumer-stage-expired": "hết hạn",
+ "mwoauth-consumer-stage-approved": "chấp nhận",
+ "mwoauth-consumer-stage-disabled": "tắt",
+ "mwoauth-consumer-stage-suppressed": "ẩn",
+ "mwoauthconsumerregistration-navigation": "Điều hướng:",
+ "mwoauthconsumerregistration-list": "Danh sách tiêu dùng của tôi",
+ "mwoauthconsumerregistration-main": "Chính",
+ "mwoauthconsumerregistration-propose-legend": "Ứng dụng tiêu dùng OAuth mới",
+ "mwoauthconsumerregistration-update-legend": "Cập nhật ứng dụng tiêu dùng OAuth",
+ "mwoauthconsumerregistration-propose-submit": "Đề xuất tiêu dùng",
+ "mwoauthconsumerregistration-update-submit": "Cập nhật tiêu dùng",
+ "mwoauthconsumerregistration-name": "Trình tiêu dùng",
+ "mwoauthconsumerregistration-user": "Nhà xuất bản",
+ "mwoauthconsumerregistration-description": "Miêu tả",
+ "mwoauthconsumerregistration-email": "Địa chỉ thư điện tử liên lạc",
+ "mwoauthconsumerregistration-consumerkey": "Từ khóa tiêu dùng",
+ "mwoauthconsumerregistration-stage": "Trạng thái",
+ "mwoauthconsumerregistration-lastchange": "Thay đổi cuối cùng",
+ "mwoauthconsumerregistration-manage": "quản lý",
+ "mwoauthconsumerregistration-resetsecretkey": "Đặt lại chìa khóa bí mật thành một giá trị mới",
+ "oauthmanageconsumers": "Quản lý tiêu dùng OAuth",
+ "mwoauthmanageconsumers-notloggedin": "Bạn phải đăng nhập để truy cấp trang này.",
+ "mwoauthmanageconsumers-type": "Hàng đợi:",
+ "mwoauthmanageconsumers-showproposed": "Yêu cầu được đề xuất",
+ "mwoauthmanageconsumers-showrejected": "Yêu cầu bị từ chối",
+ "mwoauthmanageconsumers-showexpired": "Yêu cầu đã hết hạn",
+ "mwoauthmanageconsumers-linkproposed": "yêu cầu được đề xuất",
+ "mwoauthmanageconsumers-linkrejected": "yêu cầu bị từ chối",
+ "mwoauthmanageconsumers-linkexpired": "yêu cầu đã hết hạn",
+ "mwoauthmanageconsumers-linkapproved": "yêu cầu được chấp nhận",
+ "mwoauthmanageconsumers-linkdisabled": "yêu cầu bị vô hiệu",
+ "mwoauthmanageconsumers-main": "Chính",
+ "mwoauthmanageconsumers-name": "Trình tiêu dùng",
+ "mwoauthmanageconsumers-user": "Nhà xuất bản",
+ "mwoauthmanageconsumers-description": "Miêu tả",
+ "mwoauthmanageconsumers-email": "Địa chỉ thư điện tử liên lạc",
+ "mwoauthmanageconsumers-consumerkey": "Từ khóa tiêu dùng",
+ "mwoauthmanageconsumers-lastchange": "Thay đổi cuối cùng",
+ "mwoauthmanageconsumers-review": "xem lại/quản lý",
+ "mwoauthmanageconsumers-confirm-legend": "Quản lý tiêu dùng OAuth",
+ "mwoauthmanageconsumers-action": "Thay đổi trạng thái:",
+ "mwoauthmanageconsumers-approve": "Chấp nhận",
+ "mwoauthmanageconsumers-reject": "Từ chối",
+ "mwoauthmanageconsumers-rsuppress": "Từ chối và ẩn",
+ "mwoauthmanageconsumers-disable": "Tắt",
+ "mwoauthmanageconsumers-dsuppress": "Tắt và ẩn",
+ "mwoauthmanageconsumers-reenable": "Chấp nhận",
+ "mwoauthmanageconsumers-reason": "Lý do:",
+ "mwoauthmanageconsumers-confirm-submit": "Cập nhật trạng thái tiêu dùng",
+ "mwoauthmanageconsumers-success-approved": "Yêu cầu đã được chấp nhận.",
+ "mwoauthmanageconsumers-success-rejected": "Yêu cầu đã bị từ chối.",
+ "oauthlistconsumers": "Liệt kê các ứng dụng OAuth",
+ "mwoauthlistconsumers-legend": "Duyệt các ứng dụng OAuth",
+ "mwoauthlistconsumers-view": "chi tiết",
+ "mwoauthlistconsumers-none": "Không tìm thấy ứng dụng khớp truy vấn này.",
+ "mwoauthlistconsumers-name": "Tên ứng dụng",
+ "mwoauthlistconsumers-version": "Phiên bản tiêu dùng:",
+ "mwoauthlistconsumers-user": "Nhà xuất bản",
+ "mwoauthlistconsumers-description": "Miêu tả",
+ "mwoauthlistconsumers-wiki": "Dự án có liên quan",
+ "mwoauthlistconsumers-callbackurl": "“URL gọi lại” OAuth",
+ "mwoauthlistconsumers-basicgrantsonly": "(chỉ truy cập cơ bản)",
+ "mwoauthlistconsumers-status": "Trạng thái",
+ "mwoauth-consumer-stage-any": "bất cứ",
+ "mwoauthlistconsumers-status-proposed": "đề xuất",
+ "mwoauthlistconsumers-status-approved": "chấp nhận",
+ "mwoauthlistconsumers-status-disabled": "tắt",
+ "mwoauthlistconsumers-status-rejected": "từ chối",
+ "mwoauthlistconsumers-status-expired": "hết hạn",
+ "oauthmanagemygrants": "Quản lý các ứng dụng kết nối",
+ "mwoauthmanagemygrants-navigation": "Điều hướng:",
+ "mwoauthmanagemygrants-showlist": "Danh sách các ứng dụng kết nối",
+ "mwoauthmanagemygrants-none": "Không có ứng dụng nào được kết nối với tài khoản của bạn.",
+ "mwoauthmanagemygrants-user": "Nhà xuất bản:",
+ "mwoauthmanagemygrants-description": "Miêu tả",
+ "mwoauthmanagemygrants-wikiallowed": "Được cho phép trong dự án:",
+ "mwoauthmanagemygrants-grants": "Các quyền có liên quan",
+ "mwoauthmanagemygrants-grantsallowed": "Các quyền được cấp",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "Các quyền được cấp có liên quan:",
+ "mwoauthmanagemygrants-review": "quản lý truy cập",
+ "mwoauthmanagemygrants-revoke": "thu hồi quyền truy cập",
+ "mwoauthmanagemygrants-grantaccept": "Cấp quyền",
+ "mwoauthmanagemygrants-confirm-legend": "Quản lý ứng dụng kết nối",
+ "mwoauthmanagemygrants-update": "Cập nhật các dấu hiệu được cấp",
+ "mwoauthmanagemygrants-renounce": "Rút quyền",
+ "mwoauthmanagemygrants-action": "Thay đổi trạng thái:",
+ "mwoauthmanagemygrants-confirm-submit": "Cập nhật trạng thái của dấu hiệu truy cập",
+ "mwoauthconsumer-consumer-logpage": "Nhật trình tiêu dùng OAuth",
+ "mwoauth-bad-request-missing-params": "Rất tiếc, đã xảy ra lỗi khi thiết lập ứng dụng kết nối này. Hãy liên lạc với nhà phát triển ứng dụng.\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth thiếu tham số, $1</span>",
+ "mwoauth-bad-request-invalid-action": "Rất tiếc, đã xảy ra lỗi. Xin vui lòng liên lạc với tác giả ứng dụng để xin trợ giúp.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL bất ngờ, $1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "Rất tiếc, đã xảy ra lỗi. Xin vui lòng [$1 liên lạc] với tác giả ứng dụng để xin trợ giúp.\n\n<span class=\"plainlinks mw-mwoautherror-details\">URL bất ngờ, $2</span>",
+ "mwoauthdatastore-request-token-not-found": "Rất tiếc, có trục trặc khi kết nối với ứng dụng này.\nHãy quay lại và thử kết nối với tài khoản của bạn lần nữa hoặc liên lạc với nhà phát triển ứng dụng.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Không tìm thấy mã OAuth, $1</span>",
+ "mwoauthdatastore-bad-token": "Không tìm thấy dấu hiệu ứng với yêu cầu của bạn.",
+ "mwoauthdatastore-bad-source-ip": "Yêu cầu này đến từ một địa chỉ IP không hợp lệ.",
+ "mwoauthdatastore-bad-verifier": "Mã xác minh được cung cấp là không hợp lệ.",
+ "mwoauthdatastore-invalid-token-type": "Đã yêu cầu kiểu dấu hiệu không hợp lệ.",
+ "mwoauthgrants-general-error": "Có lỗi trong yêu cầu OAuth của bạn.",
+ "mwoauthserver-bad-consumer": "“$1” không được chấp nhận là một Ứng dụng Kết nối. Hãy [$2 liên lạc] với tác giả ứng dụng để xin trợ giúp.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Ứng dụng kết nối OAuth không được chấp nhận, $3</span>",
+ "mwoauthserver-bad-consumer-key": "Rất tiếc, đã xảy ra lỗi khi kết nối với ứng dụng này.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Chìa khóa OAuth bất ngờ, $1</span>",
+ "mwoauthserver-insufficient-rights": "Tài khoản của bạn không được phép sử dụng tính năng Ứng dụng kết nối, hãy liên lạc với quản lý viên trang Web của bạn để tìm hiểu lý do tại sao.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Người dùng không đủ quyền OAuth, $1</span>",
+ "mwoauthserver-invalid-request-token": "Dấu hiệu không hợp lệ trong yêu cầu của bạn.",
+ "mwoauthserver-invalid-user": "Để sử dụng Ứng dụng Kết nối trên trang Web này, bạn cần phải có tài khoản trên tất cả các dự án. Khi nào bạn có tài khoản trên tất cả các dự án, xin hãy thử lại kết nối với “$1”.\n\n<span class=\"plainlinks mw-mwoautherror-details\">Cần đăng nhập thống nhất, $2</span>",
+ "mwoauth-invalid-authorization-title": "Lỗi xác minh OAuth",
+ "mwoauth-form-description-allwikis": "Xin chào $1,\n\nĐể hoàn thành yêu cầu của bạn, '''$2''' cần được phép thực hiện các tác vụ sau thay mặt cho bạn trên tất cả các dự án của trang Web này:\n\n$4",
+ "mwoauth-form-description-onewiki": "Xin chào $1,\n\nĐể hoàn thành yêu cầu của bạn, '''$2''' cần được phép thực hiện các tác vụ sau thay mặt cho bạn trên ''$4'':\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "Xin chào $1,\n\nĐể hoàn thành yêu cầu của bạn, '''$2''' cần có quyền truy cập cơ bản thay mặt cho bạn trên tất cả các dự án của trang Web này. Nó sẽ không thay đổi gì dùng tài khoản của bạn.",
+ "mwoauth-form-description-onewiki-nogrants": "Xin chào $1,\n\nĐể hoàn thành yêu cầu của bạn, '''$2''' cần có quyền truy cập cơ bản thay mặt cho bạn trên ''$4''. Nó sẽ không thay đổi gì dùng tài khoản của bạn.",
+ "mwoauth-form-button-approve": "Cho phép",
+ "mwoauth-form-button-cancel": "Hủy bỏ",
+ "mwoauth-error": "Lỗi Kết nối với Ứng dụng",
+ "mwoauth-grants-heading": "Các quyền được yêu cầu:",
+ "mwoauth-grants-nogrants": "Ứng dụng không yêu cầu quyền nào.",
+ "mwoauth-acceptance-cancelled": "Bạn đã quyết định không cho phép “$1” truy cập tài khoản của bạn. “$1” chỉ hoạt động nếu bạn cho phép truy cập. Bạn có thể trở lại “$1” hoặc [[Special:OAuthManageMyGrants|quản lý]] các ứng dụng kết nối của bạn.",
+ "grant-mwoauth-authonlyprivate": "Xác minh danh tính người dùng chỉ có quyền truy cập vào tên thật và địa chỉ thư điện tử, không có quyền đọc trang hoặc thay mặt cho người dùng.",
+ "mwoauth-oauth-exception": "Đã xuất hiện lỗi trong giao thức OAuth: $1",
+ "mwoauth-callback-not-oob": "oauth_callback phải được xác định là “oob” (phải là chữ thường)",
+ "right-mwoauthproposeconsumer": "Đề xuất tiêu dùng OAuth mới",
+ "right-mwoauthmanageconsumer": "Quản lý tiêu dùng OAuth",
+ "right-mwoauthviewprivate": "Xem dữ liệu riêng OAuth",
+ "right-mwoauthmanagemygrants": "Quản lý các quyền OAuth được cấp",
+ "action-mwoauthmanageconsumer": "quản lý tiêu dùng OAuth",
+ "action-mwoauthmanagemygrants": "quản lý các quyền OAuth mà bạn cấp",
+ "action-mwoauthproposeconsumer": "đề xuất tiêu dùng OAuth mới",
+ "echo-category-title-oauth-owner": "Phát triển OAuth",
+ "echo-pref-tooltip-oauth-owner": "Thông báo cho tôi về các sự kiện có liên quan đến ứng dụng OAuth do tôi tạo ra.",
+ "echo-category-title-oauth-admin": "Quản lý OAuth",
+ "echo-pref-tooltip-oauth-admin": "Thông báo cho tôi về các sự kiện có liên quan đến xem lại ứng dụng OAuth.",
+ "notification-oauth-app-propose-title": "$1 đã đề nghị một ứng dụng OAuth mới: $2",
+ "notification-oauth-app-update-title": "$1 đã cập nhật ứng dụng OAuth $2",
+ "notification-oauth-app-approve-title": "$1 đã chấp nhận ứng dụng OAuth của {{GENDER:$3}}bạn ($2)",
+ "notification-oauth-app-reject-title": "$1 đã từ chối ứng dụng OAuth của {{GENDER:$3}}bạn ($2)",
+ "notification-oauth-app-disable-title": "$1 đã vô hiệu hóa ứng dụng OAuth của {{GENDER:$3}}bạn ($2)",
+ "notification-oauth-app-reenable-title": "$1 đã kích hoạt lại ứng dụng OAuth của {{GENDER:$3}}bạn ($2)",
+ "notification-oauth-app-propose-subject": "$1 đã đề nghị một ứng dụng OAuth mới tại {{SITENAME}}",
+ "notification-oauth-app-update-subject": "$1 đã cập nhật một ứng dụng OAuth tại {{SITENAME}}",
+ "notification-oauth-app-approve-subject": "$1 đã chấp nhận ứng dụng OAuth của {{GENDER:$3}}bạn tại {{SITENAME}}",
+ "notification-oauth-app-reject-subject": "$1 đã từ chối ứng dụng OAuth của {{GENDER:$3}}bạn tại {{SITENAME}}",
+ "notification-oauth-app-disable-subject": "$1 đã vô hiệu hóa ứng dụng OAuth của {{GENDER:$3}}bạn tại {{SITENAME}}",
+ "notification-oauth-app-reenable-subject": "$1 đã kích hoạt lại ứng dụng OAuth của {{GENDER:$3}}bạn tại {{SITENAME}}",
+ "notification-oauth-app-propose-primary-link": "Xem lại ứng dụng",
+ "notification-oauth-app-update-primary-link": "Xem lại ứng dụng",
+ "notification-oauth-app-approve-primary-link": "Xem ứng dụng",
+ "notification-oauth-app-reject-primary-link": "Xem ứng dụng",
+ "notification-oauth-app-disable-primary-link": "Xem ứng dụng",
+ "notification-oauth-app-reenable-primary-link": "Xem ứng dụng",
+ "notification-oauth-app-body": "Lý do: $1"
+}
diff --git a/OAuth/i18n/vo.json b/OAuth/i18n/vo.json
new file mode 100644
index 00000000..a2975854
--- /dev/null
+++ b/OAuth/i18n/vo.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Malafaya"
+ ]
+ },
+ "mwoauthlistconsumers-description": "Bepenam"
+}
diff --git a/OAuth/i18n/wuu.json b/OAuth/i18n/wuu.json
new file mode 100644
index 00000000..8dd45583
--- /dev/null
+++ b/OAuth/i18n/wuu.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Benojan",
+ "十弌"
+ ]
+ },
+ "mwoauth-consumer-allwikis": "箇網站個全部項目",
+ "mwoauth-form-button-approve": "允許",
+ "mwoauth-form-button-cancel": "取消"
+}
diff --git a/OAuth/i18n/xmf.json b/OAuth/i18n/xmf.json
new file mode 100644
index 00000000..4ec05a04
--- /dev/null
+++ b/OAuth/i18n/xmf.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Narazeni"
+ ]
+ },
+ "mwoauth-oauth2-granttype-auth-code": "ავტორიზაციაშ კოდი"
+}
diff --git a/OAuth/i18n/yi.json b/OAuth/i18n/yi.json
new file mode 100644
index 00000000..1e2bea9a
--- /dev/null
+++ b/OAuth/i18n/yi.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "פוילישער"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "פארבונדענע אנווענדונגען:",
+ "mwoauth-consumer-version": "קאנסומענט ווערסיע:",
+ "mwoauth-consumer-email-unconfirmed": "אייער קאנטע ע־פאסט אדרעס איז נאך נישט געווארן באשטעטיגט.",
+ "oauthmanagemygrants": "פארוואלטן פארבונדענע אנווענדונגען",
+ "notification-oauth-app-body": "אורזאך: $1"
+}
diff --git a/OAuth/i18n/yue.json b/OAuth/i18n/yue.json
new file mode 100644
index 00000000..8e3eee2b
--- /dev/null
+++ b/OAuth/i18n/yue.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hello903hello"
+ ]
+ },
+ "mwoauth-prefs-managegrants": "連咗嘅應用程式:",
+ "mwoauth-prefs-managegrantslink": "管理{{PLURAL:$1|$1個|$1個|0=}}連咗嘅應用程式"
+}
diff --git a/OAuth/i18n/zh-hans.json b/OAuth/i18n/zh-hans.json
new file mode 100644
index 00000000..9e11d1f7
--- /dev/null
+++ b/OAuth/i18n/zh-hans.json
@@ -0,0 +1,298 @@
+{
+ "@metadata": {
+ "authors": [
+ "A2093064",
+ "Cwek",
+ "Duolaimi",
+ "Hzy980512",
+ "Impersonator 1",
+ "Josephine W.",
+ "LNDDYL",
+ "Liuxinyu970226",
+ "Mys 721tx",
+ "Mywood",
+ "Nbdd0121",
+ "NigelSoft",
+ "Qiyue2001",
+ "Shirayuki",
+ "Shizhao",
+ "Simon Shek",
+ "VulpesVulpes825",
+ "Xiaomingyan",
+ "Xiplus",
+ "Yfdyh000"
+ ]
+ },
+ "oauth": "OAuth",
+ "mwoauth-desc": "允许使用OAuth 1.0a 与OAuth 2.0 用作API授权",
+ "mwoauth-verified": "现在该应用以允许以您的名义访问MediaWiki。\n\n要完成这个过程,请为该应用提供这个校验值:'''$1'''",
+ "mwoauth-db-readonly": "OAuth数据库暂时锁定。请几分钟后重试。",
+ "mwoauth-missing-field": "缺少值为“$1”字段",
+ "mwoauth-invalid-field": "为\"$1\"字段提供的值无效",
+ "mwoauth-invalid-field-generic": "提供的值无效",
+ "mwoauth-field-hidden": "(这些信息已被隐藏)",
+ "mwoauth-field-private": "(这些信息是不公开的)",
+ "mwoauth-prefs-managegrants": "连接应用:",
+ "mwoauth-prefs-managegrantslink": "管理$1个连接的{{PLURAL:$1|应用程序}}",
+ "mwoauth-consumer-allwikis": "本站的所有项目",
+ "mwoauth-consumer-key": "消费者密钥:",
+ "mwoauth-consumer-name": "应用名称:",
+ "mwoauth-consumer-version": "消费者版本:",
+ "mwoauth-consumer-user": "发布者:",
+ "mwoauth-consumer-stage": "当前状态:",
+ "mwoauth-consumer-email": "联系人电子邮件地址:",
+ "mwoauth-consumer-email-help": "只对通过新消费者的应用可见",
+ "mwoauth-consumer-owner-only-label": "仅限主人:",
+ "mwoauth-consumer-owner-only": "此consumer只能被$1使用。",
+ "mwoauth-consumer-owner-only-help": "选择此选项将导致consumer被自动批准,并同意由$1使用。这将不会被其他用户所使用,并且通常出现的授权流将不会运行。使用此consumer做出的操作将不会被标记。",
+ "mwoauth-consumer-description": "应用说明:",
+ "mwoauth-consumer-callbackurl": "OAuth“回调”URL:",
+ "mwoauth-consumer-callbackisprefix": "允许取用者在请求中指定一个回调并使用上面的 \"callback\" URL 作为一个必需的前缀。",
+ "mwoauth-consumer-granttypes": "请求的权限类型:",
+ "mwoauth-consumer-grantsneeded": "应用授权:",
+ "mwoauth-consumer-required-grant": "适用于最终用户",
+ "mwoauth-consumer-wiki": "适用项目:",
+ "mwoauth-consumer-wiki-thiswiki": "当前项目 ( $1 )",
+ "mwoauth-consumer-restrictions": "使用限制:",
+ "mwoauth-consumer-restrictions-json": "使用限制(JSON) :",
+ "mwoauth-consumer-rsakey": "RSA公钥(可选):",
+ "mwoauth-consumer-rsakey-help": "输入一个公钥来使用RSA-SHA1签名方法。留空以使用HMAC-SHA1(包含随机密钥)。如果您对此不确定,请将其留空。",
+ "mwoauth-consumer-secretkey": "最终用户的私密令牌:",
+ "mwoauth-consumer-accesstoken": "访问令牌:",
+ "mwoauth-consumer-reason": "原因:",
+ "mwoauth-consumer-developer-agreement": "通过提交此应用程序,您承认我们保留禁用您的应用程序,移除或限制您或您的应用程序对此网站的访问权的权利,并当我们相信根据我们自身的判断,您或您的应用程序违反此网站的任何方针、指引和指导原则时,继续从事任何其他我们认为适当的操作过程。我们可以在我们谨慎判断认为必要时,随时更改此应用程序方针,而不事先通知。您继续使用OAuth就意味着认可这些更改。",
+ "mwoauth-consumer-email-unconfirmed": "您的账户的电子邮件地址还没有确认。",
+ "mwoauth-consumer-email-mismatched": "提供的电子邮件地址必须与您的帐户相匹配。",
+ "mwoauth-consumer-alreadyexists": "最终用户与此名称/版本/发布者的组合已经存在",
+ "mwoauth-consumer-alreadyexistsversion": "使用此名称/发布者组合的程序已存在相等或更高版本(“$1”)",
+ "mwoauth-consumer-not-accepted": "不能更新已挂起消费者请求的信息",
+ "mwoauth-consumer-not-proposed": "该消费者现不被建议",
+ "mwoauth-consumer-not-disabled": "该消费者现不被启用",
+ "mwoauth-consumer-not-approved": "该消费者不被许可(或者已被禁用)",
+ "mwoauth-missing-consumer-key": "没有提供消费者密钥。",
+ "mwoauth-invalid-consumer-key": "根据提供的密钥不存在相应消费者。",
+ "mwoauth-invalid-access-token": "根据提供的密钥不存在相应允许令牌。",
+ "mwoauth-invalid-access-wrongwiki": "这个消费者只被允许在“$1”项目使用。",
+ "mwoauth-consumer-conflict": "有人更改了您查看的消费者的属性。请再试一次。您可能想要检查的更改日志。",
+ "mwoauth-consumer-grantshelp": "每种功能补助都会访问已存在用户账户的用户权限。参见[[Special:ListGrants|授权表]]以获取更多信息。",
+ "mwoauth-consumer-stage-proposed": "已提议",
+ "mwoauth-consumer-stage-rejected": "已拒绝",
+ "mwoauth-consumer-stage-expired": "已过期",
+ "mwoauth-consumer-stage-approved": "已批准",
+ "mwoauth-consumer-stage-disabled": "已禁用",
+ "mwoauth-consumer-stage-suppressed": "已阻止",
+ "oauthconsumerregistration": "OAuth消费方注册",
+ "mwoauthconsumerregistration-navigation": "导航:",
+ "mwoauthconsumerregistration-propose": "提议新消费者",
+ "mwoauthconsumerregistration-list": "我的消费者列表",
+ "mwoauthconsumerregistration-main": "主要",
+ "mwoauthconsumerregistration-propose-text": "开发者应使用下面的表单提议新的OAuth应用程序(更多信息请参见[//www.mediawiki.org/wiki/Extension:OAuth 该扩展插件文档])。提交表单后,您将收到与您应用程序相关的令牌,它用于验证身份。OAuth管理员会审核并可能通过您的提议,之后您的程序才能正常访问。\n\n一些建议与提醒:\n* 如果可能,尝试使用较少的功能,避免目前不需要的功能。\n* 版本应存在于表单“major.minor.release”(最新的两个作为选项)并在需要时增加功能。\n* 如果可能,请提供RSA公钥(PEM格式);否则将会使用(较不安全的)私有领牌。\n* 您可使用项目ID限制应用程序访问此网站的单一项目(使用“ * ”访问所有项目)。",
+ "mwoauthconsumerregistration-update-text": "使用下面的表格更新您控制的OAuth消费方信息。\n\n此处所有的值将覆盖之前设定。不要将字段留空,除非您要清除这些值。",
+ "mwoauthconsumerregistration-maintext": "此页面用于让开发人员在此网站的注册信息中提议和更新OAuth新消费方应用程序。\n\n在此,您可以:\n* [[Special:OAuthConsumerRegistration/propose|为新消费方申请令牌]]。\n* [[Special:OAuthConsumerRegistration/list|管理您现有的消费方]]。\n\n关于OAuth的更多信息,请参见[//www.mediawiki.org/wiki/Extension:OAuth 扩展程序的说明文档]。",
+ "mwoauthconsumerregistration-propose-legend": "新OAuth消费方应用程序",
+ "mwoauthconsumerregistration-update-legend": "更新OAuth消费方应用程序",
+ "mwoauthconsumerregistration-propose-submit": "提议消费者",
+ "mwoauthconsumerregistration-update-submit": "更新消费者",
+ "mwoauthconsumerregistration-none": "您没有控制任何OAuth消费方。",
+ "mwoauthconsumerregistration-name": "消费者",
+ "mwoauthconsumerregistration-user": "发布者",
+ "mwoauthconsumerregistration-description": "说明",
+ "mwoauthconsumerregistration-email": "联系人电子邮件",
+ "mwoauthconsumerregistration-consumerkey": "消费者密钥",
+ "mwoauthconsumerregistration-stage": "状态",
+ "mwoauthconsumerregistration-lastchange": "最后更改",
+ "mwoauthconsumerregistration-manage": "管理",
+ "mwoauthconsumerregistration-resetsecretkey": "将密钥重置为一个新值",
+ "mwoauthconsumerregistration-proposed": "已收到您的OAuth消费方请求。\n\n您的消费方令牌是'''$1''',您的私有令牌是'''$2'''。''请保存令牌以供日后参考。''",
+ "mwoauthconsumerregistration-created-owner-only": "您的OAuth消费方已创建。\n\n您的令牌是:\n; 消费方令牌:$1\n; 消费方密钥:$2\n; 访问令牌:$3\n; 访问密钥:$4\n<em>请记住上述信息,以便今后参考。</em>",
+ "mwoauthconsumerregistration-updated": "已更新您的OAuth消费方注册信息。",
+ "mwoauthconsumerregistration-secretreset": "您已分配了'''$1'''的消费者密钥。''请保留好它以供今后参考。''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "已重置您的OAuth消费方令牌。新的令牌是:\n; 消费方令牌:$1\n; 消费方密钥:$2\n; 访问令牌:$3\n; 访问密钥:$4\n<em>请记住上述信息,以便今后参考。</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "您必须确认您的电子邮件地址才能创建OAuth应用程序。请通过[[Special:Preferences|系统设置]]设置并确认您的电子邮件地址。",
+ "oauthmanageconsumers": "管理OAuth消费方",
+ "mwoauthmanageconsumers-notloggedin": "您必须登录后才能访问此页。",
+ "mwoauthmanageconsumers-type": "队列:",
+ "mwoauthmanageconsumers-showproposed": "拟议的请求",
+ "mwoauthmanageconsumers-showrejected": "被拒绝的请求",
+ "mwoauthmanageconsumers-showexpired": "过期的请求",
+ "mwoauthmanageconsumers-linkproposed": "提议的请求",
+ "mwoauthmanageconsumers-linkrejected": "拒绝的请求",
+ "mwoauthmanageconsumers-linkexpired": "过期的请求",
+ "mwoauthmanageconsumers-linkapproved": "通过的请求",
+ "mwoauthmanageconsumers-linkdisabled": "禁用的请求",
+ "mwoauthmanageconsumers-main": "主要",
+ "mwoauthmanageconsumers-maintext": "本页面用于处理OAuth(参见http://oauth.net)消费方应用程序请求和管理已有OAuth消费方。",
+ "mwoauthmanageconsumers-queues": "在下方选择消费者确认队列:",
+ "mwoauthmanageconsumers-q-proposed": "已提议消费者请求队列",
+ "mwoauthmanageconsumers-q-rejected": "已拒绝消费者请求队列",
+ "mwoauthmanageconsumers-q-expired": "已过时消费者请求队列",
+ "mwoauthmanageconsumers-lists": "在下方的状态列表中选择一个消费者:",
+ "mwoauthmanageconsumers-l-approved": "现有已批准程序列表",
+ "mwoauthmanageconsumers-l-disabled": "现有已禁用程序列表",
+ "mwoauthmanageconsumers-none-proposed": "此列表上没有提议消费者。",
+ "mwoauthmanageconsumers-none-rejected": "此列表上没有提议消费者。",
+ "mwoauthmanageconsumers-none-expired": "此列表上没有提议消费者。",
+ "mwoauthmanageconsumers-none-approved": "没有消费者满足此条件。",
+ "mwoauthmanageconsumers-none-disabled": "没有消费者满足此条件。",
+ "mwoauthmanageconsumers-name": "消费者",
+ "mwoauthmanageconsumers-user": "发布者",
+ "mwoauthmanageconsumers-description": "说明",
+ "mwoauthmanageconsumers-email": "联系邮件",
+ "mwoauthmanageconsumers-consumerkey": "消费者密钥",
+ "mwoauthmanageconsumers-lastchange": "最近更新",
+ "mwoauthmanageconsumers-review": "审查/管理",
+ "mwoauthmanageconsumers-confirm-text": "使用此表单批准、拒绝、禁用或重新启用此消费者。",
+ "mwoauthmanageconsumers-confirm-legend": "管理OAuth消费方",
+ "mwoauthmanageconsumers-action": "更改状态:",
+ "mwoauthmanageconsumers-approve": "已批准",
+ "mwoauthmanageconsumers-reject": "已回绝",
+ "mwoauthmanageconsumers-rsuppress": "已回绝并禁止",
+ "mwoauthmanageconsumers-disable": "已禁用",
+ "mwoauthmanageconsumers-dsuppress": "已禁用并禁止",
+ "mwoauthmanageconsumers-reenable": "已批准",
+ "mwoauthmanageconsumers-reason": "原因:",
+ "mwoauthmanageconsumers-confirm-submit": "更新消费者状态",
+ "mwoauthmanageconsumers-success-approved": "请求已批准。",
+ "mwoauthmanageconsumers-success-rejected": "请求已被拒绝。",
+ "mwoauthmanageconsumers-success-disabled": "消费者已禁用。",
+ "mwoauthmanageconsumers-success-reanable": "消费者已重新启用。",
+ "mwoauthmanageconsumers-search-name": "使用此名称的消费者",
+ "mwoauthmanageconsumers-search-publisher": "通过此用户的消费者",
+ "oauthlistconsumers": "OAuth应用程序列表",
+ "mwoauthlistconsumers-legend": "浏览OAuth应用程序",
+ "mwoauthlistconsumers-view": "详情",
+ "mwoauthlistconsumers-none": "找不到满足此条件的应用程序。",
+ "mwoauthlistconsumers-name": "应用程序名称",
+ "mwoauthlistconsumers-version": "消费者版本",
+ "mwoauthlistconsumers-user": "发布者",
+ "mwoauthlistconsumers-description": "说明",
+ "mwoauthlistconsumers-wiki": "可用项目",
+ "mwoauthlistconsumers-callbackurl": "OAuth“回调URL”",
+ "mwoauthlistconsumers-callbackisprefix": "允许取用者在请求中指定一个回调并使用上面的 \"callback\" URL 作为一个必需的前缀。",
+ "mwoauthlistconsumers-grants": "可用补助",
+ "mwoauthlistconsumers-basicgrantsonly": "(仅适用于基本访问)",
+ "mwoauthlistconsumers-status": "状态",
+ "mwoauth-consumer-stage-any": "任何",
+ "mwoauthlistconsumers-status-proposed": "建议",
+ "mwoauthlistconsumers-status-approved": "批准",
+ "mwoauthlistconsumers-status-disabled": "停用",
+ "mwoauthlistconsumers-status-rejected": "拒绝",
+ "mwoauthlistconsumers-status-expired": "已过期",
+ "oauthmanagemygrants": "管理连接的应用程序",
+ "mwoauthmanagemygrants-text": "此页面列出了可以使用您的账户的所有应用程序。这些应用程序的可访问范围取决于您授予这些应用程序可代表您操作时所允许的限制条件。如果您单独授予一个应用程序可代表您访问不同的姊妹项目,那么您将会在下面看到每个项目的单独配置。\n\n已连接的应用程序将通过 OAuth 协议访问您的账户。<span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth 详细了解有关已连接的应用程序])</span>",
+ "mwoauthmanagemygrants-navigation": "导航:",
+ "mwoauthmanagemygrants-showlist": "连接的应用程序列表",
+ "mwoauthmanagemygrants-none": "没有应用程序连接到您的账户。",
+ "mwoauthmanagemygrants-user": "发布者:",
+ "mwoauthmanagemygrants-description": "说明",
+ "mwoauthmanagemygrants-wikiallowed": "允许在项目上使用:",
+ "mwoauthmanagemygrants-grants": "应用授权:",
+ "mwoauthmanagemygrants-grantsallowed": "允许授权",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "允许的可用授权:",
+ "mwoauthmanagemygrants-review": "管理访问",
+ "mwoauthmanagemygrants-revoke": "移除授权",
+ "mwoauthmanagemygrants-grantaccept": "授权",
+ "mwoauthmanagemygrants-update-text": "使用以下表单修改授予给代表您的操作的应用程序的权限。",
+ "mwoauthmanagemygrants-revoke-text": "使用下面的表格撤销应用程序以您的名义的访问权限。",
+ "mwoauthmanagemygrants-confirm-legend": "管理连接的应用程序",
+ "mwoauthmanagemygrants-update": "更新授权",
+ "mwoauthmanagemygrants-renounce": "取消授权",
+ "mwoauthmanagemygrants-action": "更新状态:",
+ "mwoauthmanagemygrants-confirm-submit": "更新授权令牌状态",
+ "mwoauthmanagemygrants-success-update": "您对此应用程序的参数设置已更新。",
+ "mwoauthmanagemygrants-success-renounce": "已取消应用程序对您账户的访问权。",
+ "mwoauthmanagemygrants-basic-tooltip": "为何我不能更新此功能?此功能向您提供可连接应用基本权限,它要求功能正常。如果您不希望此可连接应用拥有这些权限,您需要移除应用访问权。",
+ "mwoauthmanagemygrants-authonly-tooltip": "为什么我不能更新此权限?如果您不希望这个连接的应用程序拥有此权限,您应该移除应用程序的访问权。",
+ "logentry-mwoauthconsumer-propose": "$1{{GENDER:$2|申请建立}}一个OAuth消费方(消费方密钥$4)",
+ "logentry-mwoauthconsumer-update": "$1{{GENDER:$2|更新了}}一个OAuth消费方(消费方密钥$4)",
+ "logentry-mwoauthconsumer-approve": "$1{{GENDER:$2|批准了}}$3的OAuth消费方(消费方密钥$4)",
+ "logentry-mwoauthconsumer-reject": "$1{{GENDER:$2|拒绝了}}$3的OAuth消费方(消费方密钥$4)",
+ "logentry-mwoauthconsumer-disable": "$1{{GENDER:$2|禁用了}}$3的OAuth消费方(消费方密钥$4)",
+ "logentry-mwoauthconsumer-reenable": "$1{{GENDER:$2|重新启用了}}$3的OAuth消费方(消费方密钥$4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1{{GENDER:$2|创建了}}仅限主人的OAuth消费方(消费方密钥$4)",
+ "mwoauthconsumer-consumer-logpage": "OAuth消费方日志",
+ "mwoauthconsumer-consumer-logpagetext": "在册OAuth消费方的批准、拒绝和禁用日志。",
+ "mwoauth-bad-request-missing-params": "抱歉,配置此连接应用程序时出错。请联络应用程序的开发者。\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth缺少参数,$1</span>",
+ "mwoauth-bad-request-invalid-action": "抱歉,出现一些错误,您需要联系应用程序作者以获取帮助。\n\n<span class=\"plainlinks mw-mwoautherror-details\">未知URL,$1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "抱歉,出现一些错误。您需要[$1 联络]应用程序作者以获取帮助。\n\n<span class=\"plainlinks mw-mwoautherror-details\">未知URL,$2</span>",
+ "mwoauthdatastore-access-token-not-found": "对于那个授权令牌,找不到批准的功能。",
+ "mwoauthdatastore-request-token-not-found": "抱歉,连接此应用程序时出错。返回并重新尝试连接您的账户,或联络应用程序开发者。\n\n<span class=\"plainlinks mw-mwoautherror-details\">找不到OAuth令牌,$1</span>",
+ "mwoauthdatastore-callback-not-found": "缓存中找不到OAuth回调URL。这可能是应用程序向服务器发送请求时出现的错误。",
+ "mwoauthdatastore-request-token-already-used": "此请求已经完成,不能重复提交。回到应用程序并再次尝试连接您的账户,或联络应用程序作者。\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth令牌已被使用,$1</span>",
+ "mwoauthdatastore-bad-token": "找不到与您的请求相匹配的令牌。",
+ "mwoauthdatastore-bad-source-ip": "请求来自无效的IP地址。",
+ "mwoauthdatastore-bad-verifier": "提交的验证代码无效。",
+ "mwoauthdatastore-invalid-token-type": "请求令牌的类型无效。",
+ "mwoauthgrants-general-error": "您的OAuth请求中存在错误。",
+ "mwoauthserver-bad-consumer": "“$1”未被批准作为可连接的应用程序。请[$2 联络]应用程序作者获得帮助。\n\n<span class=\"plainlinks mw-mwoautherror-details\">已连接的OAuth应用程序未获批准,$3</span>",
+ "mwoauthserver-bad-consumer-key": "抱歉,此程序连接时出现一些错误。\n\n<span class=\"plainlinks mw-mwoautherror-details\">未知的OAuth密钥,$1</span>",
+ "mwoauthserver-insufficient-rights": "您的账户不允许使用连接的应用程序,请联系您的网站管理员了解原因。\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth用户权限不足,$1</span>",
+ "mwoauthserver-invalid-request-token": "您的申请中有无效令牌。",
+ "mwoauthserver-invalid-user": "要使用此网站连接的程序,您必须拥有跨所有项目的账户。当您在所有项目上拥有账户后,您才能再次尝试连接“$1”。\n\n<span class=\"plainlinks mw-mwoautherror-details\">需要统一登录,$2</span>",
+ "mwoauthserver-consumer-no-secret": "抱歉,此程序连接时出现一些错误。\n\n<span class=\"plainlinks mw-mwoautherror-details\">未知的OAuth密钥,$1</span>",
+ "mwoauthserver-consumer-owner-only": "“$1”是一个仅限主人连接的应用程序。要检索访问令牌,请参见[[$2]]。\n\n<span class=\"plainlinks mw-mwoautherror-details\">Consumer仅限主人,$3</span>",
+ "mwoauth-invalid-authorization-title": "OAuth认证错误",
+ "mwoauth-invalid-authorization": "您申请中的授权标头无效:$1",
+ "mwoauth-invalid-authorization-wrong-wiki": "您申请中的授权标头对于$1无效",
+ "mwoauth-invalid-authorization-invalid-user": "您的授权表头用于不存在的用户",
+ "mwoauth-invalid-authorization-wrong-user": "您的请求的授权标头用于另一用户",
+ "mwoauth-invalid-authorization-not-approved": "您尝试连接的应用程序似乎设置不正确。请联络作者“$1”获得帮助。",
+ "mwoauth-invalid-authorization-blocked-user": "您请求的许可表头来自被封禁用户",
+ "mwoauth-form-description-allwikis": "您好$1,\n\n为了完成您的请求,'''$2'''需要权限以代表您在此网站所有项目上执行以下操作:\n\n$4",
+ "mwoauth-form-description-onewiki": "您好$1,\n\n为了完成您的请求,'''$2'''需要权限以代表您在''$4''上执行以下操作:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "您好$1,\n\n为了完成您的请求,'''$2'''需要权限以在此网站所有项目中代表您访问信息。我们不会对您的账户做出更改。",
+ "mwoauth-form-description-onewiki-nogrants": "您好$1,\n\n为了完成您的请求,'''$2'''需要权限以在''$4''代表您访问信息。我们不会对您的账户做出更改。",
+ "mwoauth-form-description-allwikis-privateinfo": "您好$1,\n\n為了完成您的請求,'''$2'''需要權限以在此網站所有項目上存取有關您的資訊,這包括您的真實姓名和電子郵件地址。我們不會對您的帳戶做出更改。",
+ "mwoauth-form-description-onewiki-privateinfo": "您好$1,\n\n为了完成您的请求,'''$2'''需要权限以在''$4''访问信息,这包括您的真实姓名和电子邮件地址。我们不会对您的账户做出更改。",
+ "mwoauth-form-button-approve": "允许",
+ "mwoauth-form-button-cancel": "取消",
+ "mwoauth-error": "应用程序连接错误",
+ "mwoauth-grants-heading": "已申请权限:",
+ "mwoauth-grants-nogrants": "应用程序并无任何权限请求。",
+ "mwoauth-acceptance-cancelled": "您已选择不允许“$1”连接您的账户。“$1”将不会工作除非您重新允许它。您可回到“$1”或[[Special:OAuthManageMyGrants|管理]]您的已连接应用程序。",
+ "mwoauth-granttype-normal": "请求为特定权限授权。",
+ "grant-mwoauth-authonly": "仅限用户身份验证,没有读取页面或代表某位用户的能力。",
+ "grant-mwoauth-authonlyprivate": "用户身份验证只访问真实姓名和电子邮件地址,没有读取页面或代表某位用户的能力。",
+ "mwoauth-listgrants-extra-summary": "== OAuth特定授权 ==\n\n这些额外的授权适用于OAuth消费者。",
+ "mwoauth-oauth-exception": "OAuth 协议发生错误:$1",
+ "mwoauth-callback-not-oob": "oauth_callback必须设置且必须设置为“oob”(区分大小写)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback 必须设定,并且必须设置为“oob”(大小写敏感),或者配置回调必须是提供回调的一个前缀。",
+ "right-mwoauthproposeconsumer": "提议新的OAuth消费者",
+ "right-mwoauthupdateownconsumer": "更新您控制的OAuth消费者",
+ "right-mwoauthmanageconsumer": "管理OAuth消费者",
+ "right-mwoauthsuppress": "阻止OAuth消费者",
+ "right-mwoauthviewsuppressed": "查看已阻止的OAuth消费者",
+ "right-mwoauthviewprivate": "查看私有OAuth数据",
+ "right-mwoauthmanagemygrants": "管理OAuth功能",
+ "action-mwoauthmanageconsumer": "管理OAuth消费者",
+ "action-mwoauthmanagemygrants": "管理您的OAuth功能",
+ "action-mwoauthproposeconsumer": "提议新的OAuth应用程序",
+ "action-mwoauthupdateownconsumer": "更新您控制的OAuth应用程序",
+ "action-mwoauthviewsuppressed": "查看已阻止的OAuth应用程序",
+ "mwoauth-tag-reserved": "以<code>OAuth CID:</code>开头的标签被OAuth保留使用。",
+ "mwoauth-botpasswords-note": "<strong>注意:</strong><span class=\"plainlinks\">[$1 OAuth]</span>比机器人密码更安全,并当机器人支持它时应当首选使用。",
+ "mwoauth-api-module-disabled": "“$1”模块在OAuth中不可用。",
+ "echo-category-title-oauth-owner": "OAuth开发",
+ "echo-pref-tooltip-oauth-owner": "通知我有关我创建的OAuth应用程序相关的事件。",
+ "echo-category-title-oauth-admin": "OAuth管理员",
+ "echo-pref-tooltip-oauth-admin": "通知我有关复核OAuth应用程序相关的事件。",
+ "notification-oauth-app-propose-title": "$1{{GENDER:$1|提议}}了一个新的OAuth应用:$2",
+ "notification-oauth-app-update-title": "$1{{GENDER:$1|更新}}了OAuth应用$2",
+ "notification-oauth-app-approve-title": "$1{{GENDER:$1|通过}}了{{GENDER:$3|您}}的OAuth应用($2)",
+ "notification-oauth-app-reject-title": "$1{{GENDER:$1|拒绝}}了{{GENDER:$3|您}}的OAuth应用($2)",
+ "notification-oauth-app-disable-title": "$1{{GENDER:$1|禁用}}了{{GENDER:$3|您}}的OAuth应用($2)",
+ "notification-oauth-app-reenable-title": "$1{{GENDER:$1|重启}}了{{GENDER:$3|您}}的OAuth应用($2)",
+ "notification-oauth-app-propose-subject": "$1在{{SITENAME}}{{GENDER:$1|提议}}了一个新的OAuth应用",
+ "notification-oauth-app-update-subject": "$1在{{SITENAME}}{{GENDER:$1|更新}}了OAuth应用",
+ "notification-oauth-app-approve-subject": "$1在{{SITENAME}}{{GENDER:$1|通过}}了{{GENDER:$3|您}}的OAuth应用",
+ "notification-oauth-app-reject-subject": "$1在{{SITENAME}}{{GENDER:$1|拒绝}}了{{GENDER:$3|您}}的OAuth应用",
+ "notification-oauth-app-disable-subject": "$1在{{SITENAME}}{{GENDER:$1|禁用}}了{{GENDER:$3|您}}的OAuth应用",
+ "notification-oauth-app-reenable-subject": "$1在{{SITENAME}}{{GENDER:$1|重启}}了{{GENDER:$3|您}}的OAuth应用",
+ "notification-oauth-app-propose-primary-link": "复核应用",
+ "notification-oauth-app-update-primary-link": "复核应用",
+ "notification-oauth-app-approve-primary-link": "查看应用",
+ "notification-oauth-app-reject-primary-link": "查看应用",
+ "notification-oauth-app-disable-primary-link": "查看应用",
+ "notification-oauth-app-reenable-primary-link": "查看应用",
+ "notification-oauth-app-body": "原因:$1"
+}
diff --git a/OAuth/i18n/zh-hant.json b/OAuth/i18n/zh-hant.json
new file mode 100644
index 00000000..57cdb53a
--- /dev/null
+++ b/OAuth/i18n/zh-hant.json
@@ -0,0 +1,337 @@
+{
+ "@metadata": {
+ "authors": [
+ "A2093064",
+ "Cwlin0416",
+ "Hello903hello",
+ "Justincheng12345",
+ "Kly",
+ "LNDDYL",
+ "Liuxinyu970226",
+ "SupaplexTW",
+ "Waihorace",
+ "Xiplus",
+ "一個正常人"
+ ]
+ },
+ "mwoauth-desc": "允許使用 OAuth 1.0a 與 OAuth 2.0 作為 API 授權程式",
+ "mwoauth-nosubpage-explanation": "OAuth 是允許外部應用程式在收到來自使用者的許可後,去識別出{{SITENAME}}使用者或是代表他們做出行動的一種機制。\n\n要讓此頁面做出某些事情,會需要更多的參數。如果您是從外部應用程式發出,可能會是該應用程式的錯誤所導致,您應得向該應用程式的作者聯絡。",
+ "mwoauth-verified": "應用程式現在已允許您以個人的名義連結 MediaWiki。\n\n要完成這個程序,請提供應用程式此確認金鑰:'''$1'''",
+ "mwoauth-db-readonly": "OAuth 已資料庫暫時鎖定,請幾分鐘後再試。",
+ "mwoauth-missing-field": "未填寫 \"$1\" 欄位的欄位值",
+ "mwoauth-invalid-field": "給 \"$1\" 欄位的欄位值無效",
+ "mwoauth-invalid-field-generic": "欄位值無效",
+ "mwoauth-field-hidden": "(此為隱藏資訊)",
+ "mwoauth-field-private": "(此為非公開資訊)",
+ "mwoauth-prefs-managegrants": "已連接的應用程式:",
+ "mwoauth-prefs-managegrantslink": "管理{{PLURAL:$1|$1個已連接應用程式|$1個已連接應用程式|0=已連接應用程式}}",
+ "mwoauth-consumer-allwikis": "於此網站的所有專案",
+ "mwoauth-consumer-key": "Consumer 金鑰:",
+ "mwoauth-consumer-name": "應用程式名稱:",
+ "mwoauth-consumer-version": "Consumer 版本:",
+ "mwoauth-consumer-user": "發佈者:",
+ "mwoauth-consumer-stage": "目前狀態:",
+ "mwoauth-consumer-email": "聯絡人電子郵件地址:",
+ "mwoauth-consumer-email-help": "僅能由受批准的 consumer 可見",
+ "mwoauth-consumer-owner-only-label": "只限擁有者:",
+ "mwoauth-consumer-owner-only": "此 consumer 僅能由 $1 使用。",
+ "mwoauth-consumer-owner-only-help": "選擇此選項將會導致 consumer 被自動核准,且接受由$1使用。這不會讓其他使用者可用,且慣有的授權流程不會進行。採用此 consumer 的操作不會被標記。",
+ "mwoauth-consumer-description": "應用程式描述:",
+ "mwoauth-consumer-callbackurl": "OAuth「回呼」URL:",
+ "mwoauth-consumer-callbackisprefix": "允許 Consumer 在請求中指定一個回呼並使用上面的「回呼」URL作為一個必需的前綴。",
+ "mwoauth-consumer-granttypes": "請求的權限類型:",
+ "mwoauth-consumer-grantsneeded": "適用的權限:",
+ "mwoauth-consumer-required-grant": "適用於 Consumer",
+ "mwoauth-consumer-wiki": "適用的專案:",
+ "mwoauth-consumer-wiki-thiswiki": "現有專案 ($1)",
+ "mwoauth-consumer-restrictions": "使用限制:",
+ "mwoauth-consumer-restrictions-json": "使用限制 (JSON):",
+ "mwoauth-consumer-rsakey": "公開 RSA 金鑰 (選填):",
+ "mwoauth-consumer-rsakey-help": "輸入一個使用 RSA-SHA1 簽名方法的公鑰。留空會使用帶有隨機機密亂數的 HMAC-SHA1。若您對此不確定,請留空。",
+ "mwoauth-consumer-secretkey": "Consumer 私密權杖:",
+ "mwoauth-consumer-accesstoken": "存取金鑰:",
+ "mwoauth-consumer-reason": "原因:",
+ "mwoauth-consumer-developer-agreement": "通過提交此應用程式,您承認我們保留禁用您的應用程式,移除或限制您或您的應用程式對此網站的存取權的權利,並當我們相信根據我們自身的判斷,您或您的應用程式違反此網站的任何方針、指引和指導原則時,繼續從事任何其他我們認為適當的操作過程。我們可以在我們謹慎判斷認為必要時,隨時更改此應用程式方針,而不事先通知。您繼續使用OAuth就意味著認可這些更改。",
+ "mwoauth-consumer-email-unconfirmed": "您的帳號的電子郵件地址尚未被確認。",
+ "mwoauth-consumer-email-mismatched": "提供的電子郵件地址必須您的帳號相符。",
+ "mwoauth-consumer-alreadyexists": "使用此名稱/版本/發佈者的 Consumer 已存在",
+ "mwoauth-consumer-alreadyexistsversion": "使用此名稱/版本/發佈者的 Consumer 已存在且使用相等或較高的版本 (\"$1\")",
+ "mwoauth-consumer-not-accepted": "無法更新待申請 Consumer 請求的資訊",
+ "mwoauth-consumer-not-proposed": "該 Consumer 尚未提出申請",
+ "mwoauth-consumer-not-disabled": "該 Consumer 尚未被停用",
+ "mwoauth-consumer-not-approved": "該 Consumer 尚未被批准 (可能已停用)",
+ "mwoauth-missing-consumer-key": "未提供 Consumer 金鑰。",
+ "mwoauth-invalid-consumer-key": "沒有使用此金鑰的 Consumer。",
+ "mwoauth-invalid-access-token": "不存在所提供密鑰的存取權杖。",
+ "mwoauth-invalid-access-wrongwiki": "該 Consumer 僅可於專案 \"$1\" 使用。",
+ "mwoauth-consumer-conflict": "有人已變更您目前檢視的 Consumer 屬性,請再試一次。 您可能會希望查看修改日誌。",
+ "mwoauth-consumer-grantshelp": "每一項授權都會給予使用者帳號權限清單的存取權。參考[[Special:ListGrants|授權表格]]以取得更多資訊。",
+ "mwoauth-consumer-stage-proposed": "已提出",
+ "mwoauth-consumer-stage-rejected": "已拒絕",
+ "mwoauth-consumer-stage-expired": "已過期",
+ "mwoauth-consumer-stage-approved": "已批准",
+ "mwoauth-consumer-stage-disabled": "已停用",
+ "mwoauth-consumer-stage-suppressed": "已禁止",
+ "oauthconsumerregistration": "OAuth Consumer 註冊",
+ "mwoauthconsumerregistration-navigation": "導覽:",
+ "mwoauthconsumerregistration-propose": "提出新的 Consumer 申請",
+ "mwoauthconsumerregistration-list": "我的 Consumer 清單",
+ "mwoauthconsumerregistration-main": "主頁",
+ "mwoauthconsumerregistration-propose-text": "開發人員應使用以下表單提出申請新的OAuth Consumer(請參考[https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth 擴充套件說明文件]取得更多詳細資訊)。在送出這個表單後,您將會收到一組權杖(Token),可用來讓MediaWiki識別您的應用程式。在這之前OAuth管理員需要先核准您的應用程式。\n\n以下有幾點建議要特別說明:\n* 盡可能減少授權,避免目前不需的授權。\n* 版本的格式為「major.minor.release」(最後兩項為選填)並且需隨著授權變更時遞增。\n* 若可能,請提供一組公開RSA金鑰(使用PEM格式),否則將會採用安全性較低的秘密密鑰。\n* 您可使用專案ID來限制Consumer在網站中能存取的專案(使用「*」代表所有專案)。",
+ "mwoauthconsumerregistration-update-text": "使用以下表單來更新你可管控的 OAuth Consumer。\n\n所有填寫的數值將會覆蓋先前的數值,若您沒有要清除欄位值,請勿在欄位上留空。",
+ "mwoauthconsumerregistration-maintext": "此頁面可以讓開發人員提出申請與更新於本站登錄資料中的 OAuth Consumer 應用程式。\n\n在這裡您可以:\n* [[Special:OAuthConsumerRegistration/propose|申請新 Consumer 的權杖(Token)]]。\n* [[Special:OAuthConsumerRegistration/list|管理您已有的 Consumer]]。\n\n欲取得更多有關 OAuth 的資訊請參考[https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:OAuth 擴充套件說明文件]。",
+ "mwoauthconsumerregistration-propose-legend": "新增 OAuth Consumer 應用程式",
+ "mwoauthconsumerregistration-update-legend": "更新 OAuth Consumer 應用程式",
+ "mwoauthconsumerregistration-propose-submit": "提出 Consumer 申請",
+ "mwoauthconsumerregistration-update-submit": "更新 Consumer",
+ "mwoauthconsumerregistration-none": "您未管控任何 OAuth Consumer。",
+ "mwoauthconsumerregistration-name": "Consumer",
+ "mwoauthconsumerregistration-user": "發佈者",
+ "mwoauthconsumerregistration-description": "描述",
+ "mwoauthconsumerregistration-email": "聯絡人電子郵件",
+ "mwoauthconsumerregistration-consumerkey": "Consumer 金鑰",
+ "mwoauthconsumerregistration-stage": "狀態",
+ "mwoauthconsumerregistration-lastchange": "最新變更",
+ "mwoauthconsumerregistration-manage": "管理",
+ "mwoauthconsumerregistration-resetsecretkey": "重設祕密金鑰為新的值",
+ "mwoauthconsumerregistration-proposed": "您的 OAuth Consumer 請求已被接受。\n\n您已被分配了一組 Consumer 權杖 '''$1''',與秘密權杖 '''$2'''。''請將妥善保存這些資訊供未來參考使用。''",
+ "mwoauthconsumerregistration-created-owner-only": "您的 OAuth Consumer 已建立。\n\n您的權杖為:\n; Consumer 權杖:$1\n; Consumer 密鑰:$2\n; 存取權杖:$3\n; 存取密鑰:$4\n<em>請記下以上資訊供日後參考。</em>",
+ "mwoauthconsumerregistration-created-owner-only-oauth2": "您的 OAuth 2.0 客戶端已建立。\n\n您的權杖為:\n; 客戶端應用程式鍵:$1\n; 客戶端應用程式密鑰:$2\n; 存取權杖:$3\n<em>請記下以上資訊供日後參考。</em>",
+ "mwoauthconsumerregistration-updated": "已更新您的 OAuth Consumer 登入資訊。",
+ "mwoauthconsumerregistration-secretreset": "您已被分配了一組 Consumer 權杖 '''$1'''。''請將妥善保存這些資訊供未來參考使用。''",
+ "mwoauthconsumerregistration-secretreset-owner-only": "您的 OAuth Consumer 權杖已重新設定,新權杖為:\n; Consumer 權杖:$1\n; Consumer 密鑰:$2\n; 存取權杖:$3\n; 存取密鑰:$4\n<em>請記下以上資訊供日後參考。</em>",
+ "mwoauthconsumerregistration-secretreset-owner-only-oauth2": "您的 OAuth Consumer 權杖已重新設定,新權杖為:\n; Consumer 權杖:$1\n; Consumer 密鑰:$2\n; 存取權杖:$3\n<em>請記下以上資訊供日後參考。</em>",
+ "mwoauthconsumerregistration-need-emailconfirmed": "在建立 OAuth 應用程式之前您必須確認您的電子郵件地址。\n請透過[[Special:Preferences|偏好設定]]設定並驗證您的電子郵件地址。",
+ "oauthmanageconsumers": "管理 OAuth Consumer",
+ "mwoauthmanageconsumers-notloggedin": "您必須登入以存取此頁面。",
+ "mwoauthmanageconsumers-type": "佇列:",
+ "mwoauthmanageconsumers-showproposed": "已建議的請求",
+ "mwoauthmanageconsumers-showrejected": "已拒絕的請求",
+ "mwoauthmanageconsumers-showexpired": "已過期的請求",
+ "mwoauthmanageconsumers-linkproposed": "提議的請求",
+ "mwoauthmanageconsumers-linkrejected": "拒絕的請求",
+ "mwoauthmanageconsumers-linkexpired": "過期的請求",
+ "mwoauthmanageconsumers-linkapproved": "通過的請求",
+ "mwoauthmanageconsumers-linkdisabled": "禁用的請求",
+ "mwoauthmanageconsumers-main": "主頁",
+ "mwoauthmanageconsumers-maintext": "此頁面可用來處理 OAuth (see http://oauth.net)Consumer 應用程式申請與管理已建立的 OAuth Consumer。",
+ "mwoauthmanageconsumers-queues": "在下方選擇一個 consumer 確認佇列:",
+ "mwoauthmanageconsumers-q-proposed": "已提出申請的 Consumer 請求序列",
+ "mwoauthmanageconsumers-q-rejected": "已拒絕的 Consumer 請求序列",
+ "mwoauthmanageconsumers-q-expired": "已過期的 Consumer 請求序列",
+ "mwoauthmanageconsumers-lists": "選擇下面使用者狀況列表",
+ "mwoauthmanageconsumers-l-approved": "目前已核准的 Consumer 清單",
+ "mwoauthmanageconsumers-l-disabled": "目前已停用的 Consumer 清單",
+ "mwoauthmanageconsumers-none-proposed": "此清單沒有任提出申請的 Consumer。",
+ "mwoauthmanageconsumers-none-rejected": "此清單沒有任提出申請的 Consumer。",
+ "mwoauthmanageconsumers-none-expired": "此清單沒有任提出申請的 Consumer。",
+ "mwoauthmanageconsumers-none-approved": "無 Consumer 符合此條件。",
+ "mwoauthmanageconsumers-none-disabled": "無 Consumer 符合此條件。",
+ "mwoauthmanageconsumers-name": "Consumer",
+ "mwoauthmanageconsumers-user": "發佈者",
+ "mwoauthmanageconsumers-description": "描述",
+ "mwoauthmanageconsumers-email": "聯絡人電子郵件",
+ "mwoauthmanageconsumers-consumerkey": "Consumer 金鑰",
+ "mwoauthmanageconsumers-lastchange": "上次變更",
+ "mwoauthmanageconsumers-review": "審查/管理",
+ "mwoauthmanageconsumers-confirm-text": "使用此表單來批准、 拒絕、 停用或重新開啟此 Consumer。",
+ "mwoauthmanageconsumers-confirm-legend": "管理 OAuth Consumer",
+ "mwoauthmanageconsumers-action": "變更狀態:",
+ "mwoauthmanageconsumers-approve": "已批准",
+ "mwoauthmanageconsumers-reject": "已拒絕",
+ "mwoauthmanageconsumers-rsuppress": "已拒絕且已禁止",
+ "mwoauthmanageconsumers-disable": "已停用",
+ "mwoauthmanageconsumers-dsuppress": "已停用且已禁止",
+ "mwoauthmanageconsumers-reenable": "已批准",
+ "mwoauthmanageconsumers-reason": "原因:",
+ "mwoauthmanageconsumers-confirm-submit": "更新 Consumer 狀態",
+ "mwoauthmanageconsumers-success-approved": "已核准請求。",
+ "mwoauthmanageconsumers-success-rejected": "已拒絕請求。",
+ "mwoauthmanageconsumers-success-disabled": "已停用 Consumer。",
+ "mwoauthmanageconsumers-success-reanable": "已重新啟用 Consumer。",
+ "mwoauthmanageconsumers-search-name": "使用此名稱的 Consumer",
+ "mwoauthmanageconsumers-search-publisher": "由此使用者申請的 Consumer",
+ "oauthlistconsumers": "OAuth 應用程式清單",
+ "mwoauthlistconsumers-legend": "瀏覽 OAuth 應用程式",
+ "mwoauthlistconsumers-view": "詳細資料",
+ "mwoauthlistconsumers-none": "沒有應用程式符合此條件。",
+ "mwoauthlistconsumers-name": "應用程式名稱",
+ "mwoauthlistconsumers-version": "Consumer 版本",
+ "mwoauthlistconsumers-user": "發佈者",
+ "mwoauthlistconsumers-description": "描述",
+ "mwoauthlistconsumers-wiki": "適用的專案",
+ "mwoauthlistconsumers-callbackurl": "OAuth \"回呼 URL\"",
+ "mwoauthlistconsumers-callbackisprefix": "允許 Consumer 在請求中指定一個回呼並使用上面的「回呼」URL 作為一個必需的前綴。",
+ "mwoauthlistconsumers-grants": "適用的授權",
+ "mwoauthlistconsumers-basicgrantsonly": "(僅基本存取)",
+ "mwoauthlistconsumers-status": "狀態",
+ "mwoauth-consumer-stage-any": "任何",
+ "mwoauthlistconsumers-status-proposed": "已提出",
+ "mwoauthlistconsumers-status-approved": "已批准",
+ "mwoauthlistconsumers-status-disabled": "已停用",
+ "mwoauthlistconsumers-status-rejected": "已拒絕",
+ "mwoauthlistconsumers-status-expired": "已過期",
+ "mwoauthlistconsumers-navigation": "導覽:",
+ "mwoauthlistconsumers-update-link": "更新 Consumer",
+ "mwoauthlistconsumers-manage-link": "管理 Consumer",
+ "mwoauthlistconsumers-grants-link": "管理授權",
+ "mwoauthlistconsumers-rclink": "透過此應用程式做的近期變更",
+ "oauthmanagemygrants": "管理已連接的應用程式",
+ "mwoauthmanagemygrants-text": "此頁面列出了可以使用您帳號的所有應用程式。這些應用程式的可存取範圍取決於您授予這些應用程式可代表您操作時所允許的限制條件。如果您單獨授予一個應用程式可代表您存取不同的姊妹專案,那麼您將會在下面看到每個專案的單獨配置。\n\n已連接的應用程式將透過 OAuth 協定存取您的帳號。<span class=\"plainlinks\">([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:OAuth 詳細瞭解有關已連接的應用程式])</span>",
+ "mwoauthmanagemygrants-navigation": "導覽:",
+ "mwoauthmanagemygrants-showlist": "已連接的應用程式清單",
+ "mwoauthmanagemygrants-none": "還沒有應用程式連接至你的帳號。",
+ "mwoauthmanagemygrants-user": "發佈者:",
+ "mwoauthmanagemygrants-description": "描述",
+ "mwoauthmanagemygrants-wikiallowed": "允許的專案:",
+ "mwoauthmanagemygrants-grants": "適用的授權",
+ "mwoauthmanagemygrants-grantsallowed": "允許的授權",
+ "mwoauthmanagemygrants-applicablegrantsallowed": "允許的適用授權:",
+ "mwoauthmanagemygrants-review": "管理存取",
+ "mwoauthmanagemygrants-revoke": "撤銷存取",
+ "mwoauthmanagemygrants-grantaccept": "已授權",
+ "mwoauthmanagemygrants-update-text": "使用以下表單來變動代表您操作的應用程式之授予權限。",
+ "mwoauthmanagemygrants-revoke-text": "使用下面的表單以您的身分撤銷應用程式的存取權。",
+ "mwoauthmanagemygrants-confirm-legend": "管理已連接的應用程式",
+ "mwoauthmanagemygrants-update": "更新授權",
+ "mwoauthmanagemygrants-renounce": "取消認證",
+ "mwoauthmanagemygrants-action": "變更狀態:",
+ "mwoauthmanagemygrants-confirm-submit": "更新存取權杖狀態",
+ "mwoauthmanagemygrants-success-update": "您對此應用程序的參數設置已更新。",
+ "mwoauthmanagemygrants-success-renounce": "已取消應用程序對您賬戶的訪問權限。",
+ "mwoauthmanagemygrants-basic-tooltip": "為何我無法更新此授予?此授予給予您的需功能正常、已連接應用程式基本權限。若您不想此連接應用程式擁有這些權限,您應撤回應用程式可使用的存取。",
+ "mwoauthmanagemygrants-authonly-tooltip": "為何我無法更新此授予?若您不想要此連接應用程式擁有此權限,您應撤回應用程式可使用的存取。",
+ "mwoauthmanagemygrants-editslink": "{{GENDER:$1|您}}透過此應用程式做的編輯",
+ "mwoauthmanagemygrants-actionslink": "{{GENDER:$1|您}}透過此應用程式做的操作",
+ "logentry-mwoauthconsumer-propose": "$1 {{GENDER:$2|已提出申請}} OAuth Consumer (Consumer 金鑰 $4)",
+ "logentry-mwoauthconsumer-update": "$1 {{GENDER:$2|已更新}} OAuth Consumer (Consumer 金鑰 $4)",
+ "logentry-mwoauthconsumer-approve": "$1 {{GENDER:$2|已核准}}由 $3 申請的 OAuth Consumer (Consumer 金鑰 $4)",
+ "logentry-mwoauthconsumer-reject": "$1 {{GENDER:$2|已拒絕}}由 $3 申請的 OAuth Consumer (Consumer 金鑰 $4)",
+ "logentry-mwoauthconsumer-disable": "$1 {{GENDER:$2|已停用}}由 $3 申請的 OAuth Consumer (Consumer 金鑰 $4)",
+ "logentry-mwoauthconsumer-reenable": "$1 {{GENDER:$2|已重新開啟}}由 $3 申請的 OAuth Consumer (Consumer 金鑰 $4)",
+ "logentry-mwoauthconsumer-create-owner-only": "$1 {{GENDER:$2|已建立}}一個僅限擁有者的 OAuth Consumer(Consumer 金鑰 $4)",
+ "log-action-filter-mwoauthconsumer": "OAuth consumer 類型操作:",
+ "log-action-filter-mwoauthconsumer-approve": "OAuth consumer 核准",
+ "log-action-filter-mwoauthconsumer-create-owner-only": "僅擁有者的 OAuth consumer 創建",
+ "log-action-filter-mwoauthconsumer-disable": "OAuth consumer 停用",
+ "log-action-filter-mwoauthconsumer-propose": "OAuth consumer 提議",
+ "log-action-filter-mwoauthconsumer-reenable": "OAuth consumer 重新啟用",
+ "log-action-filter-mwoauthconsumer-reject": "OAuth consumer 拒絕",
+ "log-action-filter-mwoauthconsumer-update": "OAuth consumer 更新",
+ "mwoauthconsumer-consumer-logpage": "OAuth Consumer 日誌",
+ "mwoauthconsumer-consumer-logpagetext": "核准、拒絕,以及停用註冊 OAuth consumer 的日誌。",
+ "mwoauth-bad-request-missing-params": "很抱歉,設置此連接應用程式時發生錯誤。請聯絡應用程式的開發者。\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth 缺少參數,$1</span>",
+ "mwoauth-bad-request-invalid-action": "很抱歉,發生一些錯誤,您需要聯絡應用程式作者來取得幫助。\n\n<span class=\"plainlinks mw-mwoautherror-details\">未知的 URL,$1</span>",
+ "mwoauth-bad-request-invalid-action-contact": "很抱歉,發生一些錯誤。您需要[$1 聯絡]應用程式作者來取得幫助。\n\n<span class=\"plainlinks mw-mwoautherror-details\">未知的 URL,$2</span>",
+ "mwoauthdatastore-access-token-not-found": "查無該授權權杖已核准的授予。",
+ "mwoauthdatastore-request-token-not-found": "抱歉,連接此應用程式時出錯。返回並重新嘗試連接您的帳號,或聯繫應用程式開發人員。<span class=\"plainlinks mw-mwoautherror-details\">未找到 OAuth 的權杖,$1</span>",
+ "mwoauthdatastore-callback-not-found": "快取裡找不到 OAuth 回呼 URL。這可能是應用程式向伺服器建立請求時發生的錯誤。",
+ "mwoauthdatastore-request-token-already-used": "此請求已完成並不可再次提交。\n請返回到應用程式並再次嘗試連接您的帳號,或是聯絡應用程式作者。\n\n<span class=\"plainlinks mw-mwoautherror-details\">OAuth 權杖已被使用,$1</span>",
+ "mwoauthdatastore-bad-token": "找不到符合您請求的權杖。",
+ "mwoauthdatastore-bad-source-ip": "請求來自於無效的 IP 位址。",
+ "mwoauthdatastore-bad-verifier": "提交的驗證碼無效。",
+ "mwoauthdatastore-invalid-token-type": "請求的權杖類型無效。",
+ "mwoauthgrants-general-error": "您的 OAuth 請求中存在錯誤。",
+ "mwoauthserver-bad-consumer": "「$1」未被核准為可連接的應用程式,[$2 聯絡]應用程式作者尋求協助。\n<span class=\"plainlinks mw-mwoautherror-details\">已連接的 OAuth 應用程式未被核准,$3</span>",
+ "mwoauthserver-bad-consumer-key": "很抱歉,連結此應用程式時發生問題。\n\n<span class=\"plainlinks mw-mwoautherror-details\">未知的 OAuth 鍵,$1</span>",
+ "mwoauthserver-insufficient-rights": "您的帳號不允許使用連接的應用程式,請聯絡您的網站管理員來了解原因。\n\n<span class=\"plainlinks mw-mwoautherror-details\">不足的 OAuth 使用者權限,$1</span>",
+ "mwoauthserver-invalid-request-token": "您請求中的權杖無效。",
+ "mwoauthserver-invalid-user": "要使用此網站已連接的應用程式,您必須擁有跨所有專案的帳號。當您在所有專案上擁有帳號後,您才能再次嘗試連接「$1」。\n<span class=\"plainlinks mw-mwoautherror-details\">需要統一登入,$2</span>",
+ "mwoauthserver-consumer-no-secret": "很抱歉,連結此應用程式時發生問題。\n\n\n<span class=\"plainlinks mw-mwoautherror-details\">Consumer 未有密鑰,$1</span>",
+ "mwoauthserver-consumer-owner-only": "「$1」是一個僅限擁有者的連接應用程式。要索取存取權限,請參閱[[$2]].\n\n<span class=\"plainlinks mw-mwoautherror-details\">Consumer 僅限擁有者,$3</span>",
+ "mwoauth-invalid-authorization-title": "OAuth 授權錯誤",
+ "mwoauth-invalid-authorization": "您申請中的授權標頭無效:$1",
+ "mwoauth-invalid-authorization-wrong-wiki": "您請求裡的授權標頭對於 $1 無效",
+ "mwoauth-invalid-authorization-invalid-user": "您請求裡的認證標頭用在不存在的使用者",
+ "mwoauth-invalid-authorization-wrong-user": "您請求裡的授權標頭用在不同使用者",
+ "mwoauth-invalid-authorization-not-approved": "您嘗試連接的應用程式似乎設定有誤。請聯繫作者 \"$1\" 取得協助。",
+ "mwoauth-invalid-authorization-blocked-user": "您請求裡的認證標頭用在被封鎖的使用者",
+ "mwoauth-form-description-allwikis": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限以您的身分在此網站的所有專案上執行以下動作:\n\n$4",
+ "mwoauth-form-description-onewiki": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限使用你的身份在'''$4'''上執行以下動作:\n\n$5",
+ "mwoauth-form-description-allwikis-nogrants": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限以您的身分在此網站的所有專案上存取資訊。不會使用您的帳號做出任何修改。",
+ "mwoauth-form-description-onewiki-nogrants": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限以您的身分在'''$4'''上存取資訊。不會使用您的帳號做出任何修改。",
+ "mwoauth-form-description-allwikis-privateinfo": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限在此網站的所有專案上存取有關您的資訊,包含您的真實姓名與電子郵件地址。不會使用您的帳號做出任何修改。",
+ "mwoauth-form-description-onewiki-privateinfo": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限在'''$4'''上存取有關您的資訊,包含您的真實姓名與電子郵件地址。不會使用您的帳號做出任何修改。",
+ "mwoauth-form-description-allwikis-privateinfo-norealname": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限在此網站的所有專案上存取有關您的資訊,包含您的電子郵件地址。不會使用您的帳號做出任何修改。",
+ "mwoauth-form-description-onewiki-privateinfo-norealname": "$1您好,\n\n為了完成您的請求,'''$2'''需要權限在'''$4'''上存取有關您的資訊,包含您的電子郵件地址。不會使用您的帳號做出任何修改。",
+ "mwoauth-form-button-approve": "允許",
+ "mwoauth-form-button-cancel": "取消",
+ "mwoauth-error": "應用程式連接錯誤",
+ "mwoauth-grants-heading": "請求的權限:",
+ "mwoauth-grants-nogrants": "應用程式未請求任何權限。",
+ "mwoauth-acceptance-cancelled": "您已選擇不允許 \"$1\" 存取您的帳號,在您允許 \"$1\" 存取前將無法正常運作。 您可以返回 \"$1\" 或 [[Special:OAuthManageMyGrants|管理]] 您已連結的應用程式。",
+ "mwoauth-granttype-normal": "請求為特定權限授權。",
+ "grant-mwoauth-authonly": "僅限使用者身份驗證,沒有讀取頁面或代表某位使用者的能力。",
+ "grant-mwoauth-authonlyprivate": "使用者身份驗證只存取真實姓名和電子郵件地址,沒有讀取頁面或代表某位使用者的能力。",
+ "mwoauth-listgrants-extra-summary": "== OAuth特定授權 ==\n\n這些額外的授權適用於 OAuth Consumer。",
+ "mwoauth-oauth-exception": "OAuth 通訊協定發生錯誤:$1",
+ "mwoauth-callback-not-oob": "必須設定 oauth_callback,且須設為 \"oob\" (區分大小寫)",
+ "mwoauth-callback-not-oob-or-prefix": "oauth_callback 必須設定,且必須設定成「oob」(區分大小寫),或設置回呼為必須是所提供回呼的字首。",
+ "right-mwoauthproposeconsumer": "提出申請新的 OAuth Consumer",
+ "right-mwoauthupdateownconsumer": "更新你管控的 OAuth Consumer",
+ "right-mwoauthmanageconsumer": "管理 OAuth Consumer",
+ "right-mwoauthsuppress": "禁止使用 OAuth Consumer",
+ "right-mwoauthviewsuppressed": "檢示已禁止使用的 OAuth Consumer",
+ "right-mwoauthviewprivate": "檢視私有的 OAuth 資料",
+ "right-mwoauthmanagemygrants": "管理 OAuth 授權",
+ "action-mwoauthmanageconsumer": "管理 OAuth Consumer",
+ "action-mwoauthsuppress": "禁止使用 OAuth Consumer",
+ "action-mwoauthmanagemygrants": "管理您的 OAuth 授權",
+ "action-mwoauthproposeconsumer": "提出申請新的 OAuth Consumer",
+ "action-mwoauthupdateownconsumer": "更新你管控的 OAuth Consumer",
+ "action-mwoauthviewprivate": "檢視私有的 OAuth 資料",
+ "action-mwoauthviewsuppressed": "檢示已禁止使用的 OAuth Consumer",
+ "mwoauth-tag-reserved": "以<code>OAuth CID:</code>開頭的標籤被OAuth保留使用。",
+ "mwoauth-botpasswords-note": "<strong>注意:</strong><span class=\"plainlinks\">[$1 OAuth]</span>比機器人密碼更安全,並當機器人支援它時應當優先使用。",
+ "mwoauth-api-module-disabled": "「$1」模組在OAuth中不可用。",
+ "echo-category-title-oauth-owner": "OAuth開發",
+ "echo-pref-tooltip-oauth-owner": "通知我有關我建立的OAuth應用程式相關的事件。",
+ "echo-category-title-oauth-admin": "OAuth管理員",
+ "echo-pref-tooltip-oauth-admin": "通知我有關覆核OAuth應用程式相關的事件。",
+ "notification-oauth-app-propose-title": "$1{{GENDER:$1|提議}}了一個新的OAuth應用程式:$2",
+ "notification-oauth-app-update-title": "$1{{GENDER:$1|更新}}了OAuth應用程式$2",
+ "notification-oauth-app-approve-title": "$1{{GENDER:$1|通過}}了{{GENDER:$3|您}}的OAuth應用程式($2)",
+ "notification-oauth-app-reject-title": "$1{{GENDER:$1|拒絕}}了{{GENDER:$3|您}}的OAuth應用程式($2)",
+ "notification-oauth-app-disable-title": "$1{{GENDER:$1|禁用}}了{{GENDER:$3|您}}的OAuth應用程式($2)",
+ "notification-oauth-app-reenable-title": "$1{{GENDER:$1|重啟}}了{{GENDER:$3|您}}的OAuth應用程式($2)",
+ "notification-oauth-app-propose-subject": "$1在{{SITENAME}}{{GENDER:$1|提議}}了一個新的OAuth應用程式",
+ "notification-oauth-app-update-subject": "$1在{{SITENAME}}{{GENDER:$1|更新}}了OAuth應用程式",
+ "notification-oauth-app-approve-subject": "$1在{{SITENAME}}{{GENDER:$1|通過}}了{{GENDER:$3|您}}的OAuth應用程式",
+ "notification-oauth-app-reject-subject": "$1在{{SITENAME}}{{GENDER:$1|拒絕}}了{{GENDER:$3|您}}的OAuth應用程式",
+ "notification-oauth-app-disable-subject": "$1在{{SITENAME}}{{GENDER:$1|禁用}}了{{GENDER:$3|您}}的OAuth應用程式",
+ "notification-oauth-app-reenable-subject": "$1在{{SITENAME}}{{GENDER:$1|重啟}}了{{GENDER:$3|您}}的OAuth應用程式",
+ "notification-oauth-app-propose-primary-link": "覆核應用程式",
+ "notification-oauth-app-update-primary-link": "覆核應用程式",
+ "notification-oauth-app-approve-primary-link": "檢視應用程式",
+ "notification-oauth-app-reject-primary-link": "檢視應用程式",
+ "notification-oauth-app-disable-primary-link": "檢視應用程式",
+ "notification-oauth-app-reenable-primary-link": "檢視應用程式",
+ "notification-oauth-app-body": "原因:$1",
+ "mwoauth-oauth-version": "OAuth 協定版本",
+ "mwoauth-oauth2-is-confidential": "客戶端為機密",
+ "mwoauth-oauth2-is-confidential-help": "機密客戶端是能夠將客戶端密碼對外保密的應用程式。非機密客戶端的安全性則較低",
+ "mwoauth-oauth2-granttypes": "已允許 OAuth2 授予類型",
+ "mwoauth-oauth2-granttype-auth-code": "授權碼",
+ "mwoauth-oauth2-granttype-refresh-token": "重整權杖",
+ "mwoauth-oauth2-granttype-client-credentials": "客戶端機密",
+ "mwoauth-oauth2-error-create-at-no-user-approval": "無法建立存取權杖,使用者不核准發出此存取權杖",
+ "mwoauth-oauth2-error-user-approval-deny": "使用者拒絕來自於客戶端應用程式的請求",
+ "mwoauth-oauth-unsupported-version": "此端點不允許用於 OAuth 版本 $1",
+ "mwoauth-oauth2-error-unauthorized-scope": "範圍「$1」不允許用於此應用程式",
+ "mwoauth-oauth2-error-owner-only-invalid-grant": "僅限擁有者的客戶端必須被允許使用 client_credentials",
+ "mwoauth-oauth2-unable-to-retrieve-access-token": "無法取回存取權杖:$1",
+ "mwoauth-oauth2-error-server-error": "授權伺服器遭遇到意外情況,導致無法應付請求。",
+ "mwoauth-oauth2-error-invalid-request": "請求缺少所需要的參數、包含無效的參數值、包含出現一次以上的參數、或是其它格式錯誤。",
+ "mwoauth-oauth2-error-unauthorized-client": "客戶端無權請求一個使用此方法的授權碼。",
+ "mwoauth-oauth2-error-access-denied": "資源擁有者或授權伺服器拒絕了請求。",
+ "mwoauth-oauth2-error-unsupported-response-type": "授權伺服器不支援含有使用此方法的授權碼。",
+ "mwoauth-oauth2-error-invalid-scope": "請求範圍無效、未知、或是有誤。",
+ "mwoauth-oauth2-error-temporarily-unavailable": "由於有臨時負荷過重情況、或是伺服器正維護中緣故,授權伺服器目前無法處理請求。",
+ "mwoauth-oauth2-error-invalid-client": "客戶端授權失敗(如:未知的客戶端、未包含客戶端授權、或是未支援的授權方法)",
+ "mwoauth-oauth2-error-request-not-verified": "在驗證請求前,嘗試取得已驗證屬性",
+ "mwoauth-oauth2-invalid-access-token": "無效的存取權杖",
+ "mwoauth-consumer-access-no-user": "Consumer 核准必須綁定有效的使用者,指定的使用者 ID 為 0",
+ "mwoauth-login-required-reason": "您需要登入到您的{{SITENAME}}帳號來授權應用程式以對其存取。",
+ "mwoauthconsumer-consumer-view": "檢視此 Consumer",
+ "mwoauthconsumer-application-view": "檢視此應用程式"
+}
diff --git a/OAuth/maintenance/createOAuthConsumer.php b/OAuth/maintenance/createOAuthConsumer.php
new file mode 100644
index 00000000..f8ea6ef7
--- /dev/null
+++ b/OAuth/maintenance/createOAuthConsumer.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Example:
+ *
+ * createOAuthConsumer.php
+ * --callbackIsPrefix
+ * --callbackUrl="https://foourl"
+ * --description="Application description"
+ * --grants="editprotected"
+ * --grants="createaccount"
+ * --name="Application name"
+ * --user="Admin"
+ * --version="0.2"
+ * --wiki=default
+ * --approve
+ *
+ * You can optionally output successful results as json using --jsonOnSuccess
+ */
+
+namespace MediaWiki\Extensions\OAuth;
+
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Control\ConsumerSubmitControl;
+
+/**
+ * @ingroup Maintenance
+ */
+
+if ( getenv( 'MW_INSTALL_PATH' ) ) {
+ $IP = getenv( 'MW_INSTALL_PATH' );
+} else {
+ $IP = __DIR__ . '/../../..';
+}
+
+require_once "$IP/maintenance/Maintenance.php";
+
+class CreateOAuthConsumer extends \Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addDescription( "Create an OAuth consumer" );
+ $this->addOption( 'user', 'User to run the script as', true, true );
+ $this->addOption( 'name', 'Application name', true, true );
+ $this->addOption( 'description', 'Application description', true, true );
+ $this->addOption( 'version', 'Application version', true, true );
+ $this->addOption( 'callbackUrl', 'Callback URL', true, true );
+ $this->addOption(
+ 'callbackIsPrefix',
+ 'Allow a consumer to specify a callback in requests',
+ true
+ );
+ $this->addOption( 'grants', 'Grants', true, true, false, true );
+ $this->addOption( 'jsonOnSuccess', 'Output successful results as JSON' );
+ $this->addOption( 'approve', 'Accept the consumer' );
+ $this->requireExtension( "OAuth" );
+ }
+
+ public function execute() {
+ $user = \User::newFromName( $this->getOption( 'user' ) );
+ if ( $user->isAnon() ) {
+ $this->fatalError( 'User must be registered' );
+ }
+ if ( $user->getEmail() === '' ) {
+ $this->fatalError( 'User must have an email' );
+ }
+
+ $data = [
+ 'action' => 'propose',
+ 'name' => $this->getOption( 'name' ),
+ 'version' => $this->getOption( 'version' ),
+ 'description' => $this->getOption( 'description' ),
+ 'callbackUrl' => $this->getOption( 'callbackUrl' ),
+ 'callbackIsPrefix' => $this->hasOption( 'callbackIsPrefix' ),
+ 'grants' => '["' . implode( '","', $this->getOption( 'grants' ) ) . '"]',
+ 'granttype' => 'normal',
+ 'ownerOnly' => false,
+ 'email' => $user->getEmail(),
+ 'wiki' => '*', // All wikis
+ 'rsaKey' => '', // Generate a key
+ 'agreement' => true,
+ 'restrictions' => \MWRestrictions::newDefault(),
+ ];
+
+ $context = \RequestContext::getMain();
+ $context->setUser( $user );
+
+ $dbw = Utils::getCentralDB( DB_MASTER );
+ $control = new ConsumerSubmitControl( $context, $data, $dbw );
+ $status = $control->submit();
+
+ if ( !$status->isGood() ) {
+ $this->fatalError( $status->getMessage()->text() );
+ }
+
+ /** @var Consumer $cmr */
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $cmr = $status->value['result']['consumer'];
+
+ if ( $this->hasOption( 'approve' ) ) {
+ $data = [
+ 'action' => 'approve',
+ 'consumerKey' => $cmr->getConsumerKey(),
+ 'reason' => 'Approved by maintenance script',
+ 'changeToken' => $cmr->getChangeToken( $context ),
+ ];
+ $control = new ConsumerSubmitControl( $context, $data, $dbw );
+ $approveStatus = $control->submit();
+ }
+
+ $outputData = [
+ 'created' => true,
+ 'id' => $cmr->getId(),
+ 'name' => $cmr->getName(),
+ 'key' => $cmr->getConsumerKey(),
+ 'secret' => Utils::hmacDBSecret( $cmr->getSecretKey() ),
+ ];
+
+ if ( isset( $approveStatus ) ) {
+ $outputData['approved'] = $approveStatus->isGood() ?
+ 1 : $approveStatus->getWikiText( false, false, 'en' );
+ }
+
+ if ( $this->hasOption( 'jsonOnSuccess' ) ) {
+ $this->output( json_encode( $outputData ) );
+ } else {
+ foreach ( $outputData as $key => $value ) {
+ $this->output( $key . ': ' . $value . PHP_EOL );
+ }
+ }
+ }
+}
+
+$maintClass = CreateOAuthConsumer::class;
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/OAuth/maintenance/migrateCentralWiki.php b/OAuth/maintenance/migrateCentralWiki.php
new file mode 100644
index 00000000..9fdf2347
--- /dev/null
+++ b/OAuth/maintenance/migrateCentralWiki.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+/**
+ * Migrate oauth_registered_consumer and oauth_accepted_consumer tables to a
+ * new database with minimal downtime. This script assumes relatively small tables
+ * (The WMF has <100 consumers and aout 1000 authorizations right now).
+ *
+ * To migrate to a new central wiki within your cluster, you roughly want to:
+ * 1. Set $wgMWOAuthReadOnly = true for all wikis in your running config
+ * 2. Move the oauth_registered_consumer and oauth_accepted_consumer tables with this script
+ * 3. Update the cluster config to point to the new central wiki
+ * 4. Set $wgMWOAuthReadOnly back to false, so users can manage their consumers as normal.
+ * 5. Migrate the OAuth logs using importCentralWikiLogs.php.
+ * 6. Done!
+ *
+ * @ingroup Maintenance
+ */
+if ( getenv( 'MW_INSTALL_PATH' ) ) {
+ $IP = getenv( 'MW_INSTALL_PATH' );
+} else {
+ $IP = __DIR__ . '/../../..';
+}
+
+require_once "$IP/maintenance/Maintenance.php";
+
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthDAO;
+use MediaWiki\MediaWikiServices;
+
+class MigrateCentralWiki extends \Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addDescription( "Migrate central wiki from one wiki to another. " .
+ "OAuth should be in Read Only mode while this is running." );
+ $this->addOption( 'old', 'Previous central wiki', true, true );
+ $this->addOption( 'target', 'New central wiki', true, true );
+ $this->addOption( 'table',
+ 'Table name (oauth_registered_consumer or oauth_accepted_consumer)', true, true );
+ $this->setBatchSize( 200 );
+ $this->requireExtension( "OAuth" );
+ }
+
+ public function execute() {
+ $oldWiki = $this->getOption( 'old' );
+ $targetWiki = $this->getOption( 'target' );
+ $table = $this->getOption( 'table' );
+
+ if ( $table === 'oauth_registered_consumer' ) {
+ $idKey = 'oarc_id';
+ $cmrClass = Consumer::class;
+ $type = 'consumer';
+ } elseif ( $table === 'oauth_accepted_consumer' ) {
+ $idKey = 'oaac_id';
+ $cmrClass = ConsumerAcceptance::class;
+ $type = 'grant';
+ } else {
+ $this->error( "Invalid table name. Must be one of 'oauth_registered_consumer' " .
+ "or 'oauth_accepted_consumer'.\n", 1 );
+ throw new \LogicException();
+ }
+
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $oldDb = $lbFactory->getMainLB( $oldWiki )->getConnectionRef( DB_MASTER, [], $oldWiki );
+ $targetDb = $lbFactory->getMainLB( $targetWiki )
+ ->getConnectionRef( DB_MASTER, [], $targetWiki );
+ $targetDb->daoReadOnly = false;
+
+ $newMax = $targetDb->selectField(
+ $table,
+ "MAX($idKey)",
+ [],
+ __METHOD__
+ );
+
+ $oldMax = $oldDb->selectField(
+ $table,
+ "MAX($idKey)",
+ [],
+ __METHOD__
+ );
+
+ if ( $newMax >= $oldMax ) {
+ $this->output( "No new rows.\n" );
+ }
+
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+
+ for ( $currentId = $newMax + 1, $i = 1; $currentId <= $oldMax; ++$currentId, ++$i ) {
+ $this->output( "Migrating $type $currentId..." );
+ /** @var MWOAuthDAO $cmrClass */
+ $cmr = $cmrClass::newFromId( $oldDb, $currentId );
+ if ( $cmr ) {
+ $cmr->updateOrigin( 'new' );
+ $cmr->setPending( true );
+ $cmr->save( $targetDb );
+ $this->output( "done.\n" );
+ } else {
+ $this->output( "missing.\n" );
+ }
+
+ if ( $this->mBatchSize && $i % $this->mBatchSize === 0 ) {
+ $lbFactory->waitForReplication( [ 'domain' => $targetWiki ] );
+ }
+ }
+ }
+
+}
+
+$maintClass = MigrateCentralWiki::class;
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/OAuth/maintenance/migrateCentralWikiLogs.php b/OAuth/maintenance/migrateCentralWikiLogs.php
new file mode 100644
index 00000000..49e1105c
--- /dev/null
+++ b/OAuth/maintenance/migrateCentralWikiLogs.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * This script should be run as part of migrating to a new central OAuth wiki in your
+ * cluster. See the notes in migrateCentralWikiLogs.php for the complete process.
+ * This script is intended to be run on the new central wiki after the tables have already
+ * been migrated. This will fill in the logs from newest to oldest, and tries to do sane
+ * things if you need to stop it and restart it later.
+ *
+ * @ingroup Maintenance
+ */
+if ( getenv( 'MW_INSTALL_PATH' ) ) {
+ $IP = getenv( 'MW_INSTALL_PATH' );
+} else {
+ $IP = __DIR__ . '/../../..';
+}
+
+require_once "$IP/maintenance/Maintenance.php";
+
+use MediaWiki\MediaWikiServices;
+
+class MigrateCentralWikiLogs extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addDescription( "Import central wiki logs to this wiki" );
+ $this->addOption( 'old', 'Previous central wiki', true, true );
+ $this->setBatchSize( 200 );
+ $this->requireExtension( "OAuth" );
+ }
+
+ public function execute() {
+ $oldWiki = $this->getOption( 'old' );
+ $targetWiki = wfWikiID();
+
+ $this->output( "Moving OAuth logs from '$oldWiki' to '$targetWiki'\n" );
+
+ // We only read from $oldDb, but we do want to make sure we get the most recent logs.
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $oldDb = $lbFactory->getMainLB( $oldWiki )->getConnectionRef( DB_MASTER, [], $oldWiki );
+ $targetDb = $lbFactory->getMainLB( $targetWiki )
+ ->getConnectionRef( DB_MASTER, [], $targetWiki );
+
+ $targetMinTS = $targetDb->selectField(
+ 'logging',
+ "MIN(log_timestamp)",
+ [
+ 'log_type' => 'mwoauthconsumer',
+ ],
+ __METHOD__
+ );
+
+ $lastMinTimestamp = null;
+ if ( $targetMinTS !== false ) {
+ $lastMinTimestamp = $targetMinTS;
+ }
+
+ $commentStore = CommentStore::getStore();
+ $commentQuery = $commentStore->getJoin( 'log_comment' );
+ $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
+
+ do {
+ $conds = [ 'log_type' => 'mwoauthconsumer' ];
+
+ // This assumes that we don't have more than mBatchSize oauth log entries
+ // with the same timestamp. Otherwise this will go into an infinite loop.
+ if ( $lastMinTimestamp !== null ) {
+ $conds[] = 'log_timestamp < ' .
+ $oldDb->addQuotes( $oldDb->timestamp( $lastMinTimestamp ) );
+ }
+
+ $oldLoggs = $oldDb->select(
+ [ 'logging' ] + $commentQuery['tables'] + $actorQuery['tables'],
+ [
+ 'log_id', 'log_action', 'log_timestamp', 'log_params', 'log_deleted'
+ ] + $commentQuery['fields'] + $actorQuery['fields'],
+ $conds,
+ __METHOD__,
+ [
+ 'ORDER BY' => 'log_timestamp DESC',
+ 'LIMIT' => $this->mBatchSize + 1,
+ ],
+ $commentQuery['joins'] + $actorQuery['joins']
+ );
+
+ $rowCount = $oldLoggs->numRows();
+
+ if ( $rowCount == $this->mBatchSize + 1 ) {
+ $first = $oldLoggs->fetchObject();
+ $oldLoggs->seek( $rowCount - 2 );
+ $last = $oldLoggs->fetchObject();
+ if ( $first->log_timestamp === $last->log_timestamp ) {
+ $this->error( "Batch size too low to avoid infinite loop.\n", 1 );
+ }
+ $extra = $oldLoggs->fetchObject();
+ if ( $last->log_timestamp === $extra->log_timestamp ) {
+ $this->error( "We hit an edge case. Please increase the batch " .
+ " size and restart the transfer.\n", 1 );
+ }
+ $oldLoggs->rewind();
+ }
+
+ $targetDb->begin( __METHOD__ );
+ foreach ( $oldLoggs as $key => $row ) {
+ // Skip if this is the extra row we selected
+ if ( $key > $this->mBatchSize ) {
+ continue;
+ }
+
+ $lastMinTimestamp = $row->log_timestamp;
+
+ $this->output( "Migrating log {$row->log_id}...\n" );
+ $logUser = User::newFromName( $row->log_user_text );
+ if ( !$logUser->getId() ) {
+ $this->output(
+ "Cannot transfer log_id: {$row->log_id}, the log user doesn't exist"
+ );
+ continue;
+ }
+ $params = unserialize( $row->log_params );
+ if ( !isset( $params['4:consumer'] ) ) {
+ $this->output( "Cannot transfer log_id: {$row->log_id}, param isn't correct" );
+ continue;
+ }
+ $logEntry = new ManualLogEntry( 'mwoauthconsumer', $row->log_action );
+ $logEntry->setPerformer( $logUser );
+ $logEntry->setTarget( Title::makeTitleSafe( NS_USER, $row->log_user_text ) );
+ $logEntry->setComment( $commentStore->getComment( 'log_comment', $row )->text );
+ $logEntry->setParameters( $params );
+ $logEntry->setRelations( [
+ 'OAuthConsumer' => [ $params['4:consumer'] ]
+ ] );
+ // ManualLogEntry::insert() calls $dbw->timestamp on the value
+ $logEntry->setTimestamp( $row->log_timestamp );
+ // @TODO: Maybe this will do something some day. Sigh.
+ $logEntry->setDeleted( $row->log_deleted );
+ $logEntry->insert( $targetDb );
+ }
+ $targetDb->commit( __METHOD__ );
+
+ $lbFactory->waitForReplication();
+
+ } while ( $rowCount ); // This wastes an extra query, but keeps the logic simpler
+ }
+
+}
+
+$maintClass = MigrateCentralWikiLogs::class;
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/OAuth/maintenance/testOAuthConsumer.php b/OAuth/maintenance/testOAuthConsumer.php
new file mode 100644
index 00000000..8f8d7812
--- /dev/null
+++ b/OAuth/maintenance/testOAuthConsumer.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_HMAC_SHA1;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_RSA_SHA1;
+
+/**
+ * @ingroup Maintenance
+ */
+if ( getenv( 'MW_INSTALL_PATH' ) ) {
+ $IP = getenv( 'MW_INSTALL_PATH' );
+} else {
+ $IP = __DIR__ . '/../../..';
+}
+
+require_once "$IP/maintenance/Maintenance.php";
+
+class TestOAuthConsumer extends \Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addDescription( "Test an OAuth consumer" );
+ $this->addOption( 'consumerKey', 'Consumer key', true, true );
+ $this->addOption( 'consumerSecret', 'Consumer secret', false, true );
+ $this->addOption( 'RSAKeyFile',
+ 'File containing the RSA private key for the consumer', false, true
+ );
+ $this->addOption( 'useSSL', 'Use SSL' );
+ $this->addOption( 'verbose', 'Verbose output (e.g. HTTP request/response headers)' );
+ $this->requireExtension( "OAuth" );
+ }
+
+ public function execute() {
+ global $wgServer, $wgScriptPath;
+
+ $consumerKey = $this->getOption( 'consumerKey' );
+ $consumerSecret = $this->getOption( 'consumerSecret' );
+ $rsaKeyFile = $this->getOption( 'RSAKeyFile' );
+ $baseurl = wfExpandUrl(
+ "{$wgServer}{$wgScriptPath}/index.php?title=Special:OAuth", PROTO_CANONICAL );
+ $endpoint = "{$baseurl}/initiate&format=json&oauth_callback=oob";
+
+ $endpoint_acc = "{$baseurl}/token&format=json";
+
+ if ( !$consumerSecret && !$rsaKeyFile ) {
+ $this->error( "Either consumerSecret or RSAKeyFile required!" );
+ $this->maybeHelp( true );
+ }
+
+ $c = new OAuthConsumer( $consumerKey, $consumerSecret );
+ $parsed = parse_url( $endpoint );
+ $params = [];
+ // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
+ parse_str( $parsed['query'], $params );
+ $req_req = OAuthRequest::from_consumer_and_token( $c, null, "GET", $endpoint, $params );
+ if ( $rsaKeyFile ) {
+ try {
+ $sig_method = new class ( $rsaKeyFile ) extends OAuthSignatureMethod_RSA_SHA1 {
+ private $privKey, $pubKey;
+
+ public function __construct( $privKeyFile ) {
+ $key = file_get_contents( $privKeyFile );
+ if ( !$key ) {
+ throw new OAuthException( "Could not read private key file $privKeyFile" );
+ }
+
+ $privKey = openssl_pkey_get_private( $key );
+ if ( !$privKey ) {
+ throw new OAuthException( "File $privKeyFile does not contain a private key" );
+ }
+
+ $details = openssl_pkey_get_details( $privKey );
+ if ( $details['type'] !== OPENSSL_KEYTYPE_RSA ) {
+ throw new OAuthException( "Key is not an RSA key" );
+ }
+ if ( !$details['key'] ) {
+ throw new OAuthException( "Could not get public key from private key" );
+ }
+
+ $this->privKey = $key;
+ $this->pubKey = $details['key'];
+ }
+
+ protected function fetch_public_cert( &$request ) {
+ return $this->pubKey;
+ }
+
+ protected function fetch_private_cert( &$request ) {
+ return $this->privKey;
+ }
+ };
+ } catch ( OAuthException $ex ) {
+ $this->error( $ex->getMessage(), 1 );
+ }
+ } else {
+ $sig_method = new OAuthSignatureMethod_HMAC_SHA1();
+ }
+ $req_req->sign_request( $sig_method, $c, null );
+
+ $this->output( "Calling: $req_req\n" );
+
+ $ch = curl_init();
+ curl_setopt( $ch, CURLOPT_URL, (string)$req_req );
+ if ( $this->hasOption( 'useSSL' ) ) {
+ curl_setopt( $ch, CURLOPT_PORT, 443 );
+ }
+ if ( $this->hasOption( 'verbose' ) ) {
+ curl_setopt( $ch, CURLOPT_VERBOSE, true );
+ }
+ curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
+ curl_setopt( $ch, CURLOPT_HEADER, 0 );
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+ $data = curl_exec( $ch );
+
+ if ( !$data ) {
+ $this->output( 'Curl error: ' . curl_error( $ch ) );
+ }
+
+ $this->output( "Returned: $data\n\n" );
+
+ $token = json_decode( $data );
+ if ( !$token || !isset( $token->key ) ) {
+ $this->error( 'Could not fetch token', 1 );
+ }
+
+ $this->output( "Visit $baseurl/authorize" .
+ "&oauth_token={$token->key}&oauth_consumer_key=$consumerKey\n" );
+
+ // ACCESS TOKEN
+ $this->output( "Enter the verification code:\n" );
+ $fh = fopen( "php://stdin", "r" );
+ $line = fgets( $fh );
+
+ $rc = new OAuthConsumer( $token->key, $token->secret );
+ $parsed = parse_url( $endpoint_acc );
+ // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
+ parse_str( $parsed['query'], $params );
+ $params['oauth_verifier'] = trim( $line );
+
+ $acc_req = OAuthRequest::from_consumer_and_token( $c, $rc, "GET", $endpoint_acc, $params );
+ $acc_req->sign_request( $sig_method, $c, $rc );
+
+ $this->output( "Calling: $acc_req\n" );
+
+ unset( $ch );
+ $ch = curl_init();
+ curl_setopt( $ch, CURLOPT_URL, (string)$acc_req );
+ if ( $this->hasOption( 'useSSL' ) ) {
+ curl_setopt( $ch, CURLOPT_PORT, 443 );
+ }
+ if ( $this->hasOption( 'verbose' ) ) {
+ curl_setopt( $ch, CURLOPT_VERBOSE, true );
+ }
+ curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
+ curl_setopt( $ch, CURLOPT_HEADER, 0 );
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+ $data = curl_exec( $ch );
+ if ( !$data ) {
+ $this->output( 'Curl error: ' . curl_error( $ch ) );
+ }
+
+ $this->output( "Returned: $data\n\n" );
+ }
+}
+
+$maintClass = TestOAuthConsumer::class;
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/OAuth/package-lock.json b/OAuth/package-lock.json
new file mode 100644
index 00000000..89dfca5b
--- /dev/null
+++ b/OAuth/package-lock.json
@@ -0,0 +1,3709 @@
+{
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.0.tgz",
+ "integrity": "sha512-AN2IR/wCUYsM+PdErq6Bp3RFTXl8W0p9Nmymm7zkpsCmh+r/YYcckaCGpU8Q/mEKmST19kkGRaG42A/jxOWwBA==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.0"
+ }
+ },
+ "@babel/core": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz",
+ "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.9.0",
+ "@babel/helper-module-transforms": "^7.9.0",
+ "@babel/helpers": "^7.9.0",
+ "@babel/parser": "^7.9.0",
+ "@babel/template": "^7.8.6",
+ "@babel/traverse": "^7.9.0",
+ "@babel/types": "^7.9.0",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.1",
+ "json5": "^2.1.2",
+ "lodash": "^4.17.13",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz",
+ "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.9.5",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.13",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
+ "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.9.5"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
+ "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
+ "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
+ "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz",
+ "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.8.6",
+ "@babel/helper-simple-access": "^7.8.3",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/template": "^7.8.6",
+ "@babel/types": "^7.9.0",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
+ "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz",
+ "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.8.3",
+ "@babel/helper-optimise-call-expression": "^7.8.3",
+ "@babel/traverse": "^7.8.6",
+ "@babel/types": "^7.8.6"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz",
+ "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
+ "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.9.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz",
+ "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.9.0",
+ "@babel/types": "^7.9.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.0.tgz",
+ "integrity": "sha512-OsdTJbHlPtIk2mmtwXItYrdmalJ8T0zpVzNAbKSkHshuywj7zb29Y09McV/jQsQunc/nEyHiPV2oy9llYMLqxw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.9.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
+ "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
+ "dev": true
+ },
+ "@babel/runtime": {
+ "version": "7.9.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
+ "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "@babel/template": {
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ }
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz",
+ "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.9.5",
+ "@babel/helper-function-name": "^7.9.5",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/parser": "^7.9.0",
+ "@babel/types": "^7.9.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.13"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
+ "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.5",
+ "lodash": "^4.17.13",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
+ "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.3",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
+ "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
+ "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.3",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@stylelint/postcss-css-in-js": {
+ "version": "0.37.1",
+ "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.1.tgz",
+ "integrity": "sha512-UMf2Rni3JGKi3ZwYRGMYJ5ipOA5ENJSKMtYA/pE1ZLURwdh7B5+z2r73RmWvub+N0UuH1Lo+TGfCgYwPvqpXNw==",
+ "dev": true,
+ "requires": {
+ "@babel/core": ">=7.9.0"
+ }
+ },
+ "@stylelint/postcss-markdown": {
+ "version": "0.36.1",
+ "resolved": "https://registry.npmjs.org/@stylelint/postcss-markdown/-/postcss-markdown-0.36.1.tgz",
+ "integrity": "sha512-iDxMBWk9nB2BPi1VFQ+Dc5+XpvODBHw2n3tYpaBZuEAFQlbtF9If0Qh5LTTwSi/XwdbJ2jt+0dis3i8omyggpw==",
+ "dev": true,
+ "requires": {
+ "remark": "^12.0.0",
+ "unist-util-find-all-after": "^3.0.1"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "@types/minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=",
+ "dev": true
+ },
+ "@types/normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
+ "dev": true
+ },
+ "@types/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+ "dev": true
+ },
+ "@types/unist": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz",
+ "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==",
+ "dev": true
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
+ "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz",
+ "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+ "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-escapes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
+ "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ }
+ }
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+ "dev": true
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "autoprefixer": {
+ "version": "9.7.6",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz",
+ "integrity": "sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.11.1",
+ "caniuse-lite": "^1.0.30001039",
+ "chalk": "^2.4.2",
+ "normalize-range": "^0.1.2",
+ "num2fraction": "^1.2.2",
+ "postcss": "^7.0.27",
+ "postcss-value-parser": "^4.0.3"
+ }
+ },
+ "bail": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
+ "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz",
+ "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001043",
+ "electron-to-chromium": "^1.3.413",
+ "node-releases": "^1.1.53",
+ "pkg-up": "^2.0.0"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+ "dev": true
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^2.0.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001045",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001045.tgz",
+ "integrity": "sha512-Y8o2Iz1KPcD6FjySbk1sPpvJqchgxk/iow0DABpGyzA1UeQAuxh63Xh0Enj5/BrsYbXtCN32JmR4ZxQTCQ6E6A==",
+ "dev": true
+ },
+ "ccount": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz",
+ "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "character-entities": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
+ "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
+ "dev": true
+ },
+ "character-entities-html4": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz",
+ "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==",
+ "dev": true
+ },
+ "character-entities-legacy": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
+ "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
+ "dev": true
+ },
+ "character-reference-invalid": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
+ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
+ "dev": true
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "clone-regexp": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz",
+ "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==",
+ "dev": true,
+ "requires": {
+ "is-regexp": "^2.0.0"
+ }
+ },
+ "coffeescript": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
+ "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
+ "dev": true
+ },
+ "collapse-white-space": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz",
+ "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "cosmiconfig": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+ "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+ "dev": true,
+ "requires": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.1.0",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.7.2"
+ },
+ "dependencies": {
+ "parse-json": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
+ "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
+ }
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "dev": true,
+ "requires": {
+ "array-find-index": "^1.0.1"
+ }
+ },
+ "dateformat": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
+ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "meow": "^3.3.0"
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "decamelize-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
+ "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=",
+ "dev": true,
+ "requires": {
+ "decamelize": "^1.1.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ },
+ "dependencies": {
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
+ }
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dom-serializer": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+ "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "entities": "^2.0.0"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
+ "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==",
+ "dev": true
+ },
+ "entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+ "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
+ "dev": true
+ }
+ }
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+ "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.3.414",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.414.tgz",
+ "integrity": "sha512-UfxhIvED++qLwWrAq9uYVcqF8FdeV9sU2S7qhiHYFODxzXRrd1GZRl/PjITHsTEejgibcWDraD8TQqoHb1aCBQ==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+ "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.10.0",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^1.4.3",
+ "eslint-visitor-keys": "^1.1.0",
+ "espree": "^6.1.2",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^7.0.0",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.14",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.3",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "semver": "^6.1.2",
+ "strip-ansi": "^5.2.0",
+ "strip-json-comments": "^3.0.1",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-config-wikimedia": {
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.15.3.tgz",
+ "integrity": "sha512-MGfUwNoW2IckRismX3L8a/CI1CKkYyXHiIyAqiu27TCQwrDubryI+rtuuFYozauB3LC5WSIOnW2m1ZljtPvPPg==",
+ "dev": true,
+ "requires": {
+ "eslint": "^6.8.0",
+ "eslint-plugin-es": "^3.0.0",
+ "eslint-plugin-json": "^2.1.1",
+ "eslint-plugin-mediawiki": "^0.2.3",
+ "eslint-plugin-no-jquery": "^2.3.2",
+ "eslint-plugin-qunit": "^4.0.0",
+ "eslint-plugin-vue": "^6.1.2"
+ }
+ },
+ "eslint-plugin-es": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.0.tgz",
+ "integrity": "sha512-6/Jb/J/ZvSebydwbBJO1R9E5ky7YeElfK56Veh7e4QGFHCXoIXGH9HhVz+ibJLM3XJ1XjP+T7rKBLUa/Y7eIng==",
+ "dev": true,
+ "requires": {
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
+ },
+ "dependencies": {
+ "eslint-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
+ "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "regexpp": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-json": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-2.1.1.tgz",
+ "integrity": "sha512-Ktsab8ij33V2KFLhh4alC1FYztdmbV32DeMZYYUCZm4kKLW1s4DrleKKgtbAHSJsmshCK5QGOZtfyc2r3jCRsg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.15",
+ "vscode-json-languageservice": "^3.5.1"
+ }
+ },
+ "eslint-plugin-mediawiki": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-mediawiki/-/eslint-plugin-mediawiki-0.2.3.tgz",
+ "integrity": "sha512-/6CB/VdwZHIsPZ5gZJ3amwHUbEgbL6DZULXWTRwKoS+2q5t8TS1hu+EX83a1hPrxGWFusfV+bvgOi15aXVXi4Q==",
+ "dev": true,
+ "requires": {
+ "eslint-plugin-vue": "^6.2.2"
+ }
+ },
+ "eslint-plugin-no-jquery": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.3.2.tgz",
+ "integrity": "sha512-8M9GByb/JOO+dktgbFeC/YAMaqlscInO3fH3A9fLxZduH1NTXsIAUrimas6zDwOLBvEXpRaEZycc2QAl+W+Agw==",
+ "dev": true
+ },
+ "eslint-plugin-qunit": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-qunit/-/eslint-plugin-qunit-4.0.0.tgz",
+ "integrity": "sha512-+0i2xcYryUoLawi47Lp0iJKzkP931G5GXwIOq1KBKQc2pknV1VPjfE6b4mI2mR2RnL7WRoS30YjwC9SjQgJDXQ==",
+ "dev": true
+ },
+ "eslint-plugin-vue": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-6.2.2.tgz",
+ "integrity": "sha512-Nhc+oVAHm0uz/PkJAWscwIT4ijTrK5fqNqz9QB1D35SbbuMG1uB6Yr5AJpvPSWg+WOw7nYNswerYh0kOk64gqQ==",
+ "dev": true,
+ "requires": {
+ "natural-compare": "^1.4.0",
+ "semver": "^5.6.0",
+ "vue-eslint-parser": "^7.0.0"
+ }
+ },
+ "eslint-scope": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
+ "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+ "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+ "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+ "dev": true
+ },
+ "espree": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz",
+ "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.1.0",
+ "acorn-jsx": "^5.1.0",
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+ "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
+ "dev": true
+ },
+ "execall": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz",
+ "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==",
+ "dev": true,
+ "requires": {
+ "clone-regexp": "^2.1.0"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz",
+ "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.0",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.2",
+ "picomatch": "^2.2.1"
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "fastq": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.7.0.tgz",
+ "integrity": "sha512-YOadQRnHd5q6PogvAR/x62BGituF2ufiEA6s8aavQANw5YKHERI4AREboX6KotzP8oX2klxYF2wcV/7bn1clfQ==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "figures": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
+ "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "findup-sync": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+ "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+ "dev": true,
+ "requires": {
+ "glob": "~5.0.0"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ }
+ },
+ "flatted": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "gensync": {
+ "version": "1.0.0-beta.1",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
+ "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+ "dev": true
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
+ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
+ "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "global-modules": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+ "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^3.0.0"
+ }
+ },
+ "global-prefix": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+ "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "dev": true,
+ "requires": {
+ "ini": "^1.3.5",
+ "kind-of": "^6.0.2",
+ "which": "^1.3.1"
+ }
+ },
+ "globals": {
+ "version": "12.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz",
+ "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "globby": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.0.tgz",
+ "integrity": "sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.1.1",
+ "ignore": "^5.1.4",
+ "merge2": "^1.3.0",
+ "slash": "^3.0.0"
+ },
+ "dependencies": {
+ "ignore": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
+ "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
+ "dev": true
+ }
+ }
+ },
+ "globjoin": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz",
+ "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=",
+ "dev": true
+ },
+ "gonzales-pe": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz",
+ "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "dev": true
+ },
+ "grunt": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.1.0.tgz",
+ "integrity": "sha512-+NGod0grmviZ7Nzdi9am7vuRS/h76PcWDsV635mEXF0PEQMUV6Kb+OjTdsVxbi0PZmfQOjCMKb3w8CVZcqsn1g==",
+ "dev": true,
+ "requires": {
+ "coffeescript": "~1.10.0",
+ "dateformat": "~1.0.12",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.1",
+ "findup-sync": "~0.3.0",
+ "glob": "~7.0.0",
+ "grunt-cli": "~1.2.0",
+ "grunt-known-options": "~1.1.0",
+ "grunt-legacy-log": "~2.0.0",
+ "grunt-legacy-util": "~1.1.1",
+ "iconv-lite": "~0.4.13",
+ "js-yaml": "~3.13.1",
+ "minimatch": "~3.0.2",
+ "mkdirp": "~1.0.3",
+ "nopt": "~3.0.6",
+ "path-is-absolute": "~1.0.0",
+ "rimraf": "~2.6.2"
+ },
+ "dependencies": {
+ "grunt-cli": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
+ "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
+ "dev": true,
+ "requires": {
+ "findup-sync": "~0.3.0",
+ "grunt-known-options": "~1.1.0",
+ "nopt": "~3.0.6",
+ "resolve": "~1.1.0"
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz",
+ "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-banana-checker": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/grunt-banana-checker/-/grunt-banana-checker-0.9.0.tgz",
+ "integrity": "sha512-SqPiB6OazWqR8USL0NymtuT5Br3mD9WBBsM1rHC/3wIi2SrZNM6/+j9CIeuEM5oCn+AtO2Y0+rzzFyOdC9afAg==",
+ "dev": true
+ },
+ "grunt-eslint": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-eslint/-/grunt-eslint-22.0.0.tgz",
+ "integrity": "sha512-I7vIU4x/mb20fmA6TAmLx6Wzn7mfs8ZXeuk7LbP2ujKVFV7KZmJ3qXUyqe2wnD+v/74Rs5uYOZrLL8EoBmlG9Q==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.1.0",
+ "eslint": "^6.0.1"
+ }
+ },
+ "grunt-known-options": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz",
+ "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==",
+ "dev": true
+ },
+ "grunt-legacy-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
+ "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
+ "dev": true,
+ "requires": {
+ "colors": "~1.1.2",
+ "grunt-legacy-log-utils": "~2.0.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.5"
+ }
+ },
+ "grunt-legacy-log-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
+ "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
+ "dev": true,
+ "requires": {
+ "chalk": "~2.4.1",
+ "lodash": "~4.17.10"
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
+ "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
+ "dev": true,
+ "requires": {
+ "async": "~1.5.2",
+ "exit": "~0.1.1",
+ "getobject": "~0.1.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.10",
+ "underscore.string": "~3.3.4",
+ "which": "~1.3.0"
+ }
+ },
+ "grunt-stylelint": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/grunt-stylelint/-/grunt-stylelint-0.15.0.tgz",
+ "integrity": "sha512-1G5kbT3Y6OtAqgIv/XErtI6ai1t1UdtQWXxUV5Gd900PQoEzu/WrBYhGNAXdb/9nAsNWNjFHQjtdXQtZcDmobA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "hard-rejection": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
+ "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+ "dev": true
+ },
+ "html-tags": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
+ "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==",
+ "dev": true
+ },
+ "htmlparser2": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.1",
+ "domhandler": "^2.3.0",
+ "domutils": "^1.5.1",
+ "entities": "^1.1.1",
+ "inherits": "^2.0.1",
+ "readable-stream": "^3.1.1"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+ "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "import-lazy": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
+ "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
+ "dev": true
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.3.tgz",
+ "integrity": "sha512-+OiOVeVydu4hnCGLCSX+wedovR/Yzskv9BFqUNNKq9uU2qg7LCcCo3R86S2E7WLo0y/x2pnEZfZe1CoYnORUAw==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^2.4.2",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.15",
+ "mute-stream": "0.0.8",
+ "run-async": "^2.2.0",
+ "rxjs": "^6.5.3",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^5.1.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "is-alphabetical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
+ "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
+ "dev": true
+ },
+ "is-alphanumeric": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz",
+ "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=",
+ "dev": true
+ },
+ "is-alphanumerical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
+ "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
+ "dev": true,
+ "requires": {
+ "is-alphabetical": "^1.0.0",
+ "is-decimal": "^1.0.0"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "dev": true
+ },
+ "is-decimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
+ "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+ "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-hexadecimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
+ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
+ "is-regexp": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz",
+ "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "is-whitespace-character": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz",
+ "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==",
+ "dev": true
+ },
+ "is-word-character": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz",
+ "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+ "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "jsonc-parser": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz",
+ "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "known-css-properties": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.18.0.tgz",
+ "integrity": "sha512-69AgJ1rQa7VvUsd2kpvVq+VeObDuo3zrj0CzM5Slmf6yduQFAI2kXPDQJR2IE/u6MSAUOJrwSzjg5vlz8qcMiw==",
+ "dev": true
+ },
+ "leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "lines-and-columns": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
+ "dev": true
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "dependencies": {
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ }
+ }
+ },
+ "lodash": {
+ "version": "4.17.19",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+ "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+ "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2"
+ }
+ },
+ "longest-streak": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz",
+ "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==",
+ "dev": true
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "dev": true,
+ "requires": {
+ "currently-unhandled": "^0.4.1",
+ "signal-exit": "^3.0.0"
+ }
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+ "dev": true
+ },
+ "markdown-escapes": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz",
+ "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==",
+ "dev": true
+ },
+ "markdown-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz",
+ "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==",
+ "dev": true,
+ "requires": {
+ "repeat-string": "^1.0.0"
+ }
+ },
+ "mathml-tag-names": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
+ "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==",
+ "dev": true
+ },
+ "mdast-util-compact": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz",
+ "integrity": "sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==",
+ "dev": true,
+ "requires": {
+ "unist-util-visit": "^2.0.0"
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "dev": true,
+ "requires": {
+ "camelcase-keys": "^2.0.0",
+ "decamelize": "^1.1.2",
+ "loud-rejection": "^1.0.0",
+ "map-obj": "^1.0.1",
+ "minimist": "^1.1.3",
+ "normalize-package-data": "^2.3.4",
+ "object-assign": "^4.0.1",
+ "read-pkg-up": "^1.0.1",
+ "redent": "^1.0.0",
+ "trim-newlines": "^1.0.0"
+ }
+ },
+ "merge2": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz",
+ "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+ "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.1",
+ "picomatch": "^2.0.5"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "min-indent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
+ "integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "minimist-options": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.0.2.tgz",
+ "integrity": "sha512-seq4hpWkYSUh1y7NXxzucwAN9yVlBc3Upgdjz8vLCP97jG8kaOmzYrVH/m7tQ1NYD1wdtZbSLfdy4zFmRWuc/w==",
+ "dev": true,
+ "requires": {
+ "arrify": "^1.0.1",
+ "is-plain-obj": "^1.1.0"
+ },
+ "dependencies": {
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+ "dev": true
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
+ "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "1.1.53",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz",
+ "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
+ "dev": true
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+ "dev": true
+ },
+ "normalize-selector": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz",
+ "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=",
+ "dev": true
+ },
+ "num2fraction": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+ "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+ "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
+ "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
+ "dev": true,
+ "requires": {
+ "character-entities": "^1.0.0",
+ "character-entities-legacy": "^1.0.0",
+ "character-reference-invalid": "^1.0.0",
+ "is-alphanumerical": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-hexadecimal": "^1.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ }
+ }
+ },
+ "postcss": {
+ "version": "7.0.27",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
+ "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "source-map": "^0.6.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "postcss-html": {
+ "version": "0.36.0",
+ "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz",
+ "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==",
+ "dev": true,
+ "requires": {
+ "htmlparser2": "^3.10.0"
+ }
+ },
+ "postcss-less": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz",
+ "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.14"
+ }
+ },
+ "postcss-media-query-parser": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
+ "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=",
+ "dev": true
+ },
+ "postcss-reporter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz",
+ "integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "lodash": "^4.17.11",
+ "log-symbols": "^2.2.0",
+ "postcss": "^7.0.7"
+ },
+ "dependencies": {
+ "log-symbols": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
+ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1"
+ }
+ }
+ }
+ },
+ "postcss-resolve-nested-selector": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz",
+ "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=",
+ "dev": true
+ },
+ "postcss-safe-parser": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz",
+ "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.26"
+ }
+ },
+ "postcss-sass": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.4.tgz",
+ "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==",
+ "dev": true,
+ "requires": {
+ "gonzales-pe": "^4.3.0",
+ "postcss": "^7.0.21"
+ }
+ },
+ "postcss-scss": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.0.0.tgz",
+ "integrity": "sha512-um9zdGKaDZirMm+kZFKKVsnKPF7zF7qBAtIfTSnZXD1jZ0JNZIxdB6TxQOjCnlSzLRInVl2v3YdBh/M881C4ug==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+ "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ },
+ "postcss-syntax": {
+ "version": "0.36.2",
+ "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz",
+ "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==",
+ "dev": true
+ },
+ "postcss-value-parser": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz",
+ "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "quick-lru": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz",
+ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "dev": true,
+ "requires": {
+ "indent-string": "^2.1.0",
+ "strip-indent": "^1.0.1"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+ "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
+ "dev": true
+ },
+ "regexpp": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+ "dev": true
+ },
+ "remark": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/remark/-/remark-12.0.0.tgz",
+ "integrity": "sha512-oX4lMIS0csgk8AEbzY0h2jdR0ngiCHOpwwpxjmRa5TqAkeknY+tkhjRJGZqnCmvyuWh55/0SW5WY3R3nn3PH9A==",
+ "dev": true,
+ "requires": {
+ "remark-parse": "^8.0.0",
+ "remark-stringify": "^8.0.0",
+ "unified": "^9.0.0"
+ }
+ },
+ "remark-parse": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.1.tgz",
+ "integrity": "sha512-Ye/5W57tdQZWsfkuVyRq9SUWRgECHnDsMuyUMzdSKpTbNPkZeGtoYfsrkeSi4+Xyl0mhcPPddHITXPcCPHrl3w==",
+ "dev": true,
+ "requires": {
+ "ccount": "^1.0.0",
+ "collapse-white-space": "^1.0.2",
+ "is-alphabetical": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-whitespace-character": "^1.0.0",
+ "is-word-character": "^1.0.0",
+ "markdown-escapes": "^1.0.0",
+ "parse-entities": "^2.0.0",
+ "repeat-string": "^1.5.4",
+ "state-toggle": "^1.0.0",
+ "trim": "0.0.1",
+ "trim-trailing-lines": "^1.0.0",
+ "unherit": "^1.0.4",
+ "unist-util-remove-position": "^2.0.0",
+ "vfile-location": "^3.0.0",
+ "xtend": "^4.0.1"
+ }
+ },
+ "remark-stringify": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.0.0.tgz",
+ "integrity": "sha512-cABVYVloFH+2ZI5bdqzoOmemcz/ZuhQSH6W6ZNYnLojAUUn3xtX7u+6BpnYp35qHoGr2NFBsERV14t4vCIeW8w==",
+ "dev": true,
+ "requires": {
+ "ccount": "^1.0.0",
+ "is-alphanumeric": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-whitespace-character": "^1.0.0",
+ "longest-streak": "^2.0.1",
+ "markdown-escapes": "^1.0.0",
+ "markdown-table": "^2.0.0",
+ "mdast-util-compact": "^2.0.0",
+ "parse-entities": "^2.0.0",
+ "repeat-string": "^1.5.4",
+ "state-toggle": "^1.0.0",
+ "stringify-entities": "^3.0.0",
+ "unherit": "^1.0.4",
+ "xtend": "^4.0.1"
+ }
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
+ "replace-ext": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
+ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
+ "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "run-parallel": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
+ "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==",
+ "dev": true
+ },
+ "rxjs": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
+ "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
+ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+ "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz",
+ "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==",
+ "dev": true
+ },
+ "specificity": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz",
+ "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+ "dev": true
+ },
+ "state-toggle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz",
+ "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
+ "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
+ "dev": true
+ }
+ }
+ },
+ "stringify-entities": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.0.0.tgz",
+ "integrity": "sha512-h7NJJIssprqlyjHT2eQt2W1F+MCcNmwPGlKb0bWEdET/3N44QN3QbUF/ueKCgAssyKRZ3Br9rQ7FcXjHr0qLHw==",
+ "dev": true,
+ "requires": {
+ "character-entities-html4": "^1.0.0",
+ "character-entities-legacy": "^1.0.0",
+ "is-alphanumerical": "^1.0.0",
+ "is-decimal": "^1.0.2",
+ "is-hexadecimal": "^1.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1"
+ }
+ },
+ "style-search": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
+ "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=",
+ "dev": true
+ },
+ "stylelint": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.3.2.tgz",
+ "integrity": "sha512-kpO3/Gz2ZY40EWUwFYYkgpzhf8ZDUyKpcui5+pS0XKJBj/EMYmZpOJoL8IFAz2yApYeg91NVy5yAjE39hDzWvQ==",
+ "dev": true,
+ "requires": {
+ "@stylelint/postcss-css-in-js": "^0.37.1",
+ "@stylelint/postcss-markdown": "^0.36.1",
+ "autoprefixer": "^9.7.6",
+ "balanced-match": "^1.0.0",
+ "chalk": "^4.0.0",
+ "cosmiconfig": "^6.0.0",
+ "debug": "^4.1.1",
+ "execall": "^2.0.0",
+ "file-entry-cache": "^5.0.1",
+ "get-stdin": "^7.0.0",
+ "global-modules": "^2.0.0",
+ "globby": "^11.0.0",
+ "globjoin": "^0.1.4",
+ "html-tags": "^3.1.0",
+ "ignore": "^5.1.4",
+ "import-lazy": "^4.0.0",
+ "imurmurhash": "^0.1.4",
+ "known-css-properties": "^0.18.0",
+ "leven": "^3.1.0",
+ "lodash": "^4.17.15",
+ "log-symbols": "^3.0.0",
+ "mathml-tag-names": "^2.1.3",
+ "meow": "^6.1.0",
+ "micromatch": "^4.0.2",
+ "normalize-selector": "^0.2.0",
+ "postcss": "^7.0.27",
+ "postcss-html": "^0.36.0",
+ "postcss-less": "^3.1.4",
+ "postcss-media-query-parser": "^0.2.3",
+ "postcss-reporter": "^6.0.1",
+ "postcss-resolve-nested-selector": "^0.1.1",
+ "postcss-safe-parser": "^4.0.2",
+ "postcss-sass": "^0.4.4",
+ "postcss-scss": "^2.0.0",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-syntax": "^0.36.2",
+ "postcss-value-parser": "^4.0.3",
+ "resolve-from": "^5.0.0",
+ "slash": "^3.0.0",
+ "specificity": "^0.4.1",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "style-search": "^0.1.0",
+ "sugarss": "^2.0.0",
+ "svg-tags": "^1.0.0",
+ "table": "^5.4.6",
+ "v8-compile-cache": "^2.1.0",
+ "write-file-atomic": "^3.0.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "camelcase-keys": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
+ "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.3.1",
+ "map-obj": "^4.0.0",
+ "quick-lru": "^4.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
+ "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "get-stdin": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz",
+ "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
+ "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "map-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz",
+ "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==",
+ "dev": true
+ },
+ "meow": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-6.1.0.tgz",
+ "integrity": "sha512-iIAoeI01v6pmSfObAAWFoITAA4GgiT45m4SmJgoxtZfvI0fyZwhV4d0lTwiUXvAKIPlma05Feb2Xngl52Mj5Cg==",
+ "dev": true,
+ "requires": {
+ "@types/minimist": "^1.2.0",
+ "camelcase-keys": "^6.1.1",
+ "decamelize-keys": "^1.1.0",
+ "hard-rejection": "^2.0.0",
+ "minimist-options": "^4.0.1",
+ "normalize-package-data": "^2.5.0",
+ "read-pkg-up": "^7.0.0",
+ "redent": "^3.0.0",
+ "trim-newlines": "^3.0.0",
+ "type-fest": "^0.8.1",
+ "yargs-parser": "^18.1.1"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
+ "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+ "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+ "dev": true,
+ "requires": {
+ "@types/normalize-package-data": "^2.4.0",
+ "normalize-package-data": "^2.5.0",
+ "parse-json": "^5.0.0",
+ "type-fest": "^0.6.0"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+ "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+ "dev": true
+ }
+ }
+ },
+ "read-pkg-up": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+ "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+ "dev": true,
+ "requires": {
+ "find-up": "^4.1.0",
+ "read-pkg": "^5.2.0",
+ "type-fest": "^0.8.1"
+ }
+ },
+ "redent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
+ "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+ "dev": true,
+ "requires": {
+ "indent-string": "^4.0.0",
+ "strip-indent": "^3.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true
+ },
+ "strip-indent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+ "dev": true,
+ "requires": {
+ "min-indent": "^1.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "trim-newlines": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz",
+ "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==",
+ "dev": true
+ }
+ }
+ },
+ "stylelint-config-wikimedia": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/stylelint-config-wikimedia/-/stylelint-config-wikimedia-0.10.1.tgz",
+ "integrity": "sha512-R/E7xVKwDyneKmVwkNi+TqJlXZjnL5IH+bQPmfHrgwwyAekNx5GdYZ+tVjx7VBXdv/pjOr0HevVpXSQe86ZfVQ==",
+ "dev": true,
+ "requires": {
+ "stylelint": "13.3.2"
+ }
+ },
+ "sugarss": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz",
+ "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.2"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "svg-tags": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz",
+ "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=",
+ "dev": true
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "trim": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz",
+ "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=",
+ "dev": true
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+ "dev": true
+ },
+ "trim-trailing-lines": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz",
+ "integrity": "sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA==",
+ "dev": true
+ },
+ "trough": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz",
+ "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dev": true,
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "underscore.string": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
+ "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "^1.0.3",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "unherit": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz",
+ "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "unified": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-9.0.0.tgz",
+ "integrity": "sha512-ssFo33gljU3PdlWLjNp15Inqb77d6JnJSfyplGJPT/a+fNRNyCBeveBAYJdO5khKdF6WVHa/yYCC7Xl6BDwZUQ==",
+ "dev": true,
+ "requires": {
+ "bail": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-buffer": "^2.0.0",
+ "is-plain-obj": "^2.0.0",
+ "trough": "^1.0.0",
+ "vfile": "^4.0.0"
+ }
+ },
+ "uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+ "dev": true
+ },
+ "unist-util-find-all-after": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.1.tgz",
+ "integrity": "sha512-0GICgc++sRJesLwEYDjFVJPJttBpVQaTNgc6Jw0Jhzvfs+jtKePEMu+uD+PqkRUrAvGQqwhpDwLGWo1PK8PDEw==",
+ "dev": true,
+ "requires": {
+ "unist-util-is": "^4.0.0"
+ }
+ },
+ "unist-util-is": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz",
+ "integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ==",
+ "dev": true
+ },
+ "unist-util-remove-position": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz",
+ "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==",
+ "dev": true,
+ "requires": {
+ "unist-util-visit": "^2.0.0"
+ }
+ },
+ "unist-util-stringify-position": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
+ "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.2"
+ }
+ },
+ "unist-util-visit": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.2.tgz",
+ "integrity": "sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^4.0.0",
+ "unist-util-visit-parents": "^3.0.0"
+ }
+ },
+ "unist-util-visit-parents": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz",
+ "integrity": "sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^4.0.0"
+ }
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "v8-compile-cache": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
+ "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "vfile": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.1.0.tgz",
+ "integrity": "sha512-BaTPalregj++64xbGK6uIlsurN3BCRNM/P2Pg8HezlGzKd1O9PrwIac6bd9Pdx2uTb0QHoioZ+rXKolbVXEgJg==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "is-buffer": "^2.0.0",
+ "replace-ext": "1.0.0",
+ "unist-util-stringify-position": "^2.0.0",
+ "vfile-message": "^2.0.0"
+ }
+ },
+ "vfile-location": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.0.1.tgz",
+ "integrity": "sha512-yYBO06eeN/Ki6Kh1QAkgzYpWT1d3Qln+ZCtSbJqFExPl1S3y2qqotJQXoh6qEvl/jDlgpUJolBn3PItVnnZRqQ==",
+ "dev": true
+ },
+ "vfile-message": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
+ "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^2.0.0"
+ }
+ },
+ "vscode-json-languageservice": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.5.2.tgz",
+ "integrity": "sha512-9cUvBq00O08lpWVVOx6tQ1yLxCHss79nsUdEAVYGomRyMbnPBmc0AkYPcXI9WK1EM6HBo0R9Zo3NjFhcICpy4A==",
+ "dev": true,
+ "requires": {
+ "jsonc-parser": "^2.2.1",
+ "vscode-languageserver-textdocument": "^1.0.1",
+ "vscode-languageserver-types": "^3.15.1",
+ "vscode-nls": "^4.1.1",
+ "vscode-uri": "^2.1.1"
+ }
+ },
+ "vscode-languageserver-textdocument": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz",
+ "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==",
+ "dev": true
+ },
+ "vscode-languageserver-types": {
+ "version": "3.15.1",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
+ "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==",
+ "dev": true
+ },
+ "vscode-nls": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz",
+ "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==",
+ "dev": true
+ },
+ "vscode-uri": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.1.tgz",
+ "integrity": "sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A==",
+ "dev": true
+ },
+ "vue-eslint-parser": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.0.0.tgz",
+ "integrity": "sha512-yR0dLxsTT7JfD2YQo9BhnQ6bUTLsZouuzt9SKRP7XNaZJV459gvlsJo4vT2nhZ/2dH9j3c53bIx9dnqU2prM9g==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "eslint-scope": "^5.0.0",
+ "eslint-visitor-keys": "^1.1.0",
+ "espree": "^6.1.2",
+ "esquery": "^1.0.1",
+ "lodash": "^4.17.15"
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true
+ },
+ "yaml": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.9.2.tgz",
+ "integrity": "sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ }
+ }
+ }
+ }
+}
diff --git a/OAuth/package.json b/OAuth/package.json
new file mode 100644
index 00000000..a63d526c
--- /dev/null
+++ b/OAuth/package.json
@@ -0,0 +1,14 @@
+{
+ "private": true,
+ "scripts": {
+ "test": "grunt test"
+ },
+ "devDependencies": {
+ "eslint-config-wikimedia": "0.15.3",
+ "grunt": "1.1.0",
+ "grunt-banana-checker": "0.9.0",
+ "grunt-eslint": "22.0.0",
+ "grunt-stylelint": "0.15.0",
+ "stylelint-config-wikimedia": "0.10.1"
+ }
+}
diff --git a/OAuth/resources/assets/echo-icon.png b/OAuth/resources/assets/echo-icon.png
new file mode 100644
index 00000000..675f7898
--- /dev/null
+++ b/OAuth/resources/assets/echo-icon.png
Binary files differ
diff --git a/OAuth/resources/modules/ext.MWOAuth.AuthorizeDialog.js b/OAuth/resources/modules/ext.MWOAuth.AuthorizeDialog.js
new file mode 100644
index 00000000..6e1eda4f
--- /dev/null
+++ b/OAuth/resources/modules/ext.MWOAuth.AuthorizeDialog.js
@@ -0,0 +1,40 @@
+/**
+ * OAuth JavaScript
+ * @author Aaron Schulz 2013
+ */
+( function () {
+ 'use strict';
+
+ var mwoauth = {
+ init: function () {
+ var form = $( '#mw-mwoauth-authorize-dialog' ),
+ accept = $( '#mw-mwoauth-accept' );
+ form.find( '.mw-htmlform-submit-buttons' ).addClass( 'mw-ui-flush-right' );
+ form.dialog( {
+ dialogClass: 'mw-mwoauth-authorize-jQuery-dialog',
+ width: 0.3 * $( window ).width(),
+ title: mw.msg( 'mwoauth-desc' ),
+ draggable: false,
+ resizable: false,
+ open: function () {
+ $( window ).scrollTop( 0 );
+ },
+ create: function () {
+ $( this ).parents( '.ui-dialog:first' )
+ .find( '.ui-dialog-content' ).css( 'padding', '20px' );
+ $( this ).css( 'maxHeight', 0.9 * $( window ).height() );
+ $( this ).css( 'background-color', '#fff' );
+ $( this ).css( 'border', '1px #ccc' );
+ $( this ).dialog( 'option', 'modal', true );
+ }
+ } );
+ form.submit( function () {
+ accept.prop( 'disabled', true );
+ } );
+ }
+ };
+
+ // Perform some onload events:
+ $( mwoauth.init );
+
+}() );
diff --git a/OAuth/resources/modules/ext.MWOAuth.AuthorizeForm.css b/OAuth/resources/modules/ext.MWOAuth.AuthorizeForm.css
new file mode 100644
index 00000000..0f17df2d
--- /dev/null
+++ b/OAuth/resources/modules/ext.MWOAuth.AuthorizeForm.css
@@ -0,0 +1,38 @@
+#mw-mwoauth-authorize-form {
+ font-size: 1em;
+ color: #333;
+}
+
+#mw-mwoauth-authorize-form > a {
+ font-size: 0.8em;
+}
+
+#mw-mwoauth-authorize-form > p {
+ font-size: 1.4em;
+ padding-left: 1em;
+}
+
+#mw-mwoauth-authorize-form > ul {
+ padding-top: 0.5em;
+ line-height: 1em;
+}
+
+#mw-mwoauth-authorize-form span.mw-grantgroup {
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+input.mw-mwoauth-authorize-button {
+ float: right;
+ margin-left: 0.7em;
+ margin-right: 0.7em;
+ margin-bottom: 0.5em;
+}
+
+.client-js #mw-mwoauth-authorize-dialog {
+ display: none; /* no flicker */
+}
+
+.mw-mwoauth-authorize-jQuery-dialog .ui-dialog-titlebar {
+ display: none;
+}
diff --git a/OAuth/resources/modules/ext.MWOAuth.BasicStyles.css b/OAuth/resources/modules/ext.MWOAuth.BasicStyles.css
new file mode 100644
index 00000000..82789cf5
--- /dev/null
+++ b/OAuth/resources/modules/ext.MWOAuth.BasicStyles.css
@@ -0,0 +1,61 @@
+.mw-mwoauthconsumerregistration-body,
+.mw-mwoauthmanageconsumers-body {
+ background-color: #f0f0f0;
+}
+
+.mw-mwoauthmanageconsumers-proposed,
+.mw-mwoauthmanagemygrants-proposed,
+.mw-mwoauthconsumerregistration-proposed {
+ background-color: #b7b8e1;
+}
+
+.mw-mwoauthmanageconsumers-rejected,
+.mw-mwoauthmanagemygrants-rejected,
+.mw-mwoauthconsumerregistration-rejected {
+ background-color: #e1b7ba;
+}
+
+.mw-mwoauthmanageconsumers-expired,
+.mw-mwoauthmanagemygrants-expired,
+.mw-mwoauthconsumerregistration-expired {
+ background-color: #e1cdb7;
+}
+
+.mw-mwoauthmanageconsumers-approved,
+.mw-mwoauthmanagemygrants-approved,
+.mw-mwoauthconsumerregistration-approved {
+ background-color: #bae1b7;
+}
+
+.mw-mwoauthmanageconsumers-disabled,
+.mw-mwoauthmanagemygrants-disabled,
+.mw-mwoauthconsumerregistration-disabled {
+ background-color: #ceb7e1;
+}
+
+.mw-mwoauthmanagemygrants-list-item {
+ margin-bottom: 1em;
+}
+
+.mw-mwoautherror-details {
+ color: #72777d;
+ font-size: 0.7em;
+}
+
+span.mw-grantgroup {
+ font-weight: bold;
+}
+
+.mw-htmlform-field-HTMLInfoField .mw-mwoauth-infotable dt,
+.mw-mwoauth-infotable span.mw-grantgroup {
+ font-weight: normal;
+}
+
+/* grant descriptions inside an infotable */
+.mw-mwoauth-infotable dl {
+ margin-top: 0;
+}
+
+dl.mw-mwoauth-infotable dl dd {
+ font-size: 0.9em;
+}
diff --git a/OAuth/schema/OAuth.sql b/OAuth/schema/OAuth.sql
new file mode 100644
index 00000000..01e326da
--- /dev/null
+++ b/OAuth/schema/OAuth.sql
@@ -0,0 +1,114 @@
+-- (c) Aaron Schulz, 2013
+
+-- Replace /*_*/ with the proper prefix
+
+-- These tables should belong in one central DB per wiki-farm
+
+-- Client consumers (proposed as well as and accepted)
+CREATE TABLE IF NOT EXISTS /*_*/oauth_registered_consumer (
+ -- Immutable fields below:
+ -- Consumer ID (1:1 with oarc_consumer_key)
+ oarc_id integer unsigned NOT NULL PRIMARY KEY auto_increment,
+ -- OAuth consumer key and secret (or RSA key)
+ oarc_consumer_key varbinary(32) NOT NULL,
+ -- Name of the application
+ oarc_name varchar(128) binary NOT NULL,
+ -- (Central) user id of the user who proposed the application
+ oarc_user_id integer unsigned NOT NULL,
+ -- Version of the application
+ oarc_version varbinary(32) NOT NULL,
+ -- Callback URL
+ oarc_callback_url blob NOT NULL,
+ -- Is the consumer allowed to specify a callback URL? (See MWOAuthServer::checkCallback().)
+ oarc_callback_is_prefix tinyblob NULL DEFAULT NULL,
+ -- Application description
+ oarc_description blob NOT NULL,
+ -- Contact email address
+ oarc_email varchar(255) binary NOT NULL,
+ -- Confirmation of contact email address
+ oarc_email_authenticated varbinary(14) NULL,
+ -- Did the owner accept the developer agreement?
+ oarc_developer_agreement tinyint NOT NULL DEFAULT 0,
+ -- Is this consumer owner-only
+ oarc_owner_only tinyint NOT NULL DEFAULT 0,
+ -- What wiki this is allowed on (a single wiki or '*' for all)
+ oarc_wiki varbinary(32) NOT NULL,
+ -- Grants needed for client consumers
+ oarc_grants blob NOT NULL,
+ -- Timestamp of consumer proposal
+ oarc_registration varbinary(14) NOT NULL,
+
+ -- Mutable fields below:
+ oarc_secret_key varbinary(32) NULL,
+ oarc_rsa_key blob NULL,
+ -- JSON blob of allowed IP ranges
+ oarc_restrictions blob NOT NULL,
+ -- Stage in registration pipeline:
+ -- (0=proposed, 1=approved, 2=rejected, 3=expired, 4=disabled)
+ oarc_stage tinyint unsigned NOT NULL DEFAULT 0,
+ -- Timestamp of the last stage change
+ oarc_stage_timestamp varbinary(14) NOT NULL,
+ -- Whether this consumer is suppressed (hidden)
+ oarc_deleted tinyint unsigned NOT NULL DEFAULT 0,
+ -- Version of OAuth protocol this consumer uses
+ oarc_oauth_version TINYINT NOT NULL DEFAULT 1,
+ -- Allowed OAuth 2.0 grant types
+ oarc_oauth2_allowed_grants BLOB NULL,
+ -- OAuth2 flag indicating if consumer can be trusted with keeping secrets
+ oarc_oauth2_is_confidential TINYINT NOT NULL DEFAULT 1
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/oarc_consumer_key
+ ON /*_*/oauth_registered_consumer (oarc_consumer_key);
+CREATE UNIQUE INDEX /*i*/oarc_name_version_user
+ ON /*_*/oauth_registered_consumer (oarc_name,oarc_user_id,oarc_version);
+CREATE INDEX /*i*/oarc_user_id ON /*_*/oauth_registered_consumer (oarc_user_id);
+CREATE INDEX /*i*/oarc_stage_timestamp
+ ON /*_*/oauth_registered_consumer (oarc_stage,oarc_stage_timestamp);
+
+-- Grant approvals by users for consumers
+CREATE TABLE IF NOT EXISTS /*_*/oauth_accepted_consumer (
+ oaac_id integer unsigned NOT NULL PRIMARY KEY auto_increment,
+ -- The name of a wiki or "*"
+ oaac_wiki varchar(255) binary NOT NULL,
+ -- Key to the user who approved the consumer (on the central wiki)
+ oaac_user_id integer unsigned NOT NULL,
+ -- Key to the consumer
+ oaac_consumer_id integer unsigned NOT NULL,
+ -- Tokens for the consumer to act on behave of the user
+ oaac_access_token varbinary(32) NOT NULL,
+ oaac_access_secret varbinary(32) NOT NULL,
+ -- JSON blob of actually accepted grants
+ oaac_grants blob NOT NULL,
+ -- Timestamp of grant approval by the user
+ oaac_accepted varbinary(14) NOT NULL,
+ -- Version of OAuth protocol this consumer uses
+ oaac_oauth_version TINYINT NOT NULL DEFAULT 1
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/oaac_access_token
+ ON /*_*/oauth_accepted_consumer (oaac_access_token);
+CREATE UNIQUE INDEX /*i*/oaac_user_consumer_wiki
+ ON /*_*/oauth_accepted_consumer (oaac_user_id,oaac_consumer_id,oaac_wiki);
+CREATE INDEX /*i*/oaac_consumer_user
+ ON /*_*/oauth_accepted_consumer (oaac_consumer_id,oaac_user_id);
+CREATE INDEX /*i*/oaac_user_id ON /*_*/oauth_accepted_consumer (oaac_user_id,oaac_id);
+
+-- Access tokens used on OAuth2 requests
+CREATE TABLE IF NOT EXISTS /*_*/oauth2_access_tokens (
+ oaat_id integer unsigned NOT NULL PRIMARY KEY auto_increment,
+ -- Access token identifier
+ oaat_identifier varchar(255) NOT NULL,
+ -- Expiration timestamp
+ oaat_expires varbinary(14) NOT NULL,
+ -- Identifier of the acceptance that allows this access token to be created
+ oaat_acceptance_id integer unsigned NOT NULL,
+ -- Indicates if the access token has been revoked
+ oaat_revoked tinyint NOT NULL DEFAULT 0
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/oaat_identifier
+ ON /*_*/oauth2_access_tokens (oaat_identifier);
+
+CREATE INDEX /*i*/oaat_acceptance_id
+ ON /*_*/oauth2_access_tokens (oaat_acceptance_id);
diff --git a/OAuth/schema/index_on_oaat_acceptance_id.sql b/OAuth/schema/index_on_oaat_acceptance_id.sql
new file mode 100644
index 00000000..8caa11bf
--- /dev/null
+++ b/OAuth/schema/index_on_oaat_acceptance_id.sql
@@ -0,0 +1,2 @@
+CREATE INDEX /*i*/oaat_acceptance_id
+ ON /*_*/oauth2_access_tokens (oaat_acceptance_id);
diff --git a/OAuth/schema/mysql/callback_is_prefix.sql b/OAuth/schema/mysql/callback_is_prefix.sql
new file mode 100644
index 00000000..75881861
--- /dev/null
+++ b/OAuth/schema/mysql/callback_is_prefix.sql
@@ -0,0 +1 @@
+ALTER TABLE /*_*/oauth_registered_consumer ADD COLUMN `oarc_callback_is_prefix` tinyblob NULL DEFAULT NULL AFTER `oarc_callback_url`;
diff --git a/OAuth/schema/mysql/developer_agreement.sql b/OAuth/schema/mysql/developer_agreement.sql
new file mode 100644
index 00000000..742af2c3
--- /dev/null
+++ b/OAuth/schema/mysql/developer_agreement.sql
@@ -0,0 +1 @@
+ALTER TABLE /*_*/oauth_registered_consumer ADD COLUMN `oarc_developer_agreement` tinyint NOT NULL DEFAULT 0 AFTER `oarc_email_authenticated`;
diff --git a/OAuth/schema/mysql/oauth2_allowed_grants.sql b/OAuth/schema/mysql/oauth2_allowed_grants.sql
new file mode 100644
index 00000000..993cd15f
--- /dev/null
+++ b/OAuth/schema/mysql/oauth2_allowed_grants.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_registered_consumer
+ ADD oarc_oauth2_allowed_grants BLOB NULL;
diff --git a/OAuth/schema/mysql/oauth2_is_confidential.sql b/OAuth/schema/mysql/oauth2_is_confidential.sql
new file mode 100644
index 00000000..1864eed2
--- /dev/null
+++ b/OAuth/schema/mysql/oauth2_is_confidential.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_registered_consumer
+ ADD oarc_oauth2_is_confidential TINYINT NOT NULL DEFAULT 1;
diff --git a/OAuth/schema/mysql/oauth_version_accepted.sql b/OAuth/schema/mysql/oauth_version_accepted.sql
new file mode 100644
index 00000000..440d645c
--- /dev/null
+++ b/OAuth/schema/mysql/oauth_version_accepted.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_accepted_consumer
+ ADD oaac_oauth_version TINYINT NOT NULL DEFAULT 1;
diff --git a/OAuth/schema/mysql/oauth_version_registered.sql b/OAuth/schema/mysql/oauth_version_registered.sql
new file mode 100644
index 00000000..c9104c61
--- /dev/null
+++ b/OAuth/schema/mysql/oauth_version_registered.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_registered_consumer
+ ADD oarc_oauth_version TINYINT NOT NULL DEFAULT 1;
diff --git a/OAuth/schema/mysql/owner_only.sql b/OAuth/schema/mysql/owner_only.sql
new file mode 100644
index 00000000..f231f419
--- /dev/null
+++ b/OAuth/schema/mysql/owner_only.sql
@@ -0,0 +1 @@
+ALTER TABLE /*_*/oauth_registered_consumer ADD COLUMN `oarc_owner_only` tinyint NOT NULL DEFAULT 0 AFTER `oarc_developer_agreement`;
diff --git a/OAuth/schema/oauth2_access_tokens.sql b/OAuth/schema/oauth2_access_tokens.sql
new file mode 100644
index 00000000..73fdf86d
--- /dev/null
+++ b/OAuth/schema/oauth2_access_tokens.sql
@@ -0,0 +1,18 @@
+-- Access tokens used on OAuth2 requests
+CREATE TABLE IF NOT EXISTS /*_*/oauth2_access_tokens (
+ oaat_id integer unsigned NOT NULL PRIMARY KEY auto_increment,
+ -- Access token
+ oaat_identifier varchar(255) NOT NULL,
+ -- Expiration timestamp
+ oaat_expires varbinary(14) NOT NULL,
+ -- Identifier of the acceptance that allows this access token to be created
+ oaat_acceptance_id integer unsigned NOT NULL,
+ -- Indicates if the access token has been revoked
+ oaat_revoked tinyint NOT NULL DEFAULT 0
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/oaat_identifier
+ ON /*_*/oauth2_access_tokens (oaat_identifier);
+
+CREATE INDEX /*i*/oaat_acceptance_id
+ ON /*_*/oauth2_access_tokens (oaat_acceptance_id);
diff --git a/OAuth/schema/sqlite/callback_is_prefix.sql b/OAuth/schema/sqlite/callback_is_prefix.sql
new file mode 100644
index 00000000..13d84457
--- /dev/null
+++ b/OAuth/schema/sqlite/callback_is_prefix.sql
@@ -0,0 +1 @@
+ALTER TABLE /*_*/oauth_registered_consumer ADD COLUMN `oarc_callback_is_prefix` tinyblob NULL DEFAULT NULL;
diff --git a/OAuth/schema/sqlite/developer_agreement.sql b/OAuth/schema/sqlite/developer_agreement.sql
new file mode 100644
index 00000000..000bedec
--- /dev/null
+++ b/OAuth/schema/sqlite/developer_agreement.sql
@@ -0,0 +1 @@
+ALTER TABLE /*_*/oauth_registered_consumer ADD COLUMN `oarc_developer_agreement` tinyint NOT NULL DEFAULT 0;
diff --git a/OAuth/schema/sqlite/oauth2_allowed_grants.sql b/OAuth/schema/sqlite/oauth2_allowed_grants.sql
new file mode 100644
index 00000000..993cd15f
--- /dev/null
+++ b/OAuth/schema/sqlite/oauth2_allowed_grants.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_registered_consumer
+ ADD oarc_oauth2_allowed_grants BLOB NULL;
diff --git a/OAuth/schema/sqlite/oauth2_is_confidential.sql b/OAuth/schema/sqlite/oauth2_is_confidential.sql
new file mode 100644
index 00000000..1864eed2
--- /dev/null
+++ b/OAuth/schema/sqlite/oauth2_is_confidential.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_registered_consumer
+ ADD oarc_oauth2_is_confidential TINYINT NOT NULL DEFAULT 1;
diff --git a/OAuth/schema/sqlite/oauth_version_accepted.sql b/OAuth/schema/sqlite/oauth_version_accepted.sql
new file mode 100644
index 00000000..440d645c
--- /dev/null
+++ b/OAuth/schema/sqlite/oauth_version_accepted.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_accepted_consumer
+ ADD oaac_oauth_version TINYINT NOT NULL DEFAULT 1;
diff --git a/OAuth/schema/sqlite/oauth_version_registered.sql b/OAuth/schema/sqlite/oauth_version_registered.sql
new file mode 100644
index 00000000..c9104c61
--- /dev/null
+++ b/OAuth/schema/sqlite/oauth_version_registered.sql
@@ -0,0 +1,2 @@
+ALTER TABLE /*_*/oauth_registered_consumer
+ ADD oarc_oauth_version TINYINT NOT NULL DEFAULT 1;
diff --git a/OAuth/schema/sqlite/owner_only.sql b/OAuth/schema/sqlite/owner_only.sql
new file mode 100644
index 00000000..c0ddbfa0
--- /dev/null
+++ b/OAuth/schema/sqlite/owner_only.sql
@@ -0,0 +1 @@
+ALTER TABLE /*_*/oauth_registered_consumer ADD COLUMN `oarc_owner_only` tinyint NOT NULL DEFAULT 0;
diff --git a/OAuth/src/AuthorizationProvider/AccessToken.php b/OAuth/src/AuthorizationProvider/AccessToken.php
new file mode 100644
index 00000000..de4fd0e2
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/AccessToken.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider;
+
+use League\OAuth2\Server\Exception\OAuthServerException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+
+abstract class AccessToken extends AuthorizationProvider implements IAccessTokenProvider {
+
+ /**
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ *
+ * @return ResponseInterface
+ * @throws OAuthServerException
+ */
+ public function getAccessTokens(
+ ServerRequestInterface $request, ResponseInterface $response
+ ): ResponseInterface {
+ $this->logAccessTokenRequest( $request );
+ return $this->server->respondToAccessTokenRequest( $request, $response );
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ */
+ protected function logAccessTokenRequest( ServerRequestInterface $request ) {
+ $this->logger->info(
+ "OAuth2: Access token request - Grant type {grant}, client id: {client}", [
+ 'grant' => $this->getGrantSingleton()->getIdentifier(),
+ 'client' => $this->getClientIdFromRequest( $request )
+ ] );
+ }
+}
diff --git a/OAuth/src/AuthorizationProvider/AuthorizationProvider.php b/OAuth/src/AuthorizationProvider/AuthorizationProvider.php
new file mode 100644
index 00000000..177fbaff
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/AuthorizationProvider.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider;
+
+use Config;
+use DateInterval;
+use Exception;
+use League\OAuth2\Server\AuthorizationServer;
+use League\OAuth2\Server\Grant\GrantTypeInterface;
+use MediaWiki\Extensions\OAuth\AuthorizationServerFactory;
+use MediaWiki\Extensions\OAuth\Repository\AuthCodeRepository;
+use MediaWiki\Extensions\OAuth\Repository\RefreshTokenRepository;
+use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Log\LoggerInterface;
+use User;
+
+abstract class AuthorizationProvider implements IAuthorizationProvider {
+ /**
+ * @var AuthorizationServer
+ */
+ protected $server;
+
+ /**
+ * @var Config|null
+ */
+ protected $config;
+
+ /**
+ * @var User
+ */
+ protected $user;
+
+ /**
+ * @var LoggerInterface
+ */
+ protected $logger;
+
+ /**
+ * @var GrantTypeInterface
+ */
+ protected $grant;
+
+ /**
+ * @return AuthorizationProvider
+ * @throws Exception
+ */
+ public static function factory() {
+ $services = MediaWikiServices::getInstance();
+ $config = $services->getConfigFactory()->makeConfig( 'mwoauth' );
+ $serverFactory = AuthorizationServerFactory::factory();
+ $logger = LoggerFactory::getInstance( 'OAuth' );
+
+ // @phan-suppress-next-line PhanTypeInstantiateAbstractStatic
+ return new static( $config, $serverFactory->getAuthorizationServer(), $logger );
+ }
+
+ /**
+ * @param Config $config
+ * @param AuthorizationServer $server
+ * @param LoggerInterface $logger
+ * @throws Exception
+ */
+ public function __construct( $config, $server, $logger ) {
+ $this->config = $config;
+ $this->server = $server;
+ $this->logger = $logger;
+
+ $this->decorateAuthServer();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setUser( User $user ) {
+ $this->user = $user;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function needsUserApproval() {
+ return false;
+ }
+
+ /**
+ * @return GrantTypeInterface
+ */
+ abstract protected function getGrant() : GrantTypeInterface;
+
+ /**
+ * @return GrantTypeInterface
+ */
+ protected function getGrantSingleton() {
+ if ( !$this->grant ) {
+ $this->grant = $this->getGrant();
+ }
+
+ return $this->grant;
+ }
+
+ /**
+ * @throws Exception
+ */
+ protected function decorateAuthServer() {
+ $grant = $this->getGrantSingleton();
+ $grant->setRefreshTokenTTL( $this->getRefreshTokenTTL() );
+ $this->server->setDefaultScope( '#default' );
+ $this->server->enableGrantType(
+ $grant,
+ $this->getGrantExpirationInterval()
+ );
+ }
+
+ /**
+ * @return RefreshTokenRepository
+ */
+ protected function getRefreshTokenRepo() {
+ /** @var RefreshTokenRepository $repo */
+ $repo = RefreshTokenRepository::factory();
+ return $repo;
+ }
+
+ /**
+ * @return AuthCodeRepository
+ */
+ protected function getAuthCodeRepo() {
+ /** @var AuthCodeRepository $repo */
+ $repo = AuthCodeRepository::factory();
+ return $repo;
+ }
+
+ /**
+ * @return DateInterval
+ * @throws Exception
+ */
+ protected function getGrantExpirationInterval() {
+ $intervalSpec = 'PT1H';
+ if ( $this->config->has( 'OAuth2GrantExpirationInterval' ) ) {
+ $intervalSpec = $this->parseExpiration( $this->config->get( 'OAuth2GrantExpirationInterval' ) );
+ }
+ return new DateInterval( $intervalSpec );
+ }
+
+ /**
+ * @return DateInterval
+ * @throws Exception
+ */
+ protected function getRefreshTokenTTL() {
+ $intervalSpec = 'PT1M';
+ if ( $this->config->has( 'OAuth2RefreshTokenTTL' ) ) {
+ $intervalSpec = $this->parseExpiration( $this->config->get( 'OAuth2RefreshTokenTTL' ) );
+ }
+
+ return new DateInterval( $intervalSpec );
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ * @param string $default
+ * @return mixed|string
+ */
+ protected function getClientIdFromRequest( ServerRequestInterface $request, $default = '' ) {
+ $params = (array)$request->getParsedBody();
+
+ return $params['client_id'] ?? $default;
+ }
+
+ private function parseExpiration( $expiration ) {
+ if ( $expiration === false || $expiration === 'infinity' ) {
+ // Effectively non-expiring tokens
+ $expiration = 'P292277000000Y';
+ }
+
+ return $expiration;
+ }
+}
diff --git a/OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAccessTokens.php b/OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAccessTokens.php
new file mode 100644
index 00000000..ab2d7aac
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAccessTokens.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant;
+
+use DateInterval;
+use Exception;
+use League\OAuth2\Server\Grant\AuthCodeGrant;
+use League\OAuth2\Server\Grant\GrantTypeInterface;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\AccessToken;
+
+class AuthorizationCodeAccessTokens extends AccessToken {
+
+ /**
+ * @return GrantTypeInterface
+ * @throws Exception
+ */
+ protected function getGrant(): GrantTypeInterface {
+ $authCodeRepo = $this->getAuthCodeRepo();
+ $refreshTokenRepo = $this->getRefreshTokenRepo();
+ $grant = new AuthCodeGrant( $authCodeRepo, $refreshTokenRepo, new DateInterval( 'PT10M' ) );
+ if ( !$this->config->get( 'OAuth2RequireCodeChallengeForPublicClients' ) ) {
+ $grant->disableRequireCodeChallengeForPublicClients();
+ }
+
+ return $grant;
+ }
+}
diff --git a/OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAuthorization.php b/OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAuthorization.php
new file mode 100644
index 00000000..f1faab83
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/Grant/AuthorizationCodeAuthorization.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant;
+
+use DateInterval;
+use Exception;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use League\OAuth2\Server\Grant\AuthCodeGrant;
+use League\OAuth2\Server\Grant\GrantTypeInterface;
+use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\AuthorizationProvider;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+use MediaWiki\Extensions\OAuth\Entity\UserEntity;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+
+class AuthorizationCodeAuthorization extends AuthorizationProvider {
+
+ /**
+ * @inheritDoc
+ */
+ public function needsUserApproval() {
+ return true;
+ }
+
+ /**
+ * @return GrantTypeInterface
+ * @throws Exception
+ */
+ protected function getGrant(): GrantTypeInterface {
+ $authCodeRepo = $this->getAuthCodeRepo();
+ $refreshTokenRepo = $this->getRefreshTokenRepo();
+ $grant = new AuthCodeGrant(
+ $authCodeRepo, $refreshTokenRepo, new DateInterval( 'PT10M' )
+ );
+ if ( !$this->config->get( 'OAuth2RequireCodeChallengeForPublicClients' ) ) {
+ $grant->disableRequireCodeChallengeForPublicClients();
+ }
+
+ return $grant;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ * @return AuthorizationRequest
+ * @throws OAuthServerException
+ */
+ public function init( ServerRequestInterface $request ): AuthorizationRequest {
+ $authRequest = $this->server->validateAuthorizationRequest( $request );
+ /** @var ClientEntity $client */
+ $client = $authRequest->getClient();
+ '@phan-var ClientEntity $client';
+
+ if ( !$client->isUsableBy( $this->user ) ) {
+ throw OAuthServerException::accessDenied(
+ 'Client ' . $client->getIdentifier() .
+ ' is not usable by user with ID ' . $this->user->getId()
+ );
+ }
+ $userEntity = UserEntity::newFromMWUser( $this->user );
+ $authRequest->setUser( $userEntity );
+ $this->logAuthorizationRequest( __METHOD__, $authRequest );
+
+ $this->logger->info(
+ "OAuth2: Starting authorization request for client {client} and user (id) {user} ", [
+ 'client' => $authRequest->getClient()->getIdentifier(),
+ 'user' => $authRequest->getUser()->getIdentifier()
+ ]
+ );
+
+ return $authRequest;
+ }
+
+ /**
+ * @param AuthorizationRequest $authRequest
+ * @param ResponseInterface $response
+ * @return ResponseInterface
+ */
+ public function authorize(
+ AuthorizationRequest $authRequest, ResponseInterface $response
+ ): ResponseInterface {
+ $this->logAuthorizationRequest( __METHOD__, $authRequest );
+ return $this->server->completeAuthorizationRequest( $authRequest, $response );
+ }
+
+ /**
+ * @param string $method
+ * @param AuthorizationRequest $authRequest
+ */
+ protected function logAuthorizationRequest( $method, AuthorizationRequest $authRequest ) {
+ $this->logger->info(
+ "OAuth2: Authorization request, func {func}, for client {client} " .
+ "and user (id) {user} using grant \"{grant}\"", [
+ 'func' => $method,
+ 'client' => $authRequest->getClient()->getIdentifier(),
+ 'user' => $authRequest->getUser()->getIdentifier(),
+ 'grant' => $authRequest->getGrantTypeId()
+ ] );
+ }
+}
diff --git a/OAuth/src/AuthorizationProvider/Grant/ClientCredentials.php b/OAuth/src/AuthorizationProvider/Grant/ClientCredentials.php
new file mode 100644
index 00000000..086889f3
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/Grant/ClientCredentials.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant;
+
+use League\OAuth2\Server\Grant\ClientCredentialsGrant;
+use League\OAuth2\Server\Grant\GrantTypeInterface;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\AccessToken;
+
+class ClientCredentials extends AccessToken {
+
+ /**
+ * @return GrantTypeInterface
+ */
+ protected function getGrant(): GrantTypeInterface {
+ return new ClientCredentialsGrant();
+ }
+}
diff --git a/OAuth/src/AuthorizationProvider/Grant/RefreshToken.php b/OAuth/src/AuthorizationProvider/Grant/RefreshToken.php
new file mode 100644
index 00000000..7fbec0cf
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/Grant/RefreshToken.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant;
+
+use Exception;
+use League\OAuth2\Server\Grant\GrantTypeInterface;
+use League\OAuth2\Server\Grant\RefreshTokenGrant;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\AccessToken;
+
+class RefreshToken extends AccessToken {
+
+ /**
+ * @return GrantTypeInterface
+ * @throws Exception
+ */
+ protected function getGrant(): GrantTypeInterface {
+ return new RefreshTokenGrant(
+ $this->getRefreshTokenRepo()
+ );
+ }
+}
diff --git a/OAuth/src/AuthorizationProvider/IAccessTokenProvider.php b/OAuth/src/AuthorizationProvider/IAccessTokenProvider.php
new file mode 100644
index 00000000..56f330f3
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/IAccessTokenProvider.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+
+interface IAccessTokenProvider extends IAuthorizationProvider {
+ /**
+ * Retrieve access tokens
+ *
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ * @return ResponseInterface
+ */
+ public function getAccessTokens( ServerRequestInterface $request,
+ ResponseInterface $response ) : ResponseInterface;
+}
diff --git a/OAuth/src/AuthorizationProvider/IAuthorizationProvider.php b/OAuth/src/AuthorizationProvider/IAuthorizationProvider.php
new file mode 100644
index 00000000..70cfb4b8
--- /dev/null
+++ b/OAuth/src/AuthorizationProvider/IAuthorizationProvider.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\AuthorizationProvider;
+
+use User;
+
+interface IAuthorizationProvider {
+
+ /**
+ * Set user that on whose behalf
+ * the client is making the request
+ *
+ * @param User $user
+ */
+ public function setUser( User $user );
+
+ /**
+ * Must user explicitly allow application
+ * to use this grant type
+ *
+ * @return bool
+ */
+ public function needsUserApproval();
+
+}
diff --git a/OAuth/src/AuthorizationServerFactory.php b/OAuth/src/AuthorizationServerFactory.php
new file mode 100644
index 00000000..36abbb4c
--- /dev/null
+++ b/OAuth/src/AuthorizationServerFactory.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+use InvalidArgumentException;
+use League\OAuth2\Server\AuthorizationServer;
+use MediaWiki\Extensions\OAuth\Repository\AccessTokenRepository;
+use MediaWiki\Extensions\OAuth\Repository\ClientRepository;
+use MediaWiki\Extensions\OAuth\Repository\ScopeRepository;
+use MediaWiki\MediaWikiServices;
+
+class AuthorizationServerFactory {
+ /** @var string */
+ protected $privateKey;
+ /** @var string */
+ protected $encryptionKey;
+
+ /**
+ * @return static
+ */
+ public static function factory() {
+ $services = MediaWikiServices::getInstance();
+ $extConfig = $services->getConfigFactory()->makeConfig( 'mwoauth' );
+ $mainConfig = $services->getMainConfig();
+ $privateKey = $extConfig->get( 'OAuth2PrivateKey' );
+ $encryptionKey = $extConfig->get( 'OAuthSecretKey' ) ?? $mainConfig->get( 'SecretKey' );
+
+ return new static( $privateKey, $encryptionKey );
+ }
+
+ /**
+ * @param string $privateKey
+ * @param string $encryptionKey
+ */
+ public function __construct( $privateKey, $encryptionKey ) {
+ $this->privateKey = $privateKey;
+ $this->encryptionKey = trim( $encryptionKey );
+
+ if ( empty( $this->encryptionKey ) ) {
+ // Empty encryption key would not break the workflow, but would cause security issues
+ throw new InvalidArgumentException( 'Encryption key must be set' );
+ }
+ }
+
+ /**
+ * @return AuthorizationServer
+ */
+ public function getAuthorizationServer() {
+ return new AuthorizationServer(
+ new ClientRepository(),
+ new AccessTokenRepository(),
+ new ScopeRepository(),
+ $this->privateKey,
+ $this->encryptionKey
+ );
+ }
+}
diff --git a/OAuth/src/Backend/Consumer.php b/OAuth/src/Backend/Consumer.php
new file mode 100644
index 00000000..071ca0ce
--- /dev/null
+++ b/OAuth/src/Backend/Consumer.php
@@ -0,0 +1,800 @@
+<?php
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use FormatJson;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity as OAuth2Client;
+use MWException;
+use User;
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * Representation of an OAuth consumer.
+ */
+abstract class Consumer extends MWOAuthDAO {
+ const OAUTH_VERSION_1 = 1;
+ const OAUTH_VERSION_2 = 2;
+
+ /** @var array Backwards-compatibility grant mappings */
+ public static $mapBackCompatGrants = [
+ 'useoauth' => 'basic',
+ 'authonly' => 'mwoauth-authonly',
+ 'authonlyprivate' => 'mwoauth-authonlyprivate',
+ ];
+
+ /** @var int Unique ID */
+ protected $id;
+ /** @var string Hex token */
+ protected $consumerKey;
+ /** @var string Name of connected application */
+ protected $name;
+ /** @var int Publisher's central user ID. $wgMWOAuthSharedUserIDs defines which central ID
+ * provider to use.
+ */
+ protected $userId;
+ /** @var string Version used for handshake breaking changes */
+ protected $version;
+ /** @var string OAuth callback URL for authorization step */
+ protected $callbackUrl;
+ /**
+ * @var int OAuth callback URL is a prefix and we allow all URLs which
+ * have callbackUrl as the prefix
+ */
+ protected $callbackIsPrefix;
+ /** @var string Application description */
+ protected $description;
+ /** @var string Publisher email address */
+ protected $email;
+ /** @var string TS_MW timestamp of when email address was confirmed */
+ protected $emailAuthenticated;
+ /** @var int User accepted the developer agreement */
+ protected $developerAgreement;
+ /** @var int Consumer is for use by the owner only */
+ protected $ownerOnly;
+ /** @var string Version of the OAuth protocol */
+ protected $oauthVersion;
+ /** @var string Wiki ID the application can be used on (or "*" for all) */
+ protected $wiki;
+ /** @var string TS_MW timestamp of proposal */
+ protected $registration;
+ /** @var string Secret HMAC key */
+ protected $secretKey;
+ /** @var string Public RSA key */
+ protected $rsaKey;
+ /** @var array List of grants */
+ protected $grants;
+ /** @var \MWRestrictions IP restrictions */
+ protected $restrictions;
+ /** @var int MWOAuthConsumer::STAGE_* constant */
+ protected $stage;
+ /** @var string TS_MW timestamp of last stage change */
+ protected $stageTimestamp;
+ /** @var int Indicates (if non-zero) this consumer's information is suppressed */
+ protected $deleted;
+ /** @var bool Indicates whether the client (consumer) is able to keep the secret */
+ protected $oauth2IsConfidential;
+ /** @var array OAuth2 grant types available to the client */
+ protected $oauth2GrantTypes;
+
+ /* Stages that registered consumer takes (stored in DB) */
+ const STAGE_PROPOSED = 0;
+ const STAGE_APPROVED = 1;
+ const STAGE_REJECTED = 2;
+ const STAGE_EXPIRED = 3;
+ const STAGE_DISABLED = 4;
+
+ /**
+ * Maps stage ids to human-readable names which describe them as a state
+ * @var array
+ */
+ public static $stageNames = [
+ self::STAGE_PROPOSED => 'proposed',
+ self::STAGE_REJECTED => 'rejected',
+ self::STAGE_EXPIRED => 'expired',
+ self::STAGE_APPROVED => 'approved',
+ self::STAGE_DISABLED => 'disabled',
+ ];
+
+ /**
+ * Maps stage ids to human-readable names which describe them as an action (which would result
+ * in that stage)
+ * @var array
+ */
+ public static $stageActionNames = [
+ self::STAGE_PROPOSED => 'propose',
+ self::STAGE_REJECTED => 'reject',
+ self::STAGE_EXPIRED => 'propose',
+ self::STAGE_APPROVED => 'approve',
+ self::STAGE_DISABLED => 'disable',
+ ];
+
+ /**
+ * Get member => db field mapping
+ * Loads all fields to avoid unnecessary querying
+ *
+ * @return array
+ */
+ protected static function getSchema() {
+ return [
+ 'table' => 'oauth_registered_consumer',
+ 'fieldColumnMap' => [
+ 'id' => 'oarc_id',
+ 'consumerKey' => 'oarc_consumer_key',
+ 'name' => 'oarc_name',
+ 'userId' => 'oarc_user_id',
+ 'version' => 'oarc_version',
+ 'callbackUrl' => 'oarc_callback_url',
+ 'callbackIsPrefix' => 'oarc_callback_is_prefix',
+ 'description' => 'oarc_description',
+ 'email' => 'oarc_email',
+ 'emailAuthenticated' => 'oarc_email_authenticated',
+ 'oauthVersion' => 'oarc_oauth_version',
+ 'developerAgreement' => 'oarc_developer_agreement',
+ 'ownerOnly' => 'oarc_owner_only',
+ 'wiki' => 'oarc_wiki',
+ 'grants' => 'oarc_grants',
+ 'registration' => 'oarc_registration',
+ 'secretKey' => 'oarc_secret_key',
+ 'rsaKey' => 'oarc_rsa_key',
+ 'restrictions' => 'oarc_restrictions',
+ 'stage' => 'oarc_stage',
+ 'stageTimestamp' => 'oarc_stage_timestamp',
+ 'deleted' => 'oarc_deleted',
+ 'oauth2IsConfidential' => 'oarc_oauth2_is_confidential',
+ 'oauth2GrantTypes' => 'oarc_oauth2_allowed_grants',
+ ],
+ 'idField' => 'id',
+ 'autoIncrField' => 'id',
+ ];
+ }
+
+ protected static function getFieldPermissionChecks() {
+ return [
+ 'name' => 'userCanSee',
+ 'userId' => 'userCanSee',
+ 'version' => 'userCanSee',
+ 'callbackUrl' => 'userCanSee',
+ 'callbackIsPrefix' => 'userCanSee',
+ 'description' => 'userCanSee',
+ 'rsaKey' => 'userCanSee',
+ 'email' => 'userCanSeeEmail',
+ 'secretKey' => 'userCanSeeSecret',
+ 'restrictions' => 'userCanSeePrivate',
+ ];
+ }
+
+ /**
+ * @param array $data
+ * @return string
+ */
+ protected static function getConsumerClass( array $data ) {
+ return static::isOAuth2( $data ) ?
+ OAuth2Client::class :
+ OAuth1Consumer::class;
+ }
+
+ /**
+ * @param array $data
+ * @return bool
+ */
+ protected static function isOAuth2( array $data = [] ) {
+ $oauthVersion = $data['oarc_oauth_version'] ?? $data['oauthVersion'];
+ return (int)$oauthVersion === self::OAUTH_VERSION_2;
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param string $key
+ * @param int $flags MWOAuthConsumer::READ_* bitfield
+ * @return Consumer|bool
+ */
+ public static function newFromKey( DBConnRef $db, $key, $flags = 0 ) {
+ $row = $db->selectRow( static::getTable(),
+ array_values( static::getFieldColumnMap() ),
+ [ 'oarc_consumer_key' => (string)$key ],
+ __METHOD__,
+ ( $flags & self::READ_LOCKING ) ? [ 'FOR UPDATE' ] : []
+ );
+
+ if ( $row ) {
+ return static::newFromRow( $db, $row );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param string $name
+ * @param string $version
+ * @param int $userId Central user ID
+ * @param int $flags MWOAuthConsumer::READ_* bitfield
+ * @return Consumer|bool
+ */
+ public static function newFromNameVersionUser(
+ DBConnRef $db, $name, $version, $userId, $flags = 0
+ ) {
+ $row = $db->selectRow( static::getTable(),
+ array_values( static::getFieldColumnMap() ),
+ [
+ 'oarc_name' => (string)$name,
+ 'oarc_version' => (string)$version,
+ 'oarc_user_id' => (int)$userId
+ ],
+ __METHOD__,
+ ( $flags & self::READ_LOCKING ) ? [ 'FOR UPDATE' ] : []
+ );
+
+ if ( $row ) {
+ return static::newFromRow( $db, $row );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public static function newGrants() {
+ return [];
+ }
+
+ /**
+ * @return array
+ */
+ public static function getAllStages() {
+ return [
+ self::STAGE_PROPOSED,
+ self::STAGE_REJECTED,
+ self::STAGE_EXPIRED,
+ self::STAGE_APPROVED,
+ self::STAGE_DISABLED,
+ ];
+ }
+
+ /**
+ * Internal ID (DB primary key).
+ * @return int
+ */
+ public function getId() {
+ return $this->get( 'id' );
+ }
+
+ /**
+ * Consumer key (32-character hexadecimal string that's used in the OAuth protocol
+ * and in URLs). This is used as the consumer ID for most external purposes.
+ * @return string
+ */
+ public function getConsumerKey() {
+ return $this->get( 'consumerKey' );
+ }
+
+ /**
+ * Name of the consumer.
+ * @return string
+ */
+ public function getName() {
+ return $this->get( 'name' );
+ }
+
+ /**
+ * Central ID of the owner.
+ * @return int
+ */
+ public function getUserId() {
+ return $this->get( 'userId' );
+ }
+
+ /**
+ * Consumer version. This is mostly meant for humans: different versions of the same
+ * application have different keys and are handled as different consumers internally.
+ * @return string
+ */
+ public function getVersion() {
+ return $this->get( 'version' );
+ }
+
+ /**
+ * Callback URL (or prefix). The browser will be redirected to this URL at the end of
+ * an OAuth handshake. See getCallbackIsPrefix() for the interpretation of this field.
+ * @return string
+ */
+ public function getCallbackUrl() {
+ return $this->get( 'callbackUrl' );
+ }
+
+ /**
+ * When true, getCallbackUrl() returns a prefix; the callback URL can be provided by the caller
+ * as long as the prefix matches. When false, the callback URL will be determined by
+ * getCallbackUrl().
+ * @return bool
+ */
+ public function getCallbackIsPrefix() {
+ return $this->get( 'callbackIsPrefix' );
+ }
+
+ /**
+ * Description of the consumer. Currently interpreted as plain text; might change to wikitext
+ * in the future.
+ * @return string
+ */
+ public function getDescription() {
+ return $this->get( 'description' );
+ }
+
+ /**
+ * Email address of the owner.
+ * @return string
+ */
+ public function getEmail() {
+ return $this->get( 'email' );
+ }
+
+ /**
+ * Date of verifying the email, in TS_MW format. In practice this will be the same as
+ * getRegistration().
+ * @return string
+ */
+ public function getEmailAuthenticated() {
+ return $this->get( 'emailAuthenticated' );
+ }
+
+ /**
+ * Did the user accept the developer agreement (the terms of use checkbox at the bottom of the
+ * registration form)? Except for very old users, always true.
+ * @return bool
+ */
+ public function getDeveloperAgreement() {
+ return $this->get( 'developerAgreement' );
+ }
+
+ /**
+ * Owner-only consumers will use one-legged flow instead of three-legged (see
+ * https://github.com/Mashape/mashape-oauth/blob/master/FLOWS.md#oauth-10a-one-legged ); there
+ * is only one user (who is the same as the owner) and they learn the access token at
+ * consumer registration time.
+ * @return bool
+ */
+ public function getOwnerOnly() {
+ return $this->get( 'ownerOnly' );
+ }
+
+ /**
+ * @return int
+ */
+ abstract public function getOAuthVersion();
+
+ /**
+ * The wiki on which the consumer is allowed to access user accounts. A wiki ID or '*' for all.
+ * @return string
+ */
+ public function getWiki() {
+ return $this->get( 'wiki' );
+ }
+
+ /**
+ * The list of grants required by this application.
+ * @return string[]
+ */
+ public function getGrants() {
+ return $this->get( 'grants' );
+ }
+
+ /**
+ * Consumer registration date in TS_MW format.
+ * @return string
+ */
+ public function getRegistration() {
+ return $this->get( 'registration' );
+ }
+
+ /**
+ * Secret key used to derive the consumer secret for HMAC-SHA1 signed OAuth requests.
+ * The actual consumer secret will be calculated via MWOAuthUtils::hmacDBSecret() to mitigate
+ * DB leaks.
+ * @return string
+ */
+ public function getSecretKey() {
+ return $this->get( 'secretKey' );
+ }
+
+ /**
+ * Public RSA key for RSA-SHA1 signerd OAuth requests.
+ * @return string
+ */
+ public function getRsaKey() {
+ return $this->get( 'rsaKey' );
+ }
+
+ /**
+ * Application restrictions (such as allowed IPs).
+ * @return \MWRestrictions
+ */
+ public function getRestrictions() {
+ return $this->get( 'restrictions' );
+ }
+
+ /**
+ * Stage at which the consumer is in the review workflow (proposed, approved etc).
+ * @return int One of the STAGE_* constants
+ */
+ public function getStage() {
+ return $this->get( 'stage' );
+ }
+
+ /**
+ * Date at which the consumer was moved to the current stage, in TS_MW format.
+ * @return string
+ */
+ public function getStageTimestamp() {
+ return $this->get( 'stageTimestamp' );
+ }
+
+ /**
+ * Is the consumer suppressed? (There is no plain deletion; the closest equivalent is the
+ * rejected/disabled stage.)
+ * @return bool
+ */
+ public function getDeleted() {
+ return $this->get( 'deleted' );
+ }
+
+ /**
+ * @param MWOAuthDataStore $dataStore
+ * @param string $verifyCode verification code
+ * @param string $requestKey original request key from /initiate
+ * @return string the url for redirection
+ */
+ public function generateCallbackUrl( $dataStore, $verifyCode, $requestKey ) {
+ $callback = $dataStore->getCallbackUrl( $this->key, $requestKey );
+
+ if ( $callback === 'oob' ) {
+ $callback = $this->getCallbackUrl();
+ }
+
+ return wfAppendQuery( $callback, [
+ 'oauth_verifier' => $verifyCode,
+ 'oauth_token' => $requestKey
+ ] );
+ }
+
+ /**
+ * Attempts to find an authorization by this user for this consumer. Since a user can
+ * accept a consumer multiple times (once for "*" and once for each specific wiki),
+ * there can several access tokens per-wiki (with varying grants) for a consumer.
+ * This will choose the most wiki-specific access token. The precedence is:
+ * a) The acceptance for wiki X if the consumer is applicable only to wiki X
+ * b) The acceptance for wiki $wikiId (if the consumer is applicable to it)
+ * c) The acceptance for wikis "*" (all wikis)
+ *
+ * Users might want more grants on some wikis than on "*". Note that the reverse would not
+ * make sense, since the consumer could just use the "*" acceptance if it has more grants.
+ *
+ * @param \User $mwUser (local wiki user) User who may or may not have authorizations
+ * @param string $wikiId
+ * @throws MWOAuthException
+ * @return ConsumerAcceptance|bool
+ */
+ public function getCurrentAuthorization( User $mwUser, $wikiId ) {
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+
+ $centralUserId = Utils::getCentralIdFromLocalUser( $mwUser );
+ if ( !$centralUserId ) {
+ throw new MWOAuthException(
+ 'mwoauthserver-invalid-user',
+ [
+ $this->getName(),
+ \Message::rawParam(
+ \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E008',
+ 'E008',
+ true
+ )
+ )
+ ]
+ );
+ }
+
+ $checkWiki = $this->getWiki() !== '*' ? $this->getWiki() : $wikiId;
+
+ $cmra = ConsumerAcceptance::newFromUserConsumerWiki(
+ $dbr,
+ $centralUserId,
+ $this,
+ $checkWiki,
+ 0,
+ $this->getOAuthVersion()
+ );
+ if ( !$cmra ) {
+ $cmra = ConsumerAcceptance::newFromUserConsumerWiki(
+ $dbr,
+ $centralUserId,
+ $this,
+ '*',
+ 0,
+ $this->getOAuthVersion()
+ );
+ }
+ return $cmra;
+ }
+
+ /**
+ * @param User $mwUser
+ * @param bool $update
+ * @param array $grants
+ * @param string|null $requestTokenKey
+ * @return mixed
+ */
+ abstract public function authorize( User $mwUser, $update, $grants, $requestTokenKey = null );
+
+ /**
+ * Verify that this user can authorize this consumer
+ *
+ * @param User $mwUser
+ * @throws MWOAuthException
+ * @throws MWException
+ */
+ protected function conductAuthorizationChecks( User $mwUser ) {
+ global $wgBlockDisablesLogin;
+
+ // Check that user and consumer are in good standing
+ if ( $mwUser->isLocked() || $wgBlockDisablesLogin && $mwUser->isBlocked() ) {
+ throw new MWOAuthException( 'mwoauthserver-insufficient-rights', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E007',
+ 'E007',
+ true
+ ) )
+ ] );
+ }
+
+ if ( $this->getDeleted() ) {
+ throw new MWOAuthException( 'mwoauthserver-bad-consumer-key', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E006',
+ 'E006',
+ true
+ ) )
+ ] );
+ } elseif ( !$this->isUsableBy( $mwUser ) ) {
+ $owner = Utils::getCentralUserNameFromId(
+ $this->getUserId(),
+ $mwUser
+ );
+ throw new MWOAuthException(
+ 'mwoauthserver-bad-consumer',
+ [ $this->getName(), Utils::getCentralUserTalk( $owner ), \Message::rawParam(
+ \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E005',
+ 'E005',
+ true
+ )
+ ) ]
+ );
+ } elseif ( $this->getOwnerOnly() ) {
+ throw new MWOAuthException( 'mwoauthserver-consumer-owner-only', [
+ $this->getName(),
+ \SpecialPage::getTitleFor(
+ 'OAuthConsumerRegistration', 'update/' . $this->getConsumerKey()
+ ),
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E010',
+ 'E010',
+ true
+ ) )
+ ] );
+ }
+ }
+
+ /**
+ * @param User $mwUser
+ * @param bool $update
+ * @param array $grants
+ * @return ConsumerAcceptance
+ * @throws MWOAuthException
+ * @throws MWException
+ */
+ protected function saveAuthorization( User $mwUser, $update, $grants ) {
+ // CentralAuth may abort here if there is no global account for this user
+ $centralUserId = Utils::getCentralIdFromLocalUser( $mwUser );
+ if ( !$centralUserId ) {
+ throw new MWOAuthException(
+ 'mwoauthserver-invalid-user',
+ [
+ $this->getName(),
+ \Message::rawParam(
+ \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E008',
+ 'E008',
+ true
+ )
+ )
+ ]
+ );
+ }
+
+ $dbw = Utils::getCentralDB( DB_MASTER );
+ // Check if this authorization exists
+ $cmra = $this->getCurrentAuthorization( $mwUser, wfWikiID() );
+
+ if ( $update ) {
+ // This should be an update to an existing authorization
+ if ( !$cmra ) {
+ // update requested, but no existing key
+ throw new MWOAuthException( 'mwoauthserver-invalid-request' );
+ }
+ $cmra->setFields( [
+ 'wiki' => $this->getWiki(),
+ 'grants' => $grants
+ ] );
+ $cmra->save( $dbw );
+ } elseif ( !$cmra ) {
+ // Add the Authorization to the database
+ $accessToken = MWOAuthDataStore::newToken();
+ $cmra = ConsumerAcceptance::newFromArray( [
+ 'id' => null,
+ 'wiki' => $this->getWiki(),
+ 'userId' => $centralUserId,
+ 'consumerId' => $this->getId(),
+ 'accessToken' => $accessToken->key,
+ 'accessSecret' => $accessToken->secret,
+ 'grants' => $grants,
+ 'accepted' => wfTimestampNow(),
+ 'oauth_version' => $this->getOAuthVersion()
+ ] );
+ $cmra->save( $dbw );
+ }
+
+ return $cmra;
+ }
+
+ /**
+ * Check if the consumer is usable by $user
+ *
+ * "Usable by $user" includes:
+ * - Approved for multi-user use
+ * - Approved for owner-only use and is owned by $user
+ * - Still pending approval and is owned by $user
+ *
+ * @param \User $user
+ * @return bool
+ */
+ public function isUsableBy( \User $user ) {
+ if ( $this->stage === self::STAGE_APPROVED && !$this->getOwnerOnly() ) {
+ return true;
+ } elseif ( $this->stage === self::STAGE_PROPOSED || $this->stage === self::STAGE_APPROVED ) {
+ $centralId = Utils::getCentralIdFromLocalUser( $user );
+ return ( $centralId && $this->userId === $centralId );
+ }
+
+ return false;
+ }
+
+ protected function normalizeValues() {
+ // Keep null values since we're constructing w/ them to auto-increment
+ $this->id = $this->id === null ? null : (int)$this->id;
+ $this->userId = (int)$this->userId;
+ $this->registration = wfTimestamp( TS_MW, $this->registration );
+ $this->stage = (int)$this->stage;
+ $this->stageTimestamp = wfTimestamp( TS_MW, $this->stageTimestamp );
+ $this->emailAuthenticated = wfTimestamp( TS_MW, $this->emailAuthenticated );
+ $this->grants = (array)$this->grants; // sanity
+ $this->callbackIsPrefix = (bool)$this->callbackIsPrefix;
+ $this->ownerOnly = (bool)$this->ownerOnly;
+ $this->oauthVersion = (int)$this->oauthVersion;
+ $this->developerAgreement = (bool)$this->developerAgreement;
+ $this->deleted = (bool)$this->deleted;
+ $this->oauth2IsConfidential = (bool)$this->oauth2IsConfidential;
+ }
+
+ protected function encodeRow( DBConnRef $db, $row ) {
+ // For compatibility with other wikis in the farm, un-remap some grants
+ foreach ( self::$mapBackCompatGrants as $old => $new ) {
+ while ( ( $i = array_search( $new, $row['oarc_grants'], true ) ) !== false ) {
+ $row['oarc_grants'][$i] = $old;
+ }
+ }
+
+ $row['oarc_registration'] = $db->timestamp( $row['oarc_registration'] );
+ $row['oarc_stage_timestamp'] = $db->timestamp( $row['oarc_stage_timestamp'] );
+ $row['oarc_restrictions'] = $row['oarc_restrictions']->toJson();
+ $row['oarc_grants'] = \FormatJson::encode( $row['oarc_grants'] );
+ $row['oarc_email_authenticated'] =
+ $db->timestampOrNull( $row['oarc_email_authenticated'] );
+ $row['oarc_oauth2_allowed_grants'] = FormatJson::encode(
+ $row['oarc_oauth2_allowed_grants']
+ );
+ return $row;
+ }
+
+ protected function decodeRow( DBConnRef $db, $row ) {
+ $row['oarc_registration'] = wfTimestamp( TS_MW, $row['oarc_registration'] );
+ $row['oarc_stage'] = (int)$row['oarc_stage'];
+ $row['oarc_stage_timestamp'] = wfTimestamp( TS_MW, $row['oarc_stage_timestamp'] );
+ $row['oarc_restrictions'] = \MWRestrictions::newFromJson( $row['oarc_restrictions'] );
+ $row['oarc_grants'] = \FormatJson::decode( $row['oarc_grants'], true );
+ $row['oarc_user_id'] = (int)$row['oarc_user_id'];
+ $row['oarc_email_authenticated'] =
+ wfTimestampOrNull( TS_MW, $row['oarc_email_authenticated'] );
+ $row['oarc_oauth2_allowed_grants'] = FormatJson::decode(
+ $row['oarc_oauth2_allowed_grants'], true
+ );
+
+ // For backwards compatibility, remap some grants
+ foreach ( self::$mapBackCompatGrants as $old => $new ) {
+ while ( ( $i = array_search( $old, $row['oarc_grants'], true ) ) !== false ) {
+ $row['oarc_grants'][$i] = $new;
+ }
+ }
+
+ return $row;
+ }
+
+ /**
+ * Magic method so that fields like $consumer->secret and $consumer->key work.
+ * This allows MWOAuthConsumer to be a replacement for OAuthConsumer
+ * in lib/OAuth.php without inheriting.
+ * @param mixed $prop
+ * @return mixed
+ */
+ public function __get( $prop ) {
+ if ( $prop === 'key' ) {
+ return $this->consumerKey;
+ } elseif ( $prop === 'secret' ) {
+ return Utils::hmacDBSecret( $this->secretKey );
+ } elseif ( $prop === 'callback_url' ) {
+ return $this->callbackUrl;
+ } else {
+ throw new \LogicException( 'Direct property access attempt: ' . $prop );
+ }
+ }
+
+ protected function userCanSee( $name, \IContextSource $context ) {
+ if ( $this->getDeleted()
+ && !$context->getUser()->isAllowed( 'mwoauthviewsuppressed' )
+ ) {
+ return $context->msg( 'mwoauth-field-hidden' );
+ } else {
+ return true;
+ }
+ }
+
+ protected function userCanSeePrivate( $name, \IContextSource $context ) {
+ if ( !$context->getUser()->isAllowed( 'mwoauthviewprivate' ) ) {
+ return $context->msg( 'mwoauth-field-private' );
+ } else {
+ return $this->userCanSee( $name, $context );
+ }
+ }
+
+ protected function userCanSeeEmail( $name, \IContextSource $context ) {
+ if ( !$context->getUser()->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ return $context->msg( 'mwoauth-field-private' );
+ } else {
+ return $this->userCanSee( $name, $context );
+ }
+ }
+
+ protected function userCanSeeSecret( $name, \IContextSource $context ) {
+ return $context->msg( 'mwoauth-field-private' );
+ }
+}
diff --git a/OAuth/src/Backend/ConsumerAcceptance.php b/OAuth/src/Backend/ConsumerAcceptance.php
new file mode 100644
index 00000000..da90914b
--- /dev/null
+++ b/OAuth/src/Backend/ConsumerAcceptance.php
@@ -0,0 +1,274 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Representation of an OAuth consumer acceptance.
+ * Created when the user clicks through the OAuth authorization dialog, this allows
+ * the specified consumer to perform actions in the name of the user
+ * (subject to the grant and wiki restrictions stored in the acceptance object).
+ */
+class ConsumerAcceptance extends MWOAuthDAO {
+ /** @var int Unique ID */
+ protected $id;
+ /** @var string Wiki ID the application can be used on (or "*" for all) */
+ protected $wiki;
+ /** @var int Publisher user ID (on central wiki) */
+ protected $userId;
+ /** @var int */
+ protected $consumerId;
+ /** @var string Hex token */
+ protected $accessToken;
+ /** @var string Secret HMAC key */
+ protected $accessSecret;
+ /** @var array List of grants */
+ protected $grants;
+ /** @var string TS_MW timestamp of acceptance */
+ protected $accepted;
+ /** @var string */
+ protected $oauthVersion;
+
+ protected static function getSchema() {
+ return [
+ 'table' => 'oauth_accepted_consumer',
+ 'fieldColumnMap' => [
+ 'id' => 'oaac_id',
+ 'wiki' => 'oaac_wiki',
+ 'userId' => 'oaac_user_id',
+ 'consumerId' => 'oaac_consumer_id',
+ 'accessToken' => 'oaac_access_token',
+ 'accessSecret' => 'oaac_access_secret',
+ 'grants' => 'oaac_grants',
+ 'accepted' => 'oaac_accepted',
+ 'oauth_version' => 'oaac_oauth_version',
+ ],
+ 'idField' => 'id',
+ 'autoIncrField' => 'id',
+ ];
+ }
+
+ protected static function getFieldPermissionChecks() {
+ return [
+ 'wiki' => 'userCanSee',
+ 'userId' => 'userCanSee',
+ 'consumerId' => 'userCanSee',
+ 'accessToken' => 'userCanSeePrivate',
+ 'accessSecret' => 'userCanSeeSecret',
+ 'grants' => 'userCanSee',
+ 'accepted' => 'userCanSee',
+ 'oauth_version' => 'userCanSee',
+ ];
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param string $token Access token
+ * @param int $flags MWOAuthConsumerAcceptance::READ_* bitfield
+ * @return ConsumerAcceptance|bool
+ */
+ public static function newFromToken( DBConnRef $db, $token, $flags = 0 ) {
+ $row = $db->selectRow( static::getTable(),
+ array_values( static::getFieldColumnMap() ),
+ [ 'oaac_access_token' => (string)$token ],
+ __METHOD__,
+ ( $flags & self::READ_LOCKING ) ? [ 'FOR UPDATE' ] : []
+ );
+
+ if ( $row ) {
+ $consumer = new self();
+ $consumer->loadFromRow( $db, $row );
+ return $consumer;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param string $userId of user who authorized (central wiki's id)
+ * @param Consumer $consumer
+ * @param string $wiki wiki associated with the acceptance
+ * @param int $flags MWOAuthConsumerAcceptance::READ_* bitfield
+ * @param string $oauthVersion
+ * @return ConsumerAcceptance|bool
+ */
+ public static function newFromUserConsumerWiki(
+ DBConnRef $db, $userId, $consumer,
+ $wiki, $flags = 0, $oauthVersion = Consumer::OAUTH_VERSION_1
+ ) {
+ $row = $db->selectRow( static::getTable(),
+ array_values( static::getFieldColumnMap() ),
+ [
+ 'oaac_user_id' => (int)$userId,
+ 'oaac_consumer_id' => $consumer->getId(),
+ 'oaac_oauth_version' => $oauthVersion,
+ 'oaac_wiki' => (string)$wiki
+ ],
+ __METHOD__,
+ ( $flags & self::READ_LOCKING ) ? [ 'FOR UPDATE' ] : []
+ );
+
+ if ( $row ) {
+ $consumer = new self();
+ $consumer->loadFromRow( $db, $row );
+ return $consumer;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Database ID.
+ * @return int
+ */
+ public function getId() {
+ return $this->get( 'id' );
+ }
+
+ /**
+ * Wiki on which the user has authorized the consumer to access their account. Wiki ID or '*'
+ * for all.
+ * @return string
+ */
+ public function getWiki() {
+ return $this->get( 'wiki' );
+ }
+
+ /**
+ * Central user ID of the authorizing user.
+ * @return int
+ */
+ public function getUserId() {
+ return $this->get( 'userId' );
+ }
+
+ /**
+ * Database ID of the consumer.
+ * @return int
+ */
+ public function getConsumerId() {
+ return $this->get( 'consumerId' );
+ }
+
+ /**
+ * The access token for the OAuth protocol
+ * @return string
+ */
+ public function getAccessToken() {
+ return $this->get( 'accessToken' );
+ }
+
+ /**
+ * Secret key used to derive the access secret for the OAuth protocol.
+ * The actual access secret will be calculated via MWOAuthUtils::hmacDBSecret() to mitigate
+ * DB leaks.
+ * @return string
+ */
+ public function getAccessSecret() {
+ return $this->get( 'accessSecret' );
+ }
+
+ /**
+ * The list of grants which have been granted.
+ * @return string[]
+ */
+ public function getGrants() {
+ return $this->get( 'grants' );
+ }
+
+ /**
+ * Date of the authorization, in TS_MW format.
+ * @return string
+ */
+ public function getAccepted() {
+ return $this->get( 'accepted' );
+ }
+
+ /**
+ * @return int
+ */
+ public function getOAuthVersion() {
+ return (int)$this->get( 'oauth_version' );
+ }
+
+ protected function normalizeValues() {
+ $this->userId = (int)$this->userId;
+ $this->consumerId = (int)$this->consumerId;
+ $this->accepted = wfTimestamp( TS_MW, $this->accepted );
+ $this->grants = (array)$this->grants; // sanity
+ }
+
+ protected function encodeRow( DBConnRef $db, $row ) {
+ if ( (int)$row['oaac_user_id'] === 0 ) {
+ throw new MWOAuthException( 'mwoauth-consumer-access-no-user' );
+ }
+ // For compatibility with other wikis in the farm, un-remap some grants
+ foreach ( Consumer::$mapBackCompatGrants as $old => $new ) {
+ while ( ( $i = array_search( $new, $row['oaac_grants'], true ) ) !== false ) {
+ $row['oaac_grants'][$i] = $old;
+ }
+ }
+
+ $row['oaac_grants'] = \FormatJson::encode( $row['oaac_grants'] );
+ $row['oaac_accepted'] = $db->timestamp( $row['oaac_accepted'] );
+ return $row;
+ }
+
+ protected function decodeRow( DBConnRef $db, $row ) {
+ $row['oaac_grants'] = \FormatJson::decode( $row['oaac_grants'], true );
+ $row['oaac_accepted'] = wfTimestamp( TS_MW, $row['oaac_accepted'] );
+
+ // For backwards compatibility, remap some grants
+ foreach ( Consumer::$mapBackCompatGrants as $old => $new ) {
+ while ( ( $i = array_search( $old, $row['oaac_grants'], true ) ) !== false ) {
+ $row['oaac_grants'][$i] = $new;
+ }
+ }
+
+ return $row;
+ }
+
+ protected function userCanSee( $name, \IContextSource $context ) {
+ $centralUserId = Utils::getCentralIdFromLocalUser( $context->getUser() );
+ if ( $this->userId != $centralUserId
+ && !$context->getUser()->isAllowed( 'mwoauthviewprivate' )
+ ) {
+ return $context->msg( 'mwoauth-field-private' );
+ } else {
+ return true;
+ }
+ }
+
+ protected function userCanSeePrivate( $name, \IContextSource $context ) {
+ if ( !$context->getUser()->isAllowed( 'mwoauthviewprivate' ) ) {
+ return $context->msg( 'mwoauth-field-private' );
+ } else {
+ return $this->userCanSee( $name, $context );
+ }
+ }
+
+ protected function userCanSeeSecret( $name, \IContextSource $context ) {
+ return $context->msg( 'mwoauth-field-private' );
+ }
+}
diff --git a/OAuth/src/Backend/Hooks.php b/OAuth/src/Backend/Hooks.php
new file mode 100644
index 00000000..39bbeb97
--- /dev/null
+++ b/OAuth/src/Backend/Hooks.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Extensions\OAuth\Frontend\OAuthLogFormatter;
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\NameTableAccessException;
+
+/**
+ * Class containing hooked functions for an OAuth environment
+ */
+class Hooks {
+
+ /**
+ * Called right after configuration variables have been set.
+ */
+ public static function onRegistration() {
+ global $wgWikimediaJenkinsCI, $wgOAuth2PrivateKey, $wgOAuth2PublicKey;
+
+ // Set $wgOAuth2PrivateKey and $wgOAuth2PublicKey for Wikimedia Jenkins, PHPUnit.
+ if ( defined( 'MW_PHPUNIT_TEST' ) || ( $wgWikimediaJenkinsCI ?? false ) ) {
+ $wgOAuth2PrivateKey = <<<EOK
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBAMBGXQYJ2lXzLuQkRlWoqYJvSnNGfRvPBUVsbHfFPyCr8i6jBPcO
+vtMLFMRAaq4quRDFgQ7YQLvKTqjpN+bo7RECAwEAAQJBAKP3XTzZCihhyYskpBZI
+TsW8wnCrm+UrFgOuApHg04S3oeUXpNApxxGy+EX0aBsVoPBuisyBjiJDIFssdgJa
+IwECIQDuMipv8QOzA9qJPPpXZCQQN6znXjSE3jZhrBH879SDBQIhAM6lgY0lWB0N
+lhQZWtM8jRcxtJUFrApEizE6WFxj/LedAiEAyINgaAVqiMror3iugNyi4ygLHGWY
+LnVlMAmKxvMZYQUCIAYTeb6ztWaNSrdmk3QYmLFw5bVoCEn4//q/k2+MBRdFAiA2
+MJWJuom6IpoP0UrM/gJbwGxwgZymb4jL+sKFoIqGmA==
+-----END RSA PRIVATE KEY-----
+EOK;
+ $wgOAuth2PublicKey = <<<EOK
+-----BEGIN PUBLIC KEY-----
+MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMBGXQYJ2lXzLuQkRlWoqYJvSnNGfRvP
+BUVsbHfFPyCr8i6jBPcOvtMLFMRAaq4quRDFgQ7YQLvKTqjpN+bo7RECAwEAAQ==
+-----END PUBLIC KEY-----
+EOK;
+ }
+ }
+
+ public static function onExtensionFunctions() {
+ global $wgLogTypes, $wgLogNames,
+ $wgLogHeaders, $wgLogActionsHandlers, $wgActionFilteredLogs;
+
+ if ( Utils::isCentralWiki() ) {
+ $wgLogTypes[] = 'mwoauthconsumer';
+ $wgLogNames['mwoauthconsumer'] = 'mwoauthconsumer-consumer-logpage';
+ $wgLogHeaders['mwoauthconsumer'] = 'mwoauthconsumer-consumer-logpagetext';
+ $wgLogActionsHandlers['mwoauthconsumer/*'] = OAuthLogFormatter::class;
+ $wgActionFilteredLogs['mwoauthconsumer'] = [
+ 'approve' => [ 'approve' ],
+ 'create-owner-only' => [ 'create-owner-only' ],
+ 'disable' => [ 'disable' ],
+ 'propose' => [ 'propose' ],
+ 'reenable' => [ 'reenable' ],
+ 'reject' => [ 'reject' ],
+ 'update' => [ 'update' ],
+ ];
+ }
+ }
+
+ /**
+ * Reserve change tags that look like an OAuth change tag.
+ *
+ * @param string $tag
+ * @param \User|null $user
+ * @param \Status &$status
+ * @return bool
+ */
+ public static function onChangeTagCanCreate( $tag, ?\User $user, \Status &$status ) {
+ if ( Utils::isReservedTagName( $tag ) ) {
+ $status->fatal( 'mwoauth-tag-reserved' );
+ }
+ return true;
+ }
+
+ public static function onMergeAccountFromTo( \User $oUser, \User $nUser ) {
+ global $wgMWOAuthSharedUserIDs;
+
+ if ( !$wgMWOAuthSharedUserIDs ) {
+ $oldid = $oUser->getId();
+ $newid = $nUser->getId();
+ if ( $oldid && $newid ) {
+ self::doUserIdMerge( $oldid, $newid );
+ }
+ }
+
+ return true;
+ }
+
+ public static function onCentralAuthGlobalUserMerged( $oldname, $newname, $oldid, $newid ) {
+ global $wgMWOAuthSharedUserIDs;
+
+ if ( $wgMWOAuthSharedUserIDs && $oldid && $newid ) {
+ self::doUserIdMerge( $oldid, $newid );
+ }
+
+ return true;
+ }
+
+ protected static function doUserIdMerge( $oldid, $newid ) {
+ $dbw = Utils::getCentralDB( DB_MASTER );
+ // Merge any consumers register to this user
+ $dbw->update( 'oauth_registered_consumer',
+ [ 'oarc_user_id' => $newid ],
+ [ 'oarc_user_id' => $oldid ],
+ __METHOD__
+ );
+ // Delete any acceptance tokens by the old user ID
+ $dbw->delete( 'oauth_accepted_consumer',
+ [ 'oaac_user_id' => $oldid ],
+ __METHOD__
+ );
+ }
+
+ public static function onListDefinedTags( &$tags ) {
+ return self::getUsedConsumerTags( false, $tags );
+ }
+
+ public static function onChangeTagsListActive( &$tags ) {
+ return self::getUsedConsumerTags( true, $tags );
+ }
+
+ /**
+ * List tags that should show as defined/active on Special:Tags
+ *
+ * Handles both the ChangeTagsListActive and ListDefinedTags hooks. Only
+ * lists those tags that are actually in use on the local wiki, to avoid
+ * flooding Special:Tags with tags for consumers that will never be making
+ * logged actions.
+ *
+ * @param bool $activeOnly true for ChangeTagsListActive, false for ListDefinedTags
+ * @param array &$tags
+ * @return bool
+ */
+ private static function getUsedConsumerTags( $activeOnly, &$tags ) {
+ // Step 1: Get the list of (active) consumers' tags for this wiki
+ $db = Utils::getCentralDB( DB_REPLICA );
+ $conds = [
+ $db->makeList( [
+ 'oarc_wiki = ' . $db->addQuotes( '*' ),
+ 'oarc_wiki = ' . $db->addQuotes( wfWikiId() ),
+ ], LIST_OR ),
+ 'oarc_deleted' => 0,
+ ];
+ if ( $activeOnly ) {
+ $conds[] = $db->makeList( [
+ 'oarc_stage = ' . Consumer::STAGE_APPROVED,
+ // Proposed consumers are active for the owner, so count them too
+ 'oarc_stage = ' . Consumer::STAGE_PROPOSED,
+ ], LIST_OR );
+ }
+ $res = $db->select(
+ 'oauth_registered_consumer',
+ [ 'oarc_id' ],
+ $conds,
+ __METHOD__
+ );
+ $allTags = [];
+ foreach ( $res as $row ) {
+ $allTags[] = Utils::getTagName( $row->oarc_id );
+ }
+
+ // Step 2: Return only those that are in use.
+ $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
+ $tagIds = [];
+ foreach ( $allTags as $tag ) {
+ try {
+ $tagIds[] = $changeTagDefStore->getId( $tag );
+ } catch ( NameTableAccessException $ex ) {
+ continue;
+ }
+ }
+ if ( $tagIds === [] ) {
+ // Nothing to add, return
+ return true;
+ }
+ $conditions = [ 'ct_tag_id' => $tagIds ];
+ $field = 'ct_tag_id';
+
+ if ( $allTags ) {
+ $db = wfGetDB( DB_REPLICA );
+ $res = $db->select(
+ 'change_tag',
+ [ $field ],
+ $conditions,
+ __METHOD__,
+ [ 'DISTINCT' ]
+ );
+ foreach ( $res as $row ) {
+ $tags[] = $changeTagDefStore->getName( intval( $row->ct_tag_id ) );
+ }
+ }
+
+ return true;
+ }
+
+ public static function onSetupAfterCache() {
+ global $wgMWOAuthCentralWiki, $wgMWOAuthSharedUserIDs;
+
+ if ( $wgMWOAuthCentralWiki === false ) {
+ // Treat each wiki as its own "central wiki" as there is no actual one
+ $wgMWOAuthCentralWiki = wfWikiId(); // default
+ } else {
+ // There is actually a central wiki, requiring global user IDs via hook
+ $wgMWOAuthSharedUserIDs = true;
+ }
+ }
+
+ public static function onApiRsdServiceApis( array &$apis ) {
+ $apis['MediaWiki']['settings']['OAuth'] = true;
+ }
+}
diff --git a/OAuth/src/Backend/MWOAuthDAO.php b/OAuth/src/Backend/MWOAuthDAO.php
new file mode 100644
index 00000000..b0c60b1c
--- /dev/null
+++ b/OAuth/src/Backend/MWOAuthDAO.php
@@ -0,0 +1,478 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Rdbms\DBConnRef;
+use Wikimedia\Rdbms\DBError;
+use Wikimedia\Rdbms\DBReadOnlyError;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Representation of a Data Access Object
+ */
+abstract class MWOAuthDAO implements \IDBAccessObject {
+ private $daoOrigin = 'new'; // string; object construction origin
+ private $daoPending = true; // bool; whether fields changed or the field is new
+
+ /** @var \Psr\Log\LoggerInterface */
+ protected $logger;
+
+ /**
+ * @throws \LogicException
+ */
+ final protected function __construct() {
+ $fields = array_keys( static::getFieldPermissionChecks() );
+ if ( array_diff( $fields, $this->getFieldNames() ) ) {
+ throw new \LogicException( "Invalid field(s) defined in access check methods." );
+ }
+ $this->logger = LoggerFactory::getInstance( 'OAuth' );
+ }
+
+ /**
+ * @param array $values (field => value) map
+ * @return static
+ */
+ final public static function newFromArray( array $values ) {
+ $class = static::getConsumerClass( $values );
+ $consumer = new $class();
+
+ // Make sure oauth_version is set - for backwards compat
+ $values['oauth_version'] = $values['oauth_version'] ?? Consumer::OAUTH_VERSION_1;
+ $consumer->loadFromValues( $values );
+ return $consumer;
+ }
+
+ /**
+ * Determine and return the correct consumer class
+ *
+ * @param array $data
+ * @return string
+ */
+ protected static function getConsumerClass( array $data ) {
+ return static::class;
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param array|\stdClass $row
+ * @return static
+ */
+ final public static function newFromRow( DBConnRef $db, $row ) {
+ $class = static::getConsumerClass( (array)$row );
+ $consumer = new $class();
+ $consumer->loadFromRow( $db, $row );
+ return $consumer;
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param int $id
+ * @param int $flags MWOAuthDAO::READ_* bitfield
+ * @return static|bool Returns false if not found
+ * @throws DBError
+ */
+ final public static function newFromId( DBConnRef $db, $id, $flags = 0 ) {
+ $row = $db->selectRow( static::getTable(),
+ array_values( static::getFieldColumnMap() ),
+ [ static::getIdColumn() => (int)$id ],
+ __METHOD__,
+ ( $flags & self::READ_LOCKING ) ? [ 'FOR UPDATE' ] : []
+ );
+
+ if ( $row ) {
+ $class = static::getConsumerClass( (array)$row );
+ $consumer = new $class();
+ $consumer->loadFromRow( $db, $row );
+ return $consumer;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the value of a field
+ *
+ * @param string $name
+ * @return mixed
+ * @throws \LogicException
+ */
+ final public function get( $name ) {
+ if ( !static::hasField( $name ) ) {
+ throw new \LogicException( "Object has no '$name' field." );
+ }
+ return $this->$name;
+ }
+
+ /**
+ * Set the value of a field
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return mixed The old value
+ * @throws \Exception
+ */
+ final public function setField( $name, $value ) {
+ $old = $this->setFields( [ $name => $value ] );
+ return $old[$name];
+ }
+
+ /**
+ * Set the values for a set of fields
+ *
+ * @param array $values (field => value) map
+ * @throws \LogicException
+ * @return array Map of old values
+ */
+ final public function setFields( array $values ) {
+ $old = [];
+ foreach ( $values as $name => $value ) {
+ if ( !static::hasField( $name ) ) {
+ throw new \LogicException( "Object has no '$name' field." );
+ }
+ $old[$name] = $this->$name;
+ $this->$name = $value;
+ if ( $old[$name] !== $value ) {
+ $this->daoPending = true;
+ }
+ }
+ $this->normalizeValues();
+ return $old;
+ }
+
+ /**
+ * @return array
+ */
+ final public function getFieldNames() {
+ return array_keys( static::getFieldColumnMap() );
+ }
+
+ /**
+ * @param DBConnRef $dbw
+ * @return bool
+ * @throws DBReadOnlyError
+ */
+ public function save( DBConnRef $dbw ) {
+ $uniqueId = $this->getIdValue();
+ $idColumn = static::getIdColumn();
+ if ( !empty( $dbw->daoReadOnly ) ) {
+ throw new DBReadOnlyError( $dbw, get_class() . ": tried to save while db is read-only" );
+ }
+ if ( $this->daoOrigin === 'db' ) {
+ if ( $this->daoPending ) {
+ $this->logger->debug( get_class( $this ) . ': performing DB update; object changed.' );
+ $dbw->update(
+ static::getTable(),
+ $this->getRowArray( $dbw ),
+ [ $idColumn => $uniqueId ],
+ __METHOD__
+ );
+ $this->daoPending = false;
+ return $dbw->affectedRows() > 0;
+ } else {
+ $this->logger->debug( get_class( $this ) . ': skipping DB update; object unchanged.' );
+ return false; // short-circuit
+ }
+ } else {
+ $this->logger->debug( get_class( $this ) . ': performing DB update; new object.' );
+ $afield = static::getAutoIncrField();
+ $acolumn = $afield !== null ? static::getColumn( $afield ) : null;
+ $row = $this->getRowArray( $dbw );
+ if ( $acolumn !== null && $row[$acolumn] === null ) {
+ // auto-increment field should be omitted, not set null, for
+ // auto-incrementing behavior
+ unset( $row[$acolumn] );
+ }
+ $dbw->insert(
+ static::getTable(),
+ $row,
+ __METHOD__
+ );
+ if ( $afield !== null ) { // update field for auto-increment field
+ $this->$afield = $dbw->insertId();
+ }
+ $this->daoPending = false;
+ return true;
+ }
+ }
+
+ /**
+ * @param DBConnRef $dbw
+ * @return bool
+ * @throws DBReadOnlyError
+ */
+ public function delete( DBConnRef $dbw ) {
+ $uniqueId = $this->getIdValue();
+ $idColumn = static::getIdColumn();
+ if ( !empty( $dbw->daoReadOnly ) ) {
+ throw new DBReadOnlyError( $dbw, get_class() . ": tried to delete while db is read-only" );
+ }
+ if ( $this->daoOrigin === 'db' ) {
+ $dbw->delete(
+ static::getTable(),
+ [ $idColumn => $uniqueId ],
+ __METHOD__
+ );
+ $this->daoPending = true;
+ return $dbw->affectedRows() > 0;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the schema information for this object type
+ *
+ * This should return an associative array with:
+ * - idField : a field with an int/hex UNIQUE identifier
+ * - autoIncrField : a field that auto-increments in the DB (or NULL if none)
+ * - table : a table name
+ * - fieldColumnMap : a map of field names to column names
+ *
+ * @throws \MWException
+ * @return array
+ */
+ protected static function getSchema() {
+ // Note: declaring this abstract raises E_STRICT
+ throw new \MWException( "getSchema() not defined in " . get_class() );
+ }
+
+ /**
+ * Get the access control check methods for this object type
+ *
+ * This returns a map of field names to method names.
+ * The methods check if a context user has access to the field,
+ * returning true if they do and a Message object otherwise.
+ * The methods take (field name, \IContextSource) as arguments.
+ *
+ * @see MWOAuthDAO::userCanAccess()
+ * @see MWOAuthDAOAccessControl
+ *
+ * @throws \LogicException Subclasses must override
+ * @return array Map of (field name => name of method that checks access)
+ */
+ protected static function getFieldPermissionChecks() {
+ // Note: declaring this abstract raises E_STRICT
+ throw new \LogicException( "getFieldPermissionChecks() not defined in " . get_class() );
+ }
+
+ /**
+ * @return string
+ */
+ final protected static function getTable() {
+ $schema = static::getSchema();
+ return $schema['table'];
+ }
+
+ /**
+ * @return array
+ */
+ final protected static function getFieldColumnMap() {
+ $schema = static::getSchema();
+ return $schema['fieldColumnMap'];
+ }
+
+ /**
+ * @param string $field
+ * @return string
+ */
+ final protected static function getColumn( $field ) {
+ $schema = static::getSchema();
+ return $schema['fieldColumnMap'][$field];
+ }
+
+ /**
+ * @param string $field
+ * @return bool
+ */
+ final protected static function hasField( $field ) {
+ $schema = static::getSchema();
+ return isset( $schema['fieldColumnMap'][$field] );
+ }
+
+ /**
+ * @return string|null
+ */
+ final protected static function getAutoIncrField() {
+ $schema = static::getSchema();
+ return $schema['autoIncrField'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ final protected static function getIdColumn() {
+ $schema = static::getSchema();
+ return $schema['fieldColumnMap'][$schema['idField']];
+ }
+
+ /**
+ * @return int|string
+ */
+ final protected function getIdValue() {
+ $schema = static::getSchema();
+ $field = $schema['idField'];
+ return $this->$field;
+ }
+
+ /**
+ * @param array $values
+ * @throws \MWException
+ */
+ final protected function loadFromValues( array $values ) {
+ foreach ( static::getFieldColumnMap() as $field => $column ) {
+ if ( !array_key_exists( $field, $values ) ) {
+ throw new \MWException( get_class( $this ) . " requires '$field' field." );
+ }
+ $this->$field = $values[$field];
+ }
+ $this->normalizeValues();
+ $this->daoOrigin = 'new';
+ $this->daoPending = true;
+ }
+
+ /**
+ * Subclasses should make this normalize fields (e.g. timestamps)
+ *
+ * @return void
+ */
+ abstract protected function normalizeValues();
+
+ /**
+ * @param DBConnRef $db
+ * @param \stdClass|array $row
+ * @return void
+ */
+ final protected function loadFromRow( DBConnRef $db, $row ) {
+ $row = $this->decodeRow( $db, (array)$row );
+ $values = [];
+ foreach ( static::getFieldColumnMap() as $field => $column ) {
+ $values[$field] = $row[$column];
+ }
+ $this->loadFromValues( $values );
+ $this->daoOrigin = 'db';
+ $this->daoPending = false;
+ }
+
+ /**
+ * Subclasses should make this to encode DB fields (e.g. timestamps).
+ * This must also flatten any PHP data structures into flat values.
+ *
+ * @param DBConnRef $db
+ * @param array $row
+ * @return array
+ */
+ abstract protected function encodeRow( DBConnRef $db, $row );
+
+ /**
+ * Subclasses should make this to decode DB fields (e.g. timestamps).
+ * This can also expand some flat values (e.g. JSON) into PHP data structures.
+ * Note: this does not need to handle what normalizeValues() already does.
+ *
+ * @param DBConnRef $db
+ * @param array $row
+ * @return array
+ */
+ abstract protected function decodeRow( DBConnRef $db, $row );
+
+ /**
+ * @param DBConnRef $db
+ * @return array
+ */
+ final protected function getRowArray( DBConnRef $db ) {
+ $row = [];
+ foreach ( static::getFieldColumnMap() as $field => $column ) {
+ $row[$column] = $this->$field;
+ }
+ return $this->encodeRow( $db, $row );
+ }
+
+ /**
+ * Check if a user (from the context) can view a field
+ *
+ * @see MWOAuthDAO::userCanAccess()
+ * @see MWOAuthDAOAccessControl
+ *
+ * @param string $name
+ * @param \IContextSource $context
+ * @return \Message|true Returns on success or a Message if the user lacks access
+ */
+ final public function userCanAccess( $name, \IContextSource $context ) {
+ $map = static::getFieldPermissionChecks();
+ if ( isset( $map[$name] ) ) {
+ $method = $map[$name];
+ return $this->$method( $name, $context );
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Get the current conflict token value for a user
+ *
+ * @param \IContextSource $context
+ * @return string Hex token
+ */
+ final public function getChangeToken( \IContextSource $context ) {
+ $map = [];
+ foreach ( $this->getFieldNames() as $field ) {
+ if ( $this->userCanAccess( $field, $context ) ) {
+ $map[$field] = $this->$field;
+ } else {
+ $map[$field] = null; // don't convey this information
+ }
+ }
+ return hash_hmac(
+ 'sha1',
+ serialize( $map ),
+ "{$context->getUser()->getId()}#{$this->getIdValue()}"
+ );
+ }
+
+ /**
+ * Compare an old change token to the current one
+ *
+ * @param \IContextSource $context
+ * @param string $oldToken
+ * @return bool Whether the current is unchanged
+ */
+ final public function checkChangeToken( \IContextSource $context, $oldToken ) {
+ return ( $this->getChangeToken( $context ) === $oldToken );
+ }
+
+ /**
+ * Update whether this object should be written to the data store
+ * @param bool $pending set to true to mark this object as needing to write its data
+ */
+ public function setPending( $pending ) {
+ $this->daoPending = $pending;
+ }
+
+ /**
+ * Update the origin of this object
+ * @param string $source source of the object
+ * 'new': Treat this as a new object to the datastore (insert on save)
+ * 'db': Treat this as already in the datastore (update on save)
+ */
+ public function updateOrigin( $source ) {
+ $this->daoOrigin = $source;
+ }
+}
diff --git a/OAuth/src/Backend/MWOAuthDataStore.php b/OAuth/src/Backend/MWOAuthDataStore.php
new file mode 100644
index 00000000..76c33522
--- /dev/null
+++ b/OAuth/src/Backend/MWOAuthDataStore.php
@@ -0,0 +1,257 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthDataStore;
+use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Rdbms\DBConnRef;
+
+class MWOAuthDataStore extends OAuthDataStore {
+ /** @var DBConnRef DB for the consumer/grant registry */
+ protected $centralReplica;
+
+ /** @var DBConnRef|null Master DB for repeated lookup in case of replication lag problems;
+ * null if there is no separate master and replica DB
+ */
+ protected $centralMaster;
+
+ /** @var \BagOStuff Cache for Tokens and Nonces */
+ protected $cache;
+
+ /** @var \Psr\Log\LoggerInterface */
+ protected $logger;
+
+ /**
+ * @param DBConnRef $centralReplica Central DB replica
+ * @param DBConnRef|null $centralMaster Central DB master (if different)
+ * @param \BagOStuff $cache
+ */
+ public function __construct( DBConnRef $centralReplica, $centralMaster, \BagOStuff $cache ) {
+ if ( $centralMaster !== null && !( $centralMaster instanceof DBConnRef ) ) {
+ throw new \InvalidArgumentException(
+ __METHOD__ . ': $centralMaster must be a DB or null'
+ );
+ }
+ $this->centralReplica = $centralReplica;
+ $this->centralMaster = $centralMaster;
+ $this->cache = $cache;
+ $this->logger = LoggerFactory::getInstance( 'OAuth' );
+ }
+
+ /**
+ * Get an MWOAuthConsumer from the consumer's key
+ *
+ * @param string $consumerKey the string value of the Consumer's key
+ * @return Consumer|bool
+ */
+ public function lookup_consumer( $consumerKey ) {
+ return Consumer::newFromKey( $this->centralReplica, $consumerKey );
+ }
+
+ /**
+ * Get either a request or access token from the data store
+ *
+ * @param OAuthConsumer|Consumer $consumer
+ * @param string $token_type
+ * @param string $token String the token
+ * @throws MWOAuthException
+ * @return MWOAuthToken
+ */
+ public function lookup_token( $consumer, $token_type, $token ) {
+ $this->logger->debug( __METHOD__ . ": Looking up $token_type token '$token'" );
+
+ if ( $token_type === 'request' ) {
+ $returnToken = $this->cache->get( Utils::getCacheKey(
+ 'token',
+ $consumer->key,
+ $token_type,
+ $token
+ ) );
+ if ( $returnToken === '**USED**' ) {
+ throw new MWOAuthException( 'mwoauthdatastore-request-token-already-used', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E009',
+ 'E009',
+ true
+ ) )
+ ] );
+ }
+ if ( $token === null || !( $returnToken instanceof MWOAuthToken ) ) {
+ throw new MWOAuthException( 'mwoauthdatastore-request-token-not-found', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E004',
+ 'E004',
+ true
+ ) )
+ ] );
+ }
+ } elseif ( $token_type === 'access' ) {
+ $cmra = ConsumerAcceptance::newFromToken( $this->centralReplica, $token );
+ if ( !$cmra && $this->centralMaster ) {
+ // try master in case there is replication lag T124942
+ $cmra = ConsumerAcceptance::newFromToken( $this->centralMaster, $token );
+ }
+ if ( !$cmra ) {
+ throw new MWOAuthException( 'mwoauthdatastore-access-token-not-found' );
+ }
+
+ // Ensure the cmra's consumer matches the expected consumer (T103023)
+ $mwconsumer = ( $consumer instanceof Consumer )
+ ? $consumer : $this->lookup_consumer( $consumer->key );
+ if ( !$mwconsumer || $mwconsumer->getId() !== $cmra->getConsumerId() ) {
+ throw new MWOAuthException( 'mwoauthdatastore-access-token-not-found' );
+ }
+
+ $secret = Utils::hmacDBSecret( $cmra->getAccessSecret() );
+ $returnToken = new MWOAuthToken( $cmra->getAccessToken(), $secret );
+ } else {
+ throw new MWOAuthException( 'mwoauthdatastore-invalid-token-type' );
+ }
+
+ return $returnToken;
+ }
+
+ /**
+ * Check that nonce has not been seen before. Add it on check, so we don't repeat it.
+ * Note, timestamp has already been checked, so this should be a fresh nonce.
+ *
+ * @param Consumer|OAuthConsumer $consumer
+ * @param string $token
+ * @param string $nonce
+ * @param int $timestamp
+ * @return bool
+ */
+ public function lookup_nonce( $consumer, $token, $nonce, $timestamp ) {
+ $key = Utils::getCacheKey( 'nonce', $consumer->key, $token, $nonce );
+ // Do an add for the key associated with this nonce to check if it was already used.
+ // Set timeout 5 minutes in the future of the timestamp as OAuthServer does. Use the
+ // timestamp so the client can also expire their nonce records after 5 mins.
+ if ( !$this->cache->add( $key, 1, $timestamp + 300 ) ) {
+ $this->logger->info( "$key exists, so nonce has been used by this consumer+token" );
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Helper function to generate and return an MWOAuthToken. MWOAuthToken can be used as a
+ * request or access token.
+ * TODO: put in Utils?
+ * @return MWOAuthToken
+ */
+ public static function newToken() {
+ return new MWOAuthToken(
+ \MWCryptRand::generateHex( 32 ),
+ \MWCryptRand::generateHex( 32 )
+ );
+ }
+
+ /**
+ * Generate a new token (attached to this consumer), save it in the cache, and return it
+ *
+ * @param Consumer|OAuthConsumer $consumer
+ * @param string $callback
+ * @return MWOAuthToken
+ */
+ public function new_request_token( $consumer, $callback = 'oob' ) {
+ $token = self::newToken();
+ $cacheConsumerKey = Utils::getCacheKey( 'consumer', 'request', $token->key );
+ $cacheTokenKey = Utils::getCacheKey(
+ 'token', $consumer->key, 'request', $token->key
+ );
+ $cacheCallbackKey = Utils::getCacheKey(
+ 'callback', $consumer->key, 'request', $token->key
+ );
+ $this->cache->add( $cacheConsumerKey, $consumer->key, 600 ); // 10 minutes. Kindof arbitray.
+ $this->cache->add( $cacheTokenKey, $token, 600 ); // 10 minutes. Kindof arbitray.
+ $this->cache->add( $cacheCallbackKey, $callback, 600 ); // 10 minutes. Kindof arbitray.
+ $this->logger->debug( __METHOD__ .
+ ": New request token {$token->key} for {$consumer->key} with callback {$callback}" );
+ return $token;
+ }
+
+ /**
+ * Return a consumer key associated with the given request token.
+ *
+ * @param MWOAuthToken $requestToken the request token
+ * @return string|false the consumer key or false if nothing is stored for the request token
+ */
+ public function getConsumerKey( $requestToken ) {
+ $cacheKey = Utils::getCacheKey( 'consumer', 'request', $requestToken );
+ $consumerKey = $this->cache->get( $cacheKey );
+ return $consumerKey;
+ }
+
+ /**
+ * Return a stored callback URL parameter given by the consumer in /initiate.
+ * It throws an exception if callback URL parameter does not exist in the cache.
+ * A stored callback URL parameter is deleted from the cache once read for the first
+ * time.
+ *
+ * @param string $consumerKey the consumer key
+ * @param string $requestKey original request key from /initiate
+ * @throws MWOAuthException
+ * @return string|false the stored callback URL parameter
+ */
+ public function getCallbackUrl( $consumerKey, $requestKey ) {
+ $cacheKey = Utils::getCacheKey( 'callback', $consumerKey, 'request', $requestKey );
+ $callback = $this->cache->get( $cacheKey );
+ if ( $callback === null || !is_string( $callback ) ) {
+ throw new MWOAuthException( 'mwoauthdatastore-callback-not-found' );
+ }
+ $this->cache->delete( $cacheKey );
+ return $callback;
+ }
+
+ /**
+ * Return a new access token attached to this consumer for the user associated with this
+ * token if the request token is authorized. Should also invalidate the request token.
+ *
+ * @param MWOAuthToken $token the request token that started this
+ * @param Consumer $consumer
+ * @param int|null $verifier
+ * @throws MWOAuthException
+ * @return MWOAuthToken the access token
+ */
+ public function new_access_token( $token, $consumer, $verifier = null ) {
+ $this->logger->debug( __METHOD__ .
+ ": Getting new access token for token {$token->key}, consumer {$consumer->key}" );
+
+ if ( !$token->getVerifyCode() || !$token->getAccessKey() ) {
+ throw new MWOAuthException( 'mwoauthdatastore-bad-token' );
+ } elseif ( $token->getVerifyCode() !== $verifier ) {
+ throw new MWOAuthException( 'mwoauthdatastore-bad-verifier' );
+ }
+
+ $cacheKey = Utils::getCacheKey( 'token',
+ $consumer->getConsumerKey(), 'request', $token->key );
+ $accessToken = $this->lookup_token( $consumer, 'access', $token->getAccessKey() );
+ $this->cache->set( $cacheKey, '**USED**', 600 );
+ $this->logger->debug( __METHOD__ .
+ ": New access token {$accessToken->key} for {$consumer->key}" );
+ return $accessToken;
+ }
+
+ /**
+ * Update a request token. The token probably already exists, but had another attribute added.
+ *
+ * @param MWOAuthToken $token the token to store
+ * @param Consumer|OAuthConsumer $consumer
+ */
+ public function updateRequestToken( $token, $consumer ) {
+ $cacheKey = Utils::getCacheKey( 'token', $consumer->key, 'request', $token->key );
+ $this->cache->set( $cacheKey, $token, 600 ); // 10 more minutes. Kindof arbitray.
+ }
+
+ /**
+ * Return the string representing the Consumer's public RSA key
+ *
+ * @param string $consumerKey the string value of the Consumer's key
+ * @return string|null
+ */
+ public function getRSAKey( $consumerKey ) {
+ $cmr = Consumer::newFromKey( $this->centralReplica, $consumerKey );
+ return $cmr ? $cmr->getRsaKey() : null;
+ }
+}
diff --git a/OAuth/src/Backend/MWOAuthException.php b/OAuth/src/Backend/MWOAuthException.php
new file mode 100644
index 00000000..844bb210
--- /dev/null
+++ b/OAuth/src/Backend/MWOAuthException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+
+class MWOAuthException extends OAuthException {
+ public $msg, $params;
+
+ /**
+ * Exception that may be shown to an end user
+ * @param string $msg Message key (string) for error text
+ * @param array|null $params with parameters to wfMessage()
+ */
+ public function __construct( $msg, $params = null ) {
+ $this->msg = $msg;
+ $this->params = $params;
+ parent::__construct(
+ wfMessage( $msg, $params )->inLanguage( 'en' )->useDatabase( false )->plain()
+ );
+ }
+
+}
diff --git a/OAuth/src/Backend/MWOAuthRequest.php b/OAuth/src/Backend/MWOAuthRequest.php
new file mode 100644
index 00000000..00dec824
--- /dev/null
+++ b/OAuth/src/Backend/MWOAuthRequest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+use MediaWiki\Extensions\OAuth\Lib\OAuthUtil;
+use MediaWiki\Logger\LoggerFactory;
+
+/**
+ * @file
+ * @ingroup OAuth
+ *
+ * @license GPL-2.0-or-later
+ * @author Chris Steipp
+ */
+
+class MWOAuthRequest extends OAuthRequest {
+ private $sourceIP;
+
+ public function __construct( $httpMethod, $httpUrl, $parameters, $sourcIP = false ) {
+ $this->sourceIP = $sourcIP;
+ parent::__construct( $httpMethod, $httpUrl, $parameters );
+ }
+
+ public function getConsumerKey() {
+ $key = '';
+ if ( isset( $this->parameters['oauth_consumer_key'] ) ) {
+ $key = $this->parameters['oauth_consumer_key'];
+ }
+ return $key;
+ }
+
+ /**
+ * Track the source IP of the request, so we can enforce the IP whitelist
+ * @return string $ip the ip of the source
+ */
+ public function getSourceIP() {
+ return $this->sourceIP;
+ }
+
+ public static function fromRequest( \WebRequest $request ) {
+ $httpMethod = strtoupper( $request->getMethod() );
+ $httpUrl = $request->getFullRequestURL();
+ $logger = LoggerFactory::getInstance( 'OAuth' );
+
+ // Find request headers
+ $requestHeaders = Utils::getHeaders();
+
+ // Parse the query-string to find GET parameters
+ $parameters = $request->getQueryValuesOnly();
+
+ // It's a POST request of the proper content-type, so parse POST
+ // parameters and add those overriding any duplicates from GET
+ if ( $request->wasPosted()
+ && isset( $requestHeaders['Content-Type'] )
+ && strpos(
+ $requestHeaders['Content-Type'],
+ 'application/x-www-form-urlencoded'
+ ) === 0
+ ) {
+ $postData = OAuthUtil::parse_parameters( $request->getRawPostString() );
+ $parameters = array_merge( $parameters, $postData );
+ }
+
+ // We have a Authorization-header with OAuth data. Parse the header
+ // and add those overriding any duplicates from GET or POST
+ if ( isset( $requestHeaders['Authorization'] )
+ && substr( $requestHeaders['Authorization'], 0, 6 ) == 'OAuth '
+ ) {
+ $headerParameters = OAuthUtil::split_header(
+ $requestHeaders['Authorization']
+ );
+ $parameters = array_merge( $parameters, $headerParameters );
+ }
+
+ return new self( $httpMethod, $httpUrl, $parameters, $request->getIP() );
+ }
+}
diff --git a/OAuth/src/Backend/MWOAuthServer.php b/OAuth/src/Backend/MWOAuthServer.php
new file mode 100644
index 00000000..f57c8492
--- /dev/null
+++ b/OAuth/src/Backend/MWOAuthServer.php
@@ -0,0 +1,328 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthServer;
+
+class MWOAuthServer extends OAuthServer {
+ /** @var MWOAuthDataStore */
+ protected $data_store;
+
+ /**
+ * Return a consumer key associated with the given request token.
+ *
+ * @param MWOAuthToken $requestToken the request token
+ * @return string|false the consumer key or false if nothing is stored for the request token
+ */
+ public function getConsumerKey( $requestToken ) {
+ return $this->data_store->getConsumerKey( $requestToken );
+ }
+
+ /**
+ * Process a request_token request returns the request token on success. This
+ * also checks the IP restriction, which the OAuthServer method did not.
+ *
+ * @param MWOAuthRequest &$request the request
+ * @return MWOAuthToken
+ * @throws MWOAuthException
+ */
+ public function fetch_request_token( &$request ) {
+ $this->get_version( $request );
+
+ /** @var Consumer $consumer */
+ $consumer = $this->get_consumer( $request );
+
+ // Consumer must not be owner-only
+ if ( $consumer->getOwnerOnly() ) {
+ throw new MWOAuthException( 'mwoauthserver-consumer-owner-only', [
+ $consumer->getName(),
+ \SpecialPage::getTitleFor(
+ 'OAuthConsumerRegistration', 'update/' . $consumer->getConsumerKey()
+ ),
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E010',
+ 'E010',
+ true
+ ) )
+ ] );
+ }
+
+ // Consumer must have a key for us to verify
+ if ( !$consumer->getSecretKey() && !$consumer->getRsaKey() ) {
+ throw new MWOAuthException( 'mwoauthserver-consumer-no-secret', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E011',
+ 'E011',
+ true
+ ) )
+ ] );
+ }
+
+ $this->checkSourceIP( $consumer, $request );
+
+ // no token required for the initial token request
+ $token = null;
+
+ $this->check_signature( $request, $consumer, $token );
+
+ $callback = $request->get_parameter( 'oauth_callback' );
+
+ $this->checkCallback( $consumer, $callback );
+
+ $new_token = $this->data_store->new_request_token( $consumer, $callback );
+ $new_token->oauth_callback_confirmed = 'true';
+ return $new_token;
+ }
+
+ /**
+ * Ensure the callback is "oob" or that the registered callback is a valid
+ * prefix of the supplied callback. It throws an exception if callback is
+ * invalid.
+ *
+ * In MediaWiki, we require the callback to be established at
+ * registration. OAuth 1.0a (rfc5849, section 2.1) specifies that
+ * oauth_callback is required for the temporary credentials, and "If the
+ * client is unable to receive callbacks or a callback URI has been
+ * established via other means, the parameter value MUST be set to "oob"
+ * (case sensitive), to indicate an out-of-band configuration." Otherwise,
+ * client can provide a callback and the configured callback must be
+ * a prefix of the supplied callback. The matching performed here is based
+ * on parsed URL components rather than strict string matching. Protocol
+ * upgrades from http to https are also allowed.
+ *
+ * @param Consumer $consumer
+ * @param string $callback
+ * @return void
+ * @throws MWOAuthException
+ */
+ private function checkCallback( $consumer, $callback ) {
+ if ( !$consumer->getCallbackIsPrefix() ) {
+ if ( $callback !== 'oob' ) {
+ throw new MWOAuthException( 'mwoauth-callback-not-oob' );
+ }
+
+ return;
+ }
+
+ if ( !$callback ) {
+ throw new MWOAuthException( 'mwoauth-callback-not-oob-or-prefix' );
+ }
+ if ( $callback === 'oob' ) {
+ return;
+ }
+
+ $reqCallback = wfParseUrl( $callback );
+ if ( $reqCallback === false ) {
+ throw new MWOAuthException( 'mwoauth-callback-not-oob-or-prefix' );
+ }
+
+ $knownCallback = wfParseUrl( $consumer->getCallbackUrl() );
+ $exactPath = array_key_exists( 'query', $knownCallback );
+
+ $match =
+ // Protocol can be upgraded from http to https
+ self::looseSchemeMatch( $knownCallback['scheme'], $reqCallback['scheme'] ) &&
+ // Host must match exactly
+ $knownCallback['host'] === $reqCallback['host'] &&
+ // Port must be either missing from both or an exact match
+ static::getOrNull( 'port', $knownCallback ) ===
+ static::getOrNull( 'port', $reqCallback ) &&
+ // Path must be an exact match if query is provided in the
+ // registered callback. Otherwise it must be a prefix match if
+ // provided in the registered callback or anything if no path was
+ // included in the registered callback at all.
+ static::componentMatches( 'path', $knownCallback, $reqCallback, $exactPath ) &&
+ // Query string must be aprefix match if provided in the
+ // registered callback.
+ static::componentMatches( 'query', $knownCallback, $reqCallback );
+
+ if ( !$match ) {
+ throw new MWOAuthException( 'mwoauth-callback-not-oob-or-prefix' );
+ }
+ }
+
+ /**
+ * Compare URL schemes for a match.
+ *
+ * Allows 'https' to match an expected 'http' value.
+ *
+ * @param string $want
+ * @param string $got
+ * @return bool
+ */
+ private static function looseSchemeMatch( $want, $got ) {
+ if ( $want === 'http' ) {
+ return in_array( $got, [ 'http', 'https' ], true );
+ } else {
+ return $want === $got;
+ }
+ }
+
+ /**
+ * Get a named value from an array or return null if the key does not
+ * exist.
+ *
+ * @param string $key
+ * @param array $arr
+ * @return mixed
+ */
+ private static function getOrNull( $key, $arr ) {
+ return array_key_exists( $key, $arr ) ? $arr[$key] : null;
+ }
+
+ /**
+ * Check that a callback URL component matches the expected value.
+ *
+ * @param string $part URL component name
+ * @param array $expect Expected URL components
+ * @param array $got Posted URl components
+ * @param bool $exact Perform exact match instead of prefix match
+ * @return bool
+ */
+ private static function componentMatches(
+ $part, $expect, $got, $exact = false
+ ) {
+ $match = false;
+ if ( !array_key_exists( $part, $expect ) ) {
+ // Anything in the request is ok if we do not have the URL part in
+ // the expected values
+ $match = true;
+ } elseif ( !array_key_exists( $part, $got ) ) {
+ $match = false;
+ } elseif ( $exact ) {
+ $match = $expect[$part] === $got[$part];
+ } else {
+ $want = (string)$expect[$part];
+ $have = (string)$got[$part];
+ $len = strlen( $want );
+ $match = $want === substr( $have, 0, $len );
+ }
+ return $match;
+ }
+
+ /**
+ * process an access_token request
+ * returns the access token on success
+ *
+ * @param MWOAuthRequest &$request the request
+ * @return MWOAuthToken
+ * @throws MWOAuthException
+ */
+ public function fetch_access_token( &$request ) {
+ $this->get_version( $request );
+
+ /** @var Consumer $consumer */
+ $consumer = $this->get_consumer( $request );
+
+ // Consumer must not be owner-only
+ if ( $consumer->getOwnerOnly() ) {
+ throw new MWOAuthException( 'mwoauthserver-consumer-owner-only', [
+ $consumer->getName(),
+ \SpecialPage::getTitleFor(
+ 'OAuthConsumerRegistration', 'update/' . $consumer->getConsumerKey()
+ ),
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E010',
+ 'E010',
+ true
+ ) )
+ ] );
+ }
+
+ // Consumer must have a key for us to verify
+ if ( !$consumer->getSecretKey() && !$consumer->getRsaKey() ) {
+ throw new MWOAuthException( 'mwoauthserver-consumer-no-secret', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E011',
+ 'E011',
+ true
+ ) )
+ ] );
+ }
+
+ $this->checkSourceIP( $consumer, $request );
+
+ // requires authorized request token
+ $token = $this->get_token( $request, $consumer, 'request' );
+
+ if ( !$token->secret ) {
+ // This token has a blank secret.. something is wrong
+ throw new MWOAuthException( 'mwoauthdatastore-bad-token' );
+ }
+
+ $this->check_signature( $request, $consumer, $token );
+
+ // Rev A change
+ $verifier = $request->get_parameter( 'oauth_verifier' );
+ $this->logger->debug( __METHOD__ . ": verify code is '$verifier'" );
+ $new_token = $this->data_store->new_access_token( $token, $consumer, $verifier );
+
+ return $new_token;
+ }
+
+ /**
+ * Wrap the call to the parent function and check that the source IP of
+ * the request is allowed by this consumer's restrictions.
+ * @param MWOAuthRequest &$request
+ * @return array
+ */
+ public function verify_request( &$request ) {
+ list( $consumer, $token ) = parent::verify_request( $request );
+ $this->checkSourceIP( $consumer, $request );
+ return [ $consumer, $token ];
+ }
+
+ /**
+ * Ensure the request comes from an approved IP address, if IP restriction has been
+ * setup by the Consumer. It throws an exception if IP address is invalid.
+ *
+ * @param Consumer $consumer
+ * @param MWOAuthRequest $request
+ * @throws MWOAuthException
+ */
+ private function checkSourceIP( $consumer, $request ) {
+ $restrictions = $consumer->getRestrictions();
+ if ( !$restrictions->checkIP( $request->getSourceIP() ) ) {
+ throw new MWOAuthException( 'mwoauthdatastore-bad-source-ip' );
+ }
+ }
+
+ /**
+ * @deprecated User MWOAuthConsumer::authorize(...)
+ *
+ * @param string $consumerKey
+ * @param string $requestTokenKey
+ * @param \User $mwUser
+ * @param bool $update
+ * @return string
+ */
+ public function authorize( $consumerKey, $requestTokenKey, \User $mwUser, $update ) {
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $consumer = Consumer::newFromKey( $dbr, $consumerKey );
+ return $consumer->authorize( $mwUser, $update, $consumer->getGrants(), $requestTokenKey );
+ }
+
+ /**
+ * Attempts to find an authorization by this user for this consumer. Since a user can
+ * accept a consumer multiple times (once for "*" and once for each specific wiki),
+ * there can several access tokens per-wiki (with varying grants) for a consumer.
+ * This will choose the most wiki-specific access token. The precedence is:
+ * a) The acceptance for wiki X if the consumer is applicable only to wiki X
+ * b) The acceptance for wiki $wikiId (if the consumer is applicable to it)
+ * c) The acceptance for wikis "*" (all wikis)
+ *
+ * Users might want more grants on some wikis than on "*". Note that the reverse would not
+ * make sense, since the consumer could just use the "*" acceptance if it has more grants.
+ *
+ * @param \User $mwUser (local wiki user) User who may or may not have authorizations
+ * @param Consumer $consumer
+ * @param string $wikiId
+ * @throws MWOAuthException
+ * @return ConsumerAcceptance
+ * @deprecated Use MWOAuthConsumer::getCurrentAuthorization(...)
+ */
+ public function getCurrentAuthorization( \User $mwUser, $consumer, $wikiId ) {
+ wfDeprecated( __METHOD__ );
+ return $consumer->getCurrentAuthorization( $mwUser, $wikiId );
+ }
+}
diff --git a/OAuth/src/Backend/MWOAuthSignatureMethod_RSA_SHA1.php b/OAuth/src/Backend/MWOAuthSignatureMethod_RSA_SHA1.php
new file mode 100644
index 00000000..46e7b63f
--- /dev/null
+++ b/OAuth/src/Backend/MWOAuthSignatureMethod_RSA_SHA1.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthDataStore;
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_RSA_SHA1;
+
+class MWOAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod_RSA_SHA1 {
+ /** @var MWOAuthDataStore */
+ protected $store;
+ /** @var string PEM encoded RSA private key */
+ private $privateKey;
+
+ /**
+ * @param OAuthDataStore $store
+ * @param string|null $privateKey RSA private key, passed to openssl_get_privatekey
+ */
+ public function __construct( OAuthDataStore $store, $privateKey = null ) {
+ $this->store = $store;
+ $this->privateKey = $privateKey;
+
+ if ( $privateKey !== null ) {
+ $key = openssl_pkey_get_private( $privateKey );
+ if ( !$key ) {
+ throw new OAuthException( "Invalid private key given" );
+ }
+ $details = openssl_pkey_get_details( $key );
+ if ( $details['type'] !== OPENSSL_KEYTYPE_RSA ) {
+ throw new OAuthException( "Key is not an RSA key" );
+ }
+ openssl_pkey_free( $key );
+ }
+ }
+
+ /**
+ * Get the public certificate, used to verify the request. In our case, we get
+ * the Consumer's key, and lookup the registered cert from the datastore.
+ * @param OAuthRequest &$request request recieved by the server, that we're going to verify
+ * @return string representing the public certificate
+ */
+ protected function fetch_public_cert( &$request ) {
+ $consumerKey = $request->get_parameter( 'oauth_consumer_key' );
+ return $this->store->getRSAKey( $consumerKey );
+ }
+
+ /**
+ * If you want to reuse this code to write your Consumer, implement
+ * this function to get your private key, so you can sign the request.
+ * @param OAuthRequest &$request
+ * @return string
+ * @throws OAuthException
+ */
+ protected function fetch_private_cert( &$request ) {
+ if ( $this->privateKey === null ) {
+ throw new OAuthException( "No private key was set" );
+ }
+ return $this->privateKey;
+ }
+}
diff --git a/OAuth/src/Backend/MWOAuthToken.php b/OAuth/src/Backend/MWOAuthToken.php
new file mode 100644
index 00000000..82315403
--- /dev/null
+++ b/OAuth/src/Backend/MWOAuthToken.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+
+class MWOAuthToken extends OAuthToken {
+ // Keep the verification code here
+ protected $code;
+ // Token to find grant in oauth_accepted_consumer
+ protected $accessTokenKey;
+
+ public function addVerifyCode( $code ) {
+ $this->code = $code;
+ }
+
+ public function getVerifyCode() {
+ return $this->code;
+ }
+
+ public function addAccessKey( $key ) {
+ $this->accessTokenKey = $key;
+ }
+
+ public function getAccessKey() {
+ return $this->accessTokenKey;
+ }
+}
diff --git a/OAuth/src/Backend/OAuth1Consumer.php b/OAuth/src/Backend/OAuth1Consumer.php
new file mode 100644
index 00000000..452c9f9c
--- /dev/null
+++ b/OAuth/src/Backend/OAuth1Consumer.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use MWException;
+use User;
+
+/**
+ * (c) Dejan Savuljesku 2019, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * This class mainly exists to enable clean separation
+ * of OAuth 1.0a and OAuth 2.0 code
+ *
+ * Representation of an OAuth 1.0a consumer.
+ */
+class OAuth1Consumer extends Consumer {
+
+ /**
+ * The user has authorized the request by this consumer, with this request token. Update
+ * everything so that the consumer can swap the request token for an access token. Then
+ * generate the callback URL where we will redirect our user back to the consumer.
+ *
+ * @param User $mwUser
+ * @param bool $update
+ * @param array $grants
+ * @param null $requestTokenKey
+ * @return string
+ * @throws MWOAuthException
+ * @throws MWException
+ */
+ public function authorize( \User $mwUser, $update, $grants, $requestTokenKey = null ) {
+ $this->conductAuthorizationChecks( $mwUser );
+
+ // Generate and Update the tokens:
+ // * Generate a new Verification code, and add it to the request token
+ // * Either add or update the authorization
+ // ** Generate a new access token if this is a new authorization
+ // * Resave request token with the access token
+ $verifyCode = \MWCryptRand::generateHex( 32 );
+ $store = Utils::newMWOAuthDataStore();
+ $requestToken = $store->lookup_token( $this, 'request', $requestTokenKey );
+ if ( !$requestToken || !( $requestToken instanceof MWOAuthToken ) ) {
+ throw new MWOAuthException( 'mwoauthserver-invalid-request-token' );
+ }
+ $requestToken->addVerifyCode( $verifyCode );
+
+ $cmra = $this->saveAuthorization( $mwUser, $update, $grants );
+ $accessToken = new MWOAuthToken( $cmra->getAccessToken(), '' );
+
+ $requestToken->addAccessKey( $accessToken->key );
+ $store->updateRequestToken( $requestToken, $this );
+ return $this->generateCallbackUrl(
+ $store, $requestToken->getVerifyCode(), $requestTokenKey
+ );
+ }
+
+ /**
+ * @return int
+ */
+ public function getOAuthVersion() {
+ return static::OAUTH_VERSION_1;
+ }
+}
diff --git a/OAuth/src/Backend/UpdaterHooks.php b/OAuth/src/Backend/UpdaterHooks.php
new file mode 100644
index 00000000..47eaae74
--- /dev/null
+++ b/OAuth/src/Backend/UpdaterHooks.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+/**
+ * Class containing updater functions for an OAuth environment
+ */
+class UpdaterHooks {
+ /**
+ * @param \DatabaseUpdater $updater
+ * @return bool
+ */
+ public static function addSchemaUpdates( \DatabaseUpdater $updater ) {
+ if ( !Utils::isCentralWiki() ) {
+ return true; // no tables to add
+ }
+
+ $dbType = $updater->getDB()->getType();
+
+ if ( $dbType == 'mysql' || $dbType == 'sqlite' ) {
+
+ $updater->addExtensionTable(
+ 'oauth_registered_consumer',
+ self::getPath( 'OAuth.sql', $dbType )
+ );
+
+ $updater->addExtensionField(
+ 'oauth_registered_consumer',
+ 'oarc_callback_is_prefix',
+ self::getPath( 'callback_is_prefix.sql', $dbType )
+ );
+
+ $updater->addExtensionField(
+ 'oauth_registered_consumer',
+ 'oarc_developer_agreement',
+ self::getPath( 'developer_agreement.sql', $dbType )
+ );
+
+ $updater->addExtensionField(
+ 'oauth_registered_consumer',
+ 'oarc_owner_only',
+ self::getPath( 'owner_only.sql', $dbType )
+ );
+
+ $updater->addExtensionField(
+ 'oauth_registered_consumer',
+ 'oarc_oauth_version',
+ self::getPath( 'oauth_version_registered.sql', $dbType )
+ );
+
+ $updater->addExtensionField(
+ 'oauth_registered_consumer',
+ 'oarc_oauth2_is_confidential',
+ self::getPath( 'oauth2_is_confidential.sql', $dbType )
+ );
+
+ $updater->addExtensionField(
+ 'oauth_registered_consumer',
+ 'oarc_oauth2_allowed_grants',
+ self::getPath( 'oauth2_allowed_grants.sql', $dbType )
+ );
+
+ $updater->addExtensionField(
+ 'oauth_accepted_consumer',
+ 'oaac_oauth_version',
+ self::getPath( 'oauth_version_accepted.sql', $dbType )
+ );
+
+ $updater->addExtensionTable(
+ 'oauth2_access_tokens',
+ self::getPath( 'oauth2_access_tokens.sql', $dbType )
+ );
+
+ $updater->addExtensionIndex(
+ 'oauth2_access_tokens',
+ 'oaat_acceptance_id',
+ self::getPath( 'index_on_oaat_acceptance_id.sql', $dbType )
+ );
+
+ }
+ return true;
+ }
+
+ /**
+ * @param string $filename Name of the patch file (without path).
+ * The file should be in the schema/<dbtype>/ directory
+ * or the schema/ directory.
+ * @param string $dbType 'mysql' or 'sqlite'
+ * @return string
+ */
+ protected static function getPath( $filename, $dbType ) {
+ $base = dirname( dirname( __DIR__ ) ) . '/schema';
+ if ( file_exists( "$base/$dbType/$filename" ) ) {
+ return "$base/$dbType/$filename";
+ }
+ return "$base/$filename";
+ }
+}
diff --git a/OAuth/src/Backend/Utils.php b/OAuth/src/Backend/Utils.php
new file mode 100644
index 00000000..7c6bafd7
--- /dev/null
+++ b/OAuth/src/Backend/Utils.php
@@ -0,0 +1,471 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Backend;
+
+use EchoEvent;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_HMAC_SHA1;
+use MediaWiki\MediaWikiServices;
+use User;
+use Wikimedia\Rdbms\DBConnRef;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Static utility functions for OAuth
+ *
+ * @file
+ * @ingroup OAuth
+ */
+class Utils {
+ /**
+ * @return bool
+ */
+ public static function isCentralWiki() {
+ global $wgMWOAuthCentralWiki;
+
+ return ( wfWikiId() === $wgMWOAuthCentralWiki );
+ }
+
+ /**
+ * @param int $index DB_MASTER/DB_REPLICA
+ * @return DBConnRef
+ */
+ public static function getCentralDB( $index ) {
+ global $wgMWOAuthCentralWiki, $wgMWOAuthReadOnly;
+
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+
+ // T244415: Use the master if there were changes
+ if ( $index === DB_REPLICA && $lbFactory->hasOrMadeRecentMasterChanges() ) {
+ $index = DB_MASTER;
+ }
+
+ $db = $lbFactory->getMainLB( $wgMWOAuthCentralWiki )->getLazyConnectionRef(
+ $index, [], $wgMWOAuthCentralWiki );
+ $db->daoReadOnly = $wgMWOAuthReadOnly;
+ return $db;
+ }
+
+ /**
+ * @return \BagOStuff
+ */
+ public static function getSessionCache() {
+ global $wgMWOAuthSessionCacheType;
+ global $wgSessionCacheType;
+
+ $sessionCacheType = $wgMWOAuthSessionCacheType ?? $wgSessionCacheType;
+ return \ObjectCache::getInstance( $sessionCacheType );
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @return array
+ */
+ public static function getConsumerStateCounts( DBConnRef $db ) {
+ $res = $db->select( 'oauth_registered_consumer',
+ [ 'oarc_stage', 'count' => 'COUNT(*)' ],
+ [],
+ __METHOD__,
+ [ 'GROUP BY' => 'oarc_stage' ]
+ );
+ $table = [
+ Consumer::STAGE_APPROVED => 0,
+ Consumer::STAGE_DISABLED => 0,
+ Consumer::STAGE_EXPIRED => 0,
+ Consumer::STAGE_PROPOSED => 0,
+ Consumer::STAGE_REJECTED => 0,
+ ];
+ foreach ( $res as $row ) {
+ $table[(int)$row->oarc_stage] = (int)$row->count;
+ }
+ return $table;
+ }
+
+ /**
+ * Get request headers.
+ * Sanitizes the output of apache_request_headers because
+ * we always want the keys to be Cased-Like-This and arh()
+ * returns the headers in the same case as they are in the
+ * request
+ * @return array Header name => value
+ */
+ public static function getHeaders() {
+ $request = \RequestContext::getMain()->getRequest();
+ $headers = $request->getAllHeaders();
+
+ $out = [];
+ foreach ( $headers as $key => $value ) {
+ $key = str_replace(
+ " ",
+ "-",
+ ucwords( strtolower( str_replace( "-", " ", $key ) ) )
+ );
+ $out[$key] = $value;
+ }
+ return $out;
+ }
+
+ /**
+ * Test this request for an OAuth Authorization header
+ * @param \WebRequest $request the MediaWiki request
+ * @return bool true if a header was found
+ */
+ public static function hasOAuthHeaders( \WebRequest $request ) {
+ $header = $request->getHeader( 'Authorization' );
+ if ( $header !== false && substr( $header, 0, 6 ) == 'OAuth ' ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Make a cache key for the given arguments, that (hopefully) won't clash with
+ * anything else in your cache
+ * @param string ...$args
+ * @return string
+ */
+ public static function getCacheKey( ...$args ) {
+ global $wgMWOAuthCentralWiki;
+
+ return "OAUTH:$wgMWOAuthCentralWiki:" . implode( ':', $args );
+ }
+
+ /**
+ * @param DBConnRef $dbw
+ * @return void
+ */
+ public static function runAutoMaintenance( DBConnRef $dbw ) {
+ global $wgMWOAuthRequestExpirationAge;
+
+ if ( $wgMWOAuthRequestExpirationAge <= 0 ) {
+ return;
+ }
+
+ $cutoff = time() - $wgMWOAuthRequestExpirationAge;
+ $fname = __METHOD__;
+ \DeferredUpdates::addUpdate(
+ new \AutoCommitUpdate(
+ $dbw,
+ __METHOD__,
+ function ( IDatabase $dbw ) use ( $cutoff, $fname ) {
+ $dbw->update(
+ 'oauth_registered_consumer',
+ [
+ 'oarc_stage' => Consumer::STAGE_EXPIRED,
+ 'oarc_stage_timestamp' => $dbw->timestamp()
+ ],
+ [
+ 'oarc_stage' => Consumer::STAGE_PROPOSED,
+ 'oarc_stage_timestamp < ' .
+ $dbw->addQuotes( $dbw->timestamp( $cutoff ) )
+ ],
+ $fname
+ );
+ }
+ )
+ );
+ }
+
+ /**
+ * Get the pretty name of an OAuth wiki ID restriction value
+ *
+ * @param string $wikiId A wiki ID or '*'
+ * @return string
+ */
+ public static function getWikiIdName( $wikiId ) {
+ if ( $wikiId === '*' ) {
+ return wfMessage( 'mwoauth-consumer-allwikis' )->text();
+ } else {
+ $host = \WikiMap::getWikiName( $wikiId );
+ if ( strpos( $host, '.' ) ) {
+ return $host; // e.g. "en.wikipedia.org"
+ } else {
+ return $wikiId;
+ }
+ }
+ }
+
+ /**
+ * Get the pretty names of all local wikis
+ *
+ * @return array associative array of local wiki names indexed by wiki ID
+ */
+ public static function getAllWikiNames() {
+ global $wgConf;
+ $wikiNames = [];
+ foreach ( $wgConf->getLocalDatabases() as $dbname ) {
+ $name = self::getWikiIdName( $dbname );
+ if ( $name != $dbname ) {
+ $wikiNames[$dbname] = $name;
+ }
+ }
+ return $wikiNames;
+ }
+
+ /**
+ * Quickly get a new server with all the default configurations
+ *
+ * @return MWOAuthServer with default configurations
+ */
+ public static function newMWOAuthServer() {
+ $store = static::newMWOAuthDataStore();
+ $server = new MWOAuthServer( $store );
+ $server->add_signature_method( new OAuthSignatureMethod_HMAC_SHA1() );
+ $server->add_signature_method( new MWOAuthSignatureMethod_RSA_SHA1( $store ) );
+
+ return $server;
+ }
+
+ public static function newMWOAuthDataStore() {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = self::getCentralDB( DB_REPLICA );
+ $dbw = $lb->getServerCount() > 1 ? self::getCentralDB( DB_MASTER ) : null;
+ return new MWOAuthDataStore( $dbr, $dbw, self::getSessionCache() );
+ }
+
+ /**
+ * Given a central wiki user ID, get a central user name
+ *
+ * @param int $userId
+ * @param bool|\User|string $audience show hidden names based on this user, or false for public
+ * @throws \MWException
+ * @return string|bool User name, false if not found, empty string if name is hidden
+ */
+ public static function getCentralUserNameFromId( $userId, $audience = false ) {
+ global $wgMWOAuthSharedUserIDs, $wgMWOAuthSharedUserSource;
+
+ if ( $wgMWOAuthSharedUserIDs ) { // global ID required via hook
+ $lookup = \CentralIdLookup::factory( $wgMWOAuthSharedUserSource );
+ $name = $lookup->nameFromCentralId(
+ $userId,
+ $audience === 'raw'
+ ? \CentralIdLookup::AUDIENCE_RAW
+ : ( $audience ?: \CentralIdLookup::AUDIENCE_PUBLIC )
+ );
+ if ( $name === null ) {
+ $name = false;
+ }
+ } else {
+ $name = '';
+ $user = \User::newFromId( $userId );
+ if ( $audience === 'raw'
+ || !$user->isHidden()
+ || ( $audience instanceof \User && $audience->isAllowed( 'hideuser' ) )
+ ) {
+ $name = $user->getName();
+ }
+ }
+
+ return $name;
+ }
+
+ /**
+ * Given a central wiki user ID, get a local User object
+ *
+ * @param int $userId
+ * @throws \MWException
+ * @return \User|bool User or false if not found
+ */
+ public static function getLocalUserFromCentralId( $userId ) {
+ global $wgMWOAuthSharedUserIDs, $wgMWOAuthSharedUserSource;
+
+ if ( $wgMWOAuthSharedUserIDs ) { // global ID required via hook
+ $lookup = \CentralIdLookup::factory( $wgMWOAuthSharedUserSource );
+ $user = $lookup->localUserFromCentralId( $userId );
+ if ( $user === null || !$lookup->isAttached( $user ) ) {
+ $user = false;
+ }
+ } else {
+ $user = \User::newFromId( $userId );
+ }
+
+ return $user;
+ }
+
+ /**
+ * Given a local User object, get the user ID for that user on the central wiki
+ *
+ * @param \User $user
+ * @throws \MWException
+ * @return int|bool ID or false if not found
+ */
+ public static function getCentralIdFromLocalUser( \User $user ) {
+ global $wgMWOAuthSharedUserIDs, $wgMWOAuthSharedUserSource;
+
+ if ( $wgMWOAuthSharedUserIDs ) { // global ID required via hook
+ // T227688 do not rely on array autocreation for non-stdClass
+ if ( !isset( $user->oAuthUserData ) ) {
+ $user->oAuthUserData = [];
+ }
+
+ if ( isset( $user->oAuthUserData['centralId'] ) ) {
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $id = $user->oAuthUserData['centralId'];
+ } else {
+ $lookup = \CentralIdLookup::factory( $wgMWOAuthSharedUserSource );
+ if ( !$lookup->isAttached( $user ) ) {
+ $id = false;
+ } else {
+ $id = $lookup->centralIdFromLocalUser( $user );
+ if ( $id === 0 ) {
+ $id = false;
+ }
+ }
+ // Process cache the result to avoid queries
+ $user->oAuthUserData['centralId'] = $id;
+ }
+ } else {
+ $id = $user->getId();
+ }
+
+ return $id;
+ }
+
+ /**
+ * Given a username, get the user ID for that user on the central wiki.
+ * @param string $username
+ * @throws \MWException
+ * @return int|bool ID or false if not found
+ */
+ public static function getCentralIdFromUserName( $username ) {
+ global $wgMWOAuthSharedUserIDs, $wgMWOAuthSharedUserSource;
+
+ if ( $wgMWOAuthSharedUserIDs ) { // global ID required via hook
+ $lookup = \CentralIdLookup::factory( $wgMWOAuthSharedUserSource );
+ $id = $lookup->centralIdFromName( $username );
+ if ( $id === 0 ) {
+ $id = false;
+ }
+ } else {
+ $id = false;
+ $user = \User::newFromName( $username );
+ if ( $user instanceof \User && $user->getId() > 0 ) {
+ $id = $user->getId();
+ }
+ }
+
+ return $id;
+ }
+
+ /**
+ * Get the effective secret key/token to use for OAuth purposes.
+ *
+ * For example, the "secret key" and "access secret" values that are
+ * used for authenticating request should be the result of applying this
+ * function to the respective values stored in the DB. This means that
+ * a leak of DB values is not enough to impersonate consumers.
+ *
+ * @param string $secret
+ * @return string
+ */
+ public static function hmacDBSecret( $secret ) {
+ global $wgOAuthSecretKey, $wgSecretKey;
+
+ if ( empty( $wgOAuthSecretKey ) ) {
+ $secretKey = $wgSecretKey;
+ } else {
+ $secretKey = $wgOAuthSecretKey;
+ }
+
+ return $secretKey ? hash_hmac( 'sha1', $secret, $secretKey ) : $secret;
+ }
+
+ /**
+ * Get a link to the central wiki's user talk page of a user.
+ *
+ * @param string $username the username of the User Talk link
+ * @return string the (proto-relative, urlencoded) url of the central wiki's user talk page
+ */
+ public static function getCentralUserTalk( $username ) {
+ global $wgMWOAuthCentralWiki, $wgMWOAuthSharedUserIDs;
+
+ if ( $wgMWOAuthSharedUserIDs ) {
+ $url = \WikiMap::getForeignURL(
+ $wgMWOAuthCentralWiki,
+ "User_talk:$username"
+ );
+ } else {
+ $url = \Title::makeTitleSafe( NS_USER_TALK, $username )->getFullURL();
+ }
+ return $url;
+ }
+
+ /**
+ * @param array $grants
+ * @return bool
+ */
+ public static function grantsAreValid( array $grants ) {
+ // Remove our special grants before calling the core method
+ $grants = array_diff( $grants, [ 'mwoauth-authonly', 'mwoauth-authonlyprivate' ] );
+ return \MWGrants::grantsAreValid( $grants );
+ }
+
+ /**
+ * Given an OAuth consumer stage change event, find out who needs to be notified.
+ * Will be used as an EchoAttributeManager::ATTR_LOCATORS callback.
+ * @param EchoEvent $event
+ * @return User[]
+ */
+ public static function locateUsersToNotify( EchoEvent $event ) {
+ $agent = $event->getAgent();
+ $owner = self::getLocalUserFromCentralId( $event->getExtraParam( 'owner-id' ) );
+
+ $users = [];
+ switch ( $event->getType() ) {
+ case 'oauth-app-propose':
+ // notify OAuth admins about new proposed apps
+ $oauthAdmins = self::getOAuthAdmins();
+ foreach ( $oauthAdmins as $admin ) {
+ if ( $admin->equals( $owner ) ) {
+ continue;
+ }
+ $users[$admin->getId()] = $admin;
+ }
+ break;
+ case 'oauth-app-update':
+ case 'oauth-app-approve':
+ case 'oauth-app-reject':
+ case 'oauth-app-disable':
+ case 'oauth-app-reenable':
+ // notify owner if someone else changed the status of the app
+ if ( !$owner->equals( $agent ) ) {
+ $users[$owner->getId()] = $owner;
+ }
+ break;
+ }
+ return $users;
+ }
+
+ /**
+ * Get the change tag name for a given consumer.
+ * @param int $consumerId
+ * @return string
+ */
+ public static function getTagName( $consumerId ) {
+ return 'OAuth CID: ' . (int)$consumerId;
+ }
+
+ /**
+ * Check if a given change tag name should be reserved for this extension.
+ * @param string $tagName
+ * @return bool
+ */
+ public static function isReservedTagName( $tagName ) {
+ return strpos( strtolower( $tagName ), 'oauth cid:' ) === 0;
+ }
+
+ /**
+ * Return a list of all OAuth admins (or the first 5000 in the unlikely case that there is more
+ * than that).
+ * Should be called on the central OAuth wiki.
+ * @return User[]
+ */
+ protected static function getOAuthAdmins() {
+ global $wgOAuthGroupsToNotify;
+
+ if ( !$wgOAuthGroupsToNotify ) {
+ return [];
+ }
+
+ return iterator_to_array( User::findUsersByGroup( $wgOAuthGroupsToNotify ) );
+ }
+}
diff --git a/OAuth/src/Control/ConsumerAcceptanceAccessControl.php b/OAuth/src/Control/ConsumerAcceptanceAccessControl.php
new file mode 100644
index 00000000..43551f79
--- /dev/null
+++ b/OAuth/src/Control/ConsumerAcceptanceAccessControl.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Control;
+
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+
+class ConsumerAcceptanceAccessControl extends DAOAccessControl {
+ // accessor fields copied from MWOAuthConsumerAcceptance, except they can return a Message
+ // on access error
+
+ /**
+ * Database ID.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return int|\Message
+ */
+ public function getId() {
+ return $this->get( 'id' );
+ }
+
+ /**
+ * Wiki on which the user has authorized the consumer to access their account. Wiki ID or '*'
+ * for all.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getWiki() {
+ return $this->get( 'wiki' );
+ }
+
+ /**
+ * Central user ID of the authorizing user.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return int|\Message
+ */
+ public function getUserId() {
+ return $this->get( 'userId' );
+ }
+
+ /**
+ * Database ID of the consumer.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return int|\Message
+ */
+ public function getConsumerId() {
+ return $this->get( 'consumerId' );
+ }
+
+ /**
+ * The access token for the OAuth protocol
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getAccessToken() {
+ return $this->get( 'accessToken' );
+ }
+
+ /**
+ * Secret key used to derive the access secret for the OAuth protocol.
+ * The actual access secret will be calculated via MWOAuthUtils::hmacDBSecret() to mitigate
+ * DB leaks.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getAccessSecret() {
+ return $this->get( 'accessSecret' );
+ }
+
+ /**
+ * The list of grants which have been granted.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string[]|\Message
+ */
+ public function getGrants() {
+ return $this->get( 'grants' );
+ }
+
+ /**
+ * Date of the authorization, in TS_MW format.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getAccepted() {
+ return $this->get( 'accepted' );
+ }
+
+ // accessors for common formatting
+
+ /**
+ * Pretty wiki name.
+ * @return string|\Message
+ */
+ public function getWikiName() {
+ return $this->get( 'wiki', function ( $wikiId ) {
+ return Utils::getWikiIdName( $wikiId );
+ } );
+ }
+
+ /**
+ * @return ConsumerAcceptance
+ */
+ public function getDAO() {
+ return $this->dao;
+ }
+}
diff --git a/OAuth/src/Control/ConsumerAcceptanceSubmitControl.php b/OAuth/src/Control/ConsumerAcceptanceSubmitControl.php
new file mode 100644
index 00000000..ce7d3fcb
--- /dev/null
+++ b/OAuth/src/Control/ConsumerAcceptanceSubmitControl.php
@@ -0,0 +1,234 @@
+<?php
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+namespace MediaWiki\Extensions\OAuth\Control;
+
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Repository\AccessTokenRepository;
+use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * This handles the core logic of submitting/approving application
+ * consumer requests and the logic of managing approved consumers
+ *
+ * This control can be used on any wiki, not just the management one
+ *
+ * @TODO: improve error messages
+ */
+class ConsumerAcceptanceSubmitControl extends SubmitControl {
+ /** @var DBConnRef */
+ protected $dbw;
+
+ /** @var int */
+ protected $oauthVersion;
+
+ /**
+ * @param \IContextSource $context
+ * @param array $params
+ * @param DBConnRef $dbw Result of MWOAuthUtils::getCentralDB( DB_MASTER )
+ * @param int $oauthVersion
+ */
+ public function __construct(
+ \IContextSource $context, array $params, DBConnRef $dbw, $oauthVersion
+ ) {
+ parent::__construct( $context, $params );
+ $this->dbw = $dbw;
+ $this->oauthVersion = (int)$oauthVersion;
+ }
+
+ protected function getRequiredFields() {
+ $required = [
+ 'update' => [
+ 'acceptanceId' => '/^\d+$/',
+ 'grants' => function ( $s ) {
+ $grants = \FormatJson::decode( $s, true );
+ return is_array( $grants ) && Utils::grantsAreValid( $grants );
+ }
+ ],
+ 'renounce' => [
+ 'acceptanceId' => '/^\d+$/',
+ ],
+ ];
+ if ( $this->isOAuth2() ) {
+ $required['accept'] = [
+ 'client_id' => '/^[0-9a-f]{32}$/',
+ 'confirmUpdate' => '/^[01]$/',
+ ];
+ } else {
+ $required['accept'] = [
+ 'consumerKey' => '/^[0-9a-f]{32}$/',
+ 'requestToken' => '/^[0-9a-f]{32}$/',
+ 'confirmUpdate' => '/^[01]$/',
+ ];
+ }
+
+ return $required;
+ }
+
+ protected function checkBasePermissions() {
+ $user = $this->getUser();
+ if ( !$user->getID() ) {
+ return $this->failure( 'not_logged_in', 'badaccess-group0' );
+ } elseif ( !$user->isAllowed( 'mwoauthmanagemygrants' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( wfReadOnly() ) {
+ return $this->failure( 'readonly', 'readonlytext', wfReadOnlyReason() );
+ }
+ return $this->success();
+ }
+
+ protected function processAction( $action ) {
+ $user = $this->getUser(); // proposer or admin
+ $dbw = $this->dbw; // convenience
+
+ $centralUserId = Utils::getCentralIdFromLocalUser( $user );
+ if ( !$centralUserId ) { // sanity
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ switch ( $action ) {
+ case 'accept':
+ $payload = [];
+ $identifier = $this->isOAuth2() ? 'client_id' : 'consumerKey';
+ $cmr = Consumer::newFromKey( $this->dbw, $this->vals[$identifier] );
+ if ( !$cmr ) {
+ return $this->failure( 'invalid_consumer_key', 'mwoauth-invalid-consumer-key' );
+ } elseif ( !$cmr->isUsableBy( $user ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ try {
+ if ( $this->isOAuth2() ) {
+ $scopes = isset( $this->vals['scope'] ) ? explode( ' ', $this->vals['scope'] ) : [];
+ $payload = $cmr->authorize( $this->getUser(), (bool)$this->vals['confirmUpdate'], $scopes );
+ } else {
+ $callback = $cmr->authorize(
+ $this->getUser(),
+ (bool)$this->vals[ 'confirmUpdate' ],
+ $cmr->getGrants(),
+ $this->vals[ 'requestToken' ]
+ );
+ $payload = [ 'callbackUrl' => $callback ];
+ }
+ } catch ( MWOAuthException $exception ) {
+ return $this->failure( 'oauth_exception', $exception->msg, $exception->params );
+ } catch ( OAuthException $exception ) {
+ return $this->failure( 'oauth_exception',
+ 'mwoauth-oauth-exception', $exception->getMessage() );
+ }
+
+ LoggerFactory::getInstance( 'OAuth' )->info(
+ '{user} performed action {action} on consumer {consumer}', [
+ 'action' => 'accept',
+ 'user' => $user->getName(),
+ 'consumer' => $cmr->getConsumerKey(),
+ 'target' => Utils::getCentralUserNameFromId( $cmr->getUserId(), 'raw' ),
+ 'comment' => '',
+ 'clientip' => $this->getContext()->getRequest()->getIP(),
+ ]
+ );
+
+ return $this->success( $payload );
+ case 'update':
+ $cmra = ConsumerAcceptance::newFromId( $dbw, $this->vals['acceptanceId'] );
+ if ( !$cmra ) {
+ return $this->failure( 'invalid_access_token', 'mwoauth-invalid-access-token' );
+ } elseif ( $cmra->getUserId() !== $centralUserId ) {
+ return $this->failure( 'invalid_access_token', 'mwoauth-invalid-access-token' );
+ }
+ $cmr = Consumer::newFromId( $dbw, $cmra->getConsumerId() );
+
+ $grants = \FormatJson::decode( $this->vals['grants'], true ); // requested grants
+ $grants = array_unique( array_intersect(
+ array_merge(
+ \MWGrants::getHiddenGrants(), // implied grants
+ $grants // requested grants
+ ),
+ $cmr->getGrants() // Only keep the applicable ones
+ ) );
+
+ LoggerFactory::getInstance( 'OAuth' )->info(
+ '{user} performed action {action} on consumer {consumer}', [
+ 'action' => 'update-acceptance',
+ 'user' => $user->getName(),
+ 'consumer' => $cmr->getConsumerKey(),
+ 'target' => Utils::getCentralUserNameFromId( $cmr->getUserId(), 'raw' ),
+ 'comment' => '',
+ 'clientip' => $this->getContext()->getRequest()->getIP(),
+ ]
+ );
+ $cmra->setFields( [
+ 'grants' => array_intersect( $grants, $cmr->getGrants() ) // sanity
+ ] );
+ $cmra->save( $dbw );
+
+ return $this->success( $cmra );
+ case 'renounce':
+ $cmra = ConsumerAcceptance::newFromId( $dbw, $this->vals['acceptanceId'] );
+ if ( !$cmra ) {
+ return $this->failure( 'invalid_access_token', 'mwoauth-invalid-access-token' );
+ } elseif ( $cmra->getUserId() !== $centralUserId ) {
+ return $this->failure( 'invalid_access_token', 'mwoauth-invalid-access-token' );
+ }
+
+ $cmr = Consumer::newFromId( $dbw, $cmra->get( 'consumerId' ) );
+ LoggerFactory::getInstance( 'OAuth' )->info(
+ '{user} performed action {action} on consumer {consumer}', [
+ 'action' => 'renounce',
+ 'user' => $user->getName(),
+ 'consumer' => $cmr->getConsumerKey(),
+ 'target' => Utils::getCentralUserNameFromId( $cmr->getUserId(), 'raw' ),
+ 'comment' => '',
+ 'clientip' => $this->getContext()->getRequest()->getIP(),
+ ]
+ );
+
+ if ( $cmr->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ) {
+ $this->removeOAuth2AccessTokens( $cmra->getId() );
+ }
+ $cmra->delete( $dbw );
+
+ return $this->success( $cmra );
+ }
+ }
+
+ /**
+ * Convenience function
+ *
+ * @return bool
+ */
+ private function isOAuth2() {
+ return $this->oauthVersion === Consumer::OAUTH_VERSION_2;
+ }
+
+ /**
+ * @param int $approvalId
+ */
+ private function removeOAuth2AccessTokens( $approvalId ) {
+ $accessTokenRepository = new AccessTokenRepository();
+ $accessTokenRepository->deleteForApprovalId( $approvalId );
+ }
+}
diff --git a/OAuth/src/Control/ConsumerAccessControl.php b/OAuth/src/Control/ConsumerAccessControl.php
new file mode 100644
index 00000000..01ed9648
--- /dev/null
+++ b/OAuth/src/Control/ConsumerAccessControl.php
@@ -0,0 +1,262 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Control;
+
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+
+class ConsumerAccessControl extends DAOAccessControl {
+ // accessor fields copied from MWOAuthConsumer, except they can return a Message on access error
+
+ /**
+ * Internal ID (DB primary key).
+ * Returns a Message when the user does not have permission to see this field.
+ * @return int|\Message
+ */
+ public function getId() {
+ return $this->get( 'id' );
+ }
+
+ /**
+ * Consumer key (32-character hexadecimal string that's used in the OAuth protocol
+ * and in URLs). This is used as the consumer ID for most external purposes.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getConsumerKey() {
+ return $this->get( 'consumerKey' );
+ }
+
+ /**
+ * Name of the consumer.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getName() {
+ return $this->get( 'name' );
+ }
+
+ /**
+ * @return int
+ */
+ public function getOAuthVersion() {
+ return (int)$this->get( 'oauthVersion' );
+ }
+
+ /**
+ * Central ID of the owner.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return int|\Message
+ */
+ public function getUserId() {
+ return $this->get( 'userId' );
+ }
+
+ /**
+ * Consumer version. This is mostly meant for humans: different versions of the same
+ * application have different keys and are handled as different consumers internally.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getVersion() {
+ return $this->get( 'version' );
+ }
+
+ /**
+ * Callback URL (or prefix). The browser will be redirected to this URL at the end of
+ * an OAuth handshake. See getCallbackIsPrefix() for the interpretation of this field.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getCallbackUrl() {
+ return $this->get( 'callbackUrl' );
+ }
+
+ /**
+ * When true, getCallbackUrl() returns a prefix; the callback URL can be provided by the caller
+ * as long as the prefix matches. When false, the callback URL will be determined by
+ * getCallbackUrl().
+ * Returns a Message when the user does not have permission to see this field.
+ * @return bool|\Message
+ */
+ public function getCallbackIsPrefix() {
+ return $this->get( 'callbackIsPrefix' );
+ }
+
+ /**
+ * Description of the consumer. Currently interpreted as plain text; might change to wikitext
+ * in the future.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getDescription() {
+ return $this->get( 'description' );
+ }
+
+ /**
+ * Email address of the owner.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getEmail() {
+ return $this->get( 'email' );
+ }
+
+ /**
+ * Date of verifying the email, in TS_MW format. In practice this will be the same as
+ * getRegistration().
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getEmailAuthenticated() {
+ return $this->get( 'emailAuthenticated' );
+ }
+
+ /**
+ * Did the user accept the developer agreement (the terms of use checkbox at the bottom of the
+ * registration form)? Except for very old users, always true.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return bool|\Message
+ */
+ public function getDeveloperAgreement() {
+ return $this->get( 'developerAgreement' );
+ }
+
+ /**
+ * Owner-only consumers will use one-legged flow instead of three-legged (see
+ * https://github.com/Mashape/mashape-oauth/blob/master/FLOWS.md#oauth-10a-one-legged ); there
+ * is only one user (who is the same as the owner) and they learn the access token at
+ * consumer registration time.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return bool|\Message
+ */
+ public function getOwnerOnly() {
+ return $this->get( 'ownerOnly' );
+ }
+
+ /**
+ * The wiki on which the consumer is allowed to access user accounts. A wiki ID or '*' for all.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getWiki() {
+ return $this->get( 'wiki' );
+ }
+
+ /**
+ * The list of grants required by this application.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string[]|\Message
+ */
+ public function getGrants() {
+ return $this->get( 'grants' );
+ }
+
+ /**
+ * Consumer registration date in TS_MW format.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getRegistration() {
+ return $this->get( 'registration' );
+ }
+
+ /**
+ * Secret key used to derive the consumer secret for HMAC-SHA1 signed OAuth requests.
+ * The actual consumer secret will be calculated via MWOAuthUtils::hmacDBSecret() to mitigate
+ * DB leaks.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getSecretKey() {
+ return $this->get( 'secretKey' );
+ }
+
+ /**
+ * Public RSA key for RSA-SHA1 signerd OAuth requests.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getRsaKey() {
+ return $this->get( 'rsaKey' );
+ }
+
+ /**
+ * Application restrictions (such as allowed IPs).
+ * Returns a Message when the user does not have permission to see this field.
+ * @return \MWRestrictions|\Message
+ */
+ public function getRestrictions() {
+ return $this->get( 'restrictions' );
+ }
+
+ /**
+ * Stage at which the consumer is in the review workflow (proposed, approved etc).
+ * Returns a Message when the user does not have permission to see this field.
+ * @return int|\Message One of the STAGE_* constants
+ */
+ public function getStage() {
+ return $this->get( 'stage' );
+ }
+
+ /**
+ * Date at which the consumer was moved to the current stage, in TS_MW format.
+ * Returns a Message when the user does not have permission to see this field.
+ * @return string|\Message
+ */
+ public function getStageTimestamp() {
+ return $this->get( 'stageTimestamp' );
+ }
+
+ /**
+ * Is the consumer suppressed? (There is no plain deletion; the closest equivalent is the
+ * rejected/disabled stage.)
+ * Returns a Message when the user does not have permission to see this field.
+ * @return bool|\Message
+ */
+ public function getDeleted() {
+ return $this->get( 'deleted' );
+ }
+
+ // accessors for common formatting
+
+ /**
+ * Owner username.
+ * Note that this method triggers a DB lookup.
+ * @param \User|bool $audience show hidden names based on this user, or false for public
+ * @return string|\Message
+ */
+ public function getUserName( $audience = false ) {
+ return $this->get( 'userId', function ( $id ) use ( $audience ) {
+ return Utils::getCentralUserNameFromId( $id, $audience );
+ } );
+ }
+
+ /**
+ * Pretty wiki name.
+ * @return string|\Message
+ */
+ public function getWikiName() {
+ return $this->get( 'wiki', function ( $wikiId ) {
+ return Utils::getWikiIdName( $wikiId );
+ } );
+ }
+
+ /**
+ * Consumer name and version in a "Foo [1.0]" format.
+ * @return string|\Message
+ */
+ public function getNameAndVersion() {
+ return $this->get( 'name', function ( $s ) {
+ return $s . ' ' . $this->msg( 'brackets', $this->getVersion() )->plain();
+ } );
+ }
+
+ /**
+ * @return Consumer|ClientEntity
+ */
+ public function getDAO() {
+ return $this->dao;
+ }
+}
diff --git a/OAuth/src/Control/ConsumerSubmitControl.php b/OAuth/src/Control/ConsumerSubmitControl.php
new file mode 100644
index 00000000..fa93712c
--- /dev/null
+++ b/OAuth/src/Control/ConsumerSubmitControl.php
@@ -0,0 +1,550 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Control;
+
+use ExtensionRegistry;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthDataStore;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * This handles the core logic of approving/disabling consumers
+ * from using particular user accounts
+ *
+ * This control can only be used on the management wiki
+ *
+ * @TODO: improve error messages
+ */
+class ConsumerSubmitControl extends SubmitControl {
+ /**
+ * Names of the actions that can be performed on a consumer. These are the same as the
+ * options in getRequiredFields().
+ * @var array
+ */
+ public static $actions = [ 'propose', 'update', 'approve', 'reject', 'disable', 'reenable' ];
+
+ /** @var DBConnRef */
+ protected $dbw;
+
+ /**
+ * @param \IContextSource $context
+ * @param array $params
+ * @param DBConnRef $dbw Result of MWOAuthUtils::getCentralDB( DB_MASTER )
+ */
+ public function __construct( \IContextSource $context, array $params, DBConnRef $dbw ) {
+ parent::__construct( $context, $params );
+ $this->dbw = $dbw;
+ }
+
+ protected function getRequiredFields() {
+ $validateRsaKey = function ( $s ) {
+ if ( trim( $s ) === '' ) {
+ return true;
+ }
+ $key = openssl_pkey_get_public( $s );
+ if ( $key === false ) {
+ return false;
+ }
+ $info = openssl_pkey_get_details( $key );
+ if ( $info['type'] !== OPENSSL_KEYTYPE_RSA ) {
+ return false;
+ }
+ return true;
+ };
+
+ return [
+ // Proposer (application administrator) actions:
+ 'propose' => [
+ 'name' => '/^.{1,128}$/',
+ 'version' => '/^\d{1,3}(\.\d{1,2}){0,2}(-(dev|alpha|beta))?$/',
+ 'callbackUrl' => function ( $s, $vals ) {
+ return $vals['ownerOnly'] || wfParseUrl( $s ) !== false;
+ },
+ 'description' => '/^.*$/s',
+ 'email' => function ( $s ) {
+ return \Sanitizer::validateEmail( $s );
+ },
+ 'wiki' => function ( $s ) {
+ global $wgConf;
+ return ( $s === '*'
+ || in_array( $s, $wgConf->getLocalDatabases() )
+ || array_search( $s, Utils::getAllWikiNames() ) !== false
+ );
+ },
+ 'granttype' => '/^(authonly|authonlyprivate|normal)$/',
+ 'grants' => function ( $s ) {
+ $grants = \FormatJson::decode( $s, true );
+ return is_array( $grants ) && Utils::grantsAreValid( $grants );
+ },
+ 'rsaKey' => $validateRsaKey,
+ 'agreement' => function ( $s ) {
+ return ( $s == true );
+ },
+ ],
+ 'update' => [
+ 'consumerKey' => '/^[0-9a-f]{32}$/',
+ 'rsaKey' => $validateRsaKey,
+ 'resetSecret' => function ( $s ) {
+ return is_bool( $s );
+ },
+ 'reason' => '/^.{0,255}$/',
+ 'changeToken' => '/^[0-9a-f]{40}$/'
+ ],
+ // Approver (project administrator) actions:
+ 'approve' => [
+ 'consumerKey' => '/^[0-9a-f]{32}$/',
+ 'reason' => '/^.{0,255}$/',
+ 'changeToken' => '/^[0-9a-f]{40}$/'
+ ],
+ 'reject' => [
+ 'consumerKey' => '/^[0-9a-f]{32}$/',
+ 'reason' => '/^.{0,255}$/',
+ 'suppress' => '/^[01]$/',
+ 'changeToken' => '/^[0-9a-f]{40}$/'
+ ],
+ 'disable' => [
+ 'consumerKey' => '/^[0-9a-f]{32}$/',
+ 'reason' => '/^.{0,255}$/',
+ 'suppress' => '/^[01]$/',
+ 'changeToken' => '/^[0-9a-f]{40}$/'
+ ],
+ 'reenable' => [
+ 'consumerKey' => '/^[0-9a-f]{32}$/',
+ 'reason' => '/^.{0,255}$/',
+ 'changeToken' => '/^[0-9a-f]{40}$/'
+ ]
+ ];
+ }
+
+ protected function checkBasePermissions() {
+ global $wgBlockDisablesLogin;
+ $user = $this->getUser();
+ if ( !$user->getId() ) {
+ return $this->failure( 'not_logged_in', 'badaccess-group0' );
+ } elseif ( $user->isLocked() || $wgBlockDisablesLogin && $user->isBlocked() ) {
+ return $this->failure( 'user_blocked', 'badaccess-group0' );
+ } elseif ( wfReadOnly() ) {
+ return $this->failure( 'readonly', 'readonlytext', wfReadOnlyReason() );
+ } elseif ( !Utils::isCentralWiki() ) { // sanity
+ // This logs consumer changes to the local logging table on the central wiki
+ throw new \LogicException( "This can only be used from the OAuth management wiki." );
+ }
+ return $this->success();
+ }
+
+ protected function processAction( $action ) {
+ $context = $this->getContext();
+ $user = $this->getUser(); // proposer or admin
+ $dbw = $this->dbw; // convenience
+
+ $centralUserId = Utils::getCentralIdFromLocalUser( $user );
+ if ( !$centralUserId ) { // sanity
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ switch ( $action ) {
+ case 'propose':
+ if ( !$user->isAllowed( 'mwoauthproposeconsumer' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( !$user->isEmailConfirmed() ) {
+ return $this->failure( 'email_not_confirmed', 'mwoauth-consumer-email-unconfirmed' );
+ } elseif ( $user->getEmail() !== $this->vals['email'] ) {
+ // @TODO: allow any email and don't set emailAuthenticated below
+ return $this->failure( 'email_mismatched', 'mwoauth-consumer-email-mismatched' );
+ }
+
+ if ( Consumer::newFromNameVersionUser(
+ $dbw, $this->vals['name'], $this->vals['version'], $centralUserId
+ ) ) {
+ return $this->failure( 'consumer_exists', 'mwoauth-consumer-alreadyexists' );
+ }
+
+ $wikiNames = Utils::getAllWikiNames();
+ $dbKey = array_search( $this->vals['wiki'], $wikiNames );
+ if ( $dbKey !== false ) {
+ $this->vals['wiki'] = $dbKey;
+ }
+
+ $curVer = $dbw->selectField( 'oauth_registered_consumer',
+ 'oarc_version',
+ [ 'oarc_name' => $this->vals['name'], 'oarc_user_id' => $centralUserId ],
+ __METHOD__,
+ [ 'ORDER BY' => 'oarc_registration DESC', 'FOR UPDATE' ]
+ );
+ if ( $curVer !== false && version_compare( $curVer, $this->vals['version'], '>=' ) ) {
+ return $this->failure( 'consumer_exists',
+ 'mwoauth-consumer-alreadyexistsversion', $curVer );
+ }
+
+ // Handle owner-only mode
+ if ( $this->vals['ownerOnly'] ) {
+ $this->vals['callbackUrl'] = \SpecialPage::getTitleFor( 'OAuth', 'verified' )
+ ->getLocalURL();
+ $this->vals['callbackIsPrefix'] = '';
+ $stage = Consumer::STAGE_APPROVED;
+ } else {
+ $stage = Consumer::STAGE_PROPOSED;
+ }
+
+ // Handle grant types
+ $grants = [];
+ switch ( $this->vals['granttype'] ) {
+ case 'authonly':
+ $grants = [ 'mwoauth-authonly' ];
+ break;
+ case 'authonlyprivate':
+ $grants = [ 'mwoauth-authonlyprivate' ];
+ break;
+ case 'normal':
+ $grants = array_unique( array_merge(
+ \MWGrants::getHiddenGrants(), // implied grants
+ \FormatJson::decode( $this->vals['grants'], true )
+ ) );
+ break;
+ }
+
+ $now = wfTimestampNow();
+ $cmr = Consumer::newFromArray(
+ [
+ 'id' => null, // auto-increment
+ 'consumerKey' => \MWCryptRand::generateHex( 32 ),
+ 'userId' => $centralUserId,
+ 'email' => $user->getEmail(),
+ 'emailAuthenticated' => $now, // see above
+ 'developerAgreement' => 1,
+ 'secretKey' => \MWCryptRand::generateHex( 32 ),
+ 'registration' => $now,
+ 'stage' => $stage,
+ 'stageTimestamp' => $now,
+ 'grants' => $grants,
+ 'restrictions' => $this->vals['restrictions'],
+ 'deleted' => 0
+ ] + $this->vals
+ );
+ $cmr->save( $dbw );
+
+ if ( $cmr->getOwnerOnly() ) {
+ $this->makeLogEntry(
+ $dbw, $cmr, 'create-owner-only', $user, $this->vals['description']
+ );
+ } else {
+ $this->makeLogEntry( $dbw, $cmr, $action, $user, $this->vals['description'] );
+ $this->notify( $cmr, $user, $action, null );
+ }
+
+ // If it's owner-only, automatically accept it for the user too.
+ $accessToken = null;
+ if ( $cmr->getOwnerOnly() ) {
+ $accessToken = MWOAuthDataStore::newToken();
+ $cmra = ConsumerAcceptance::newFromArray( [
+ 'id' => null,
+ 'wiki' => $cmr->getWiki(),
+ 'userId' => $centralUserId,
+ 'consumerId' => $cmr->getId(),
+ 'accessToken' => $accessToken->key,
+ 'accessSecret' => $accessToken->secret,
+ 'grants' => $cmr->getGrants(),
+ 'accepted' => $now,
+ 'oauth_version' => $cmr->getOAuthVersion()
+ ] );
+ $cmra->save( $dbw );
+ if ( $cmr instanceof ClientEntity ) {
+ // OAuth2 client
+ try {
+ $accessToken = $cmr->getOwnerOnlyAccessToken( $cmra );
+ } catch ( \Exception $ex ) {
+ return $this->failure(
+ 'unable_to_retrieve_access_token',
+ 'mwoauth-oauth2-unable-to-retrieve-access-token',
+ $ex->getMessage()
+ );
+ }
+ }
+ }
+
+ return $this->success( [ 'consumer' => $cmr, 'accessToken' => $accessToken ] );
+ case 'update':
+ if ( !$user->isAllowed( 'mwoauthupdateownconsumer' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ $cmr = Consumer::newFromKey( $dbw, $this->vals['consumerKey'] );
+ if ( !$cmr ) {
+ return $this->failure( 'invalid_consumer_key', 'mwoauth-invalid-consumer-key' );
+ } elseif ( $cmr->getUserId() !== $centralUserId ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif (
+ $cmr->getStage() !== Consumer::STAGE_APPROVED
+ && $cmr->getStage() !== Consumer::STAGE_PROPOSED
+ ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( $cmr->getDeleted() && !$user->isAllowed( 'mwoauthsuppress' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' ); // sanity
+ } elseif ( !$cmr->checkChangeToken( $context, $this->vals['changeToken'] ) ) {
+ return $this->failure( 'change_conflict', 'mwoauth-consumer-conflict' );
+ }
+
+ $cmr->setFields( [
+ 'rsaKey' => $this->vals['rsaKey'],
+ 'restrictions' => $this->vals['restrictions'],
+ 'secretKey' => $this->vals['resetSecret']
+ ? \MWCryptRand::generateHex( 32 )
+ : $cmr->getSecretKey(),
+ ] );
+
+ // Log if something actually changed
+ if ( $cmr->save( $dbw ) ) {
+ $this->makeLogEntry( $dbw, $cmr, $action, $user, $this->vals['reason'] );
+ $this->notify( $cmr, $user, $action, $this->vals['reason'] );
+ }
+
+ $cmra = null;
+ $accessToken = null;
+ if ( $cmr->getOwnerOnly() && $this->vals['resetSecret'] ) {
+ $cmra = $cmr->getCurrentAuthorization( $user, wfWikiID() );
+ $accessToken = MWOAuthDataStore::newToken();
+ $fields = [
+ 'wiki' => $cmr->getWiki(),
+ 'userId' => $centralUserId,
+ 'consumerId' => $cmr->getId(),
+ 'accessSecret' => $accessToken->secret,
+ 'grants' => $cmr->getGrants(),
+ ];
+
+ if ( $cmra ) {
+ $accessToken->key = $cmra->getAccessToken();
+ $cmra->setFields( $fields );
+ } else {
+ $cmra = ConsumerAcceptance::newFromArray( $fields + [
+ 'id' => null,
+ 'accessToken' => $accessToken->key,
+ 'accepted' => wfTimestampNow(),
+ ] );
+ }
+ $cmra->save( $dbw );
+ if ( $cmr instanceof ClientEntity ) {
+ $accessToken = $cmr->getOwnerOnlyAccessToken( $cmra, true );
+ }
+ }
+
+ return $this->success( [ 'consumer' => $cmr, 'accessToken' => $accessToken ] );
+ case 'approve':
+ if ( !$user->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ $cmr = Consumer::newFromKey( $dbw, $this->vals['consumerKey'] );
+ if ( !$cmr ) {
+ return $this->failure( 'invalid_consumer_key', 'mwoauth-invalid-consumer-key' );
+ } elseif ( !in_array( $cmr->getStage(), [
+ Consumer::STAGE_PROPOSED,
+ Consumer::STAGE_EXPIRED,
+ Consumer::STAGE_REJECTED,
+ ] ) ) {
+ return $this->failure( 'not_proposed', 'mwoauth-consumer-not-proposed' );
+ } elseif ( $cmr->getDeleted() && !$user->isAllowed( 'mwoauthsuppress' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( !$cmr->checkChangeToken( $context, $this->vals['changeToken'] ) ) {
+ return $this->failure( 'change_conflict', 'mwoauth-consumer-conflict' );
+ }
+
+ $cmr->setFields( [
+ 'stage' => Consumer::STAGE_APPROVED,
+ 'stageTimestamp' => wfTimestampNow(),
+ 'deleted' => 0 ] );
+
+ // Log if something actually changed
+ if ( $cmr->save( $dbw ) ) {
+ $this->makeLogEntry( $dbw, $cmr, $action, $user, $this->vals['reason'] );
+ $this->notify( $cmr, $user, $action, $this->vals['reason'] );
+ }
+
+ return $this->success( $cmr );
+ case 'reject':
+ if ( !$user->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ $cmr = Consumer::newFromKey( $dbw, $this->vals['consumerKey'] );
+ if ( !$cmr ) {
+ return $this->failure( 'invalid_consumer_key', 'mwoauth-invalid-consumer-key' );
+ } elseif ( $cmr->getStage() !== Consumer::STAGE_PROPOSED ) {
+ return $this->failure( 'not_proposed', 'mwoauth-consumer-not-proposed' );
+ } elseif ( $cmr->getDeleted() && !$user->isAllowed( 'mwoauthsuppress' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( $this->vals['suppress'] && !$user->isAllowed( 'mwoauthsuppress' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( !$cmr->checkChangeToken( $context, $this->vals['changeToken'] ) ) {
+ return $this->failure( 'change_conflict', 'mwoauth-consumer-conflict' );
+ }
+
+ $cmr->setFields( [
+ 'stage' => Consumer::STAGE_REJECTED,
+ 'stageTimestamp' => wfTimestampNow(),
+ 'deleted' => $this->vals['suppress'] ] );
+
+ // Log if something actually changed
+ if ( $cmr->save( $dbw ) ) {
+ $this->makeLogEntry( $dbw, $cmr, $action, $user, $this->vals['reason'] );
+ $this->notify( $cmr, $user, $action, $this->vals['reason'] );
+ }
+
+ return $this->success( $cmr );
+ case 'disable':
+ if ( !$user->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( $this->vals['suppress'] && !$user->isAllowed( 'mwoauthsuppress' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ $cmr = Consumer::newFromKey( $dbw, $this->vals['consumerKey'] );
+ if ( !$cmr ) {
+ return $this->failure( 'invalid_consumer_key', 'mwoauth-invalid-consumer-key' );
+ } elseif ( $cmr->getStage() !== Consumer::STAGE_APPROVED
+ && $cmr->getDeleted() == $this->vals['suppress']
+ ) {
+ return $this->failure( 'not_approved', 'mwoauth-consumer-not-approved' );
+ } elseif ( $cmr->getDeleted() && !$user->isAllowed( 'mwoauthsuppress' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( !$cmr->checkChangeToken( $context, $this->vals['changeToken'] ) ) {
+ return $this->failure( 'change_conflict', 'mwoauth-consumer-conflict' );
+ }
+
+ $cmr->setFields( [
+ 'stage' => Consumer::STAGE_DISABLED,
+ 'stageTimestamp' => wfTimestampNow(),
+ 'deleted' => $this->vals['suppress'] ] );
+
+ // Log if something actually changed
+ if ( $cmr->save( $dbw ) ) {
+ $this->makeLogEntry( $dbw, $cmr, $action, $user, $this->vals['reason'] );
+ $this->notify( $cmr, $user, $action, $this->vals['reason'] );
+ }
+
+ return $this->success( $cmr );
+ case 'reenable':
+ if ( !$user->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ }
+
+ $cmr = Consumer::newFromKey( $dbw, $this->vals['consumerKey'] );
+ if ( !$cmr ) {
+ return $this->failure( 'invalid_consumer_key', 'mwoauth-invalid-consumer-key' );
+ } elseif ( $cmr->getStage() !== Consumer::STAGE_DISABLED ) {
+ return $this->failure( 'not_disabled', 'mwoauth-consumer-not-disabled' );
+ } elseif ( $cmr->getDeleted() && !$user->isAllowed( 'mwoauthsuppress' ) ) {
+ return $this->failure( 'permission_denied', 'badaccess-group0' );
+ } elseif ( !$cmr->checkChangeToken( $context, $this->vals['changeToken'] ) ) {
+ return $this->failure( 'change_conflict', 'mwoauth-consumer-conflict' );
+ }
+
+ $cmr->setFields( [
+ 'stage' => Consumer::STAGE_APPROVED,
+ 'stageTimestamp' => wfTimestampNow(),
+ 'deleted' => 0 ] );
+
+ // Log if something actually changed
+ if ( $cmr->save( $dbw ) ) {
+ $this->makeLogEntry( $dbw, $cmr, $action, $user, $this->vals['reason'] );
+ $this->notify( $cmr, $user, $action, $this->vals['reason'] );
+ }
+
+ return $this->success( $cmr );
+ }
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param int $userId
+ * @return \Title
+ */
+ protected function getLogTitle( DBConnRef $db, $userId ) {
+ $name = Utils::getCentralUserNameFromId( $userId );
+ return \Title::makeTitleSafe( NS_USER, $name );
+ }
+
+ /**
+ * @param DBConnRef $dbw
+ * @param Consumer $cmr
+ * @param string $action
+ * @param \User $performer
+ * @param string $comment
+ */
+ protected function makeLogEntry(
+ $dbw, Consumer $cmr, $action, \User $performer, $comment
+ ) {
+ $logEntry = new \ManualLogEntry( 'mwoauthconsumer', $action );
+ $logEntry->setPerformer( $performer );
+ $target = $this->getLogTitle( $dbw, $cmr->getUserId() );
+ $logEntry->setTarget( $target );
+ $logEntry->setComment( $comment );
+ $logEntry->setParameters( [ '4:consumer' => $cmr->getConsumerKey() ] );
+ $logEntry->setRelations( [
+ 'OAuthConsumer' => [ $cmr->getConsumerKey() ]
+ ] );
+ $logEntry->insert( $dbw );
+
+ LoggerFactory::getInstance( 'OAuth' )->info(
+ '{user} performed action {action} on consumer {consumer}', [
+ 'action' => $action,
+ 'user' => $performer->getName(),
+ 'consumer' => $cmr->getConsumerKey(),
+ 'target' => $target->getText(),
+ 'comment' => $comment,
+ 'clientip' => $this->getContext()->getRequest()->getIP(),
+ ]
+ );
+ }
+
+ /**
+ * @param Consumer $cmr Consumer which was the subject of the action
+ * @param \User $user User who performed the action
+ * @param string $actionType Action type
+ * @param string $comment
+ * @throws \MWException
+ */
+ protected function notify( $cmr, $user, $actionType, $comment ) {
+ if ( !in_array( $actionType, self::$actions, true ) ) {
+ throw new \MWException( "Invalid action type: $actionType" );
+ } elseif ( !ExtensionRegistry::getInstance()->isLoaded( 'Echo' ) ) {
+ return;
+ } elseif ( !Utils::isCentralWiki() ) {
+ # sanity; should never get here on a replica wiki
+ return;
+ }
+
+ \EchoEvent::create( [
+ 'type' => 'oauth-app-' . $actionType,
+ 'agent' => $user,
+ 'extra' => [
+ 'action' => $actionType,
+ 'app-key' => $cmr->getConsumerKey(),
+ 'owner-id' => $cmr->getUserId(),
+ 'comment' => $comment,
+ ],
+ ] );
+ }
+}
diff --git a/OAuth/src/Control/DAOAccessControl.php b/OAuth/src/Control/DAOAccessControl.php
new file mode 100644
index 00000000..d0d8d7e0
--- /dev/null
+++ b/OAuth/src/Control/DAOAccessControl.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+namespace MediaWiki\Extensions\OAuth\Control;
+
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthDAO;
+use Message;
+
+/**
+ * Wrapper of an MWOAuthDAO that handles authorization to view fields
+ */
+class DAOAccessControl extends \ContextSource {
+ /** @var MWOAuthDAO */
+ protected $dao;
+
+ /**
+ * @param MWOAuthDAO $dao
+ * @param \IContextSource $context
+ */
+ final protected function __construct( MWOAuthDAO $dao, \IContextSource $context ) {
+ $this->dao = $dao;
+ $this->setContext( $context );
+ }
+
+ /**
+ * @param MWOAuthDAO|false|null $dao
+ * @param \IContextSource $context
+ * @throws \LogicException
+ * @return static|null|false
+ */
+ final public static function wrap( $dao, \IContextSource $context ) {
+ if ( $dao instanceof MWOAuthDAO ) {
+ return new static( $dao, $context );
+ } elseif ( $dao === null || $dao === false ) {
+ return $dao;
+ } else {
+ throw new \LogicException( "Expected MWOAuthDAO object, null, or false." );
+ }
+ }
+
+ /**
+ * @return MWOAuthDAO
+ */
+ public function getDAO() {
+ return $this->dao;
+ }
+
+ /**
+ * Helper to make return value of get() safe for wikitext
+ *
+ * @param Message|string $value
+ * @return string For use in wikitext
+ * @param-taint $value escapes_escaped
+ */
+ final public function escapeForWikitext( $value ) {
+ if ( $value instanceof Message ) {
+ return wfEscapeWikiText( $value->plain() );
+ } else {
+ return wfEscapeWikiText( $value );
+ }
+ }
+
+ /**
+ * Helper to make return value of get() safe for HTML
+ *
+ * @param Message|string $value
+ * @return string HTML escaped
+ * @param-taint $value escapes_escaped
+ */
+ final public function escapeForHtml( $value ) {
+ if ( $value instanceof Message ) {
+ return $value->parse();
+ } else {
+ return htmlspecialchars( $value );
+ }
+ }
+
+ /**
+ * Get the value of a field, taking into account user permissions.
+ * An appropriate Message will be returned if access is denied.
+ *
+ * @param string $name
+ * @param callback|null $sCallback Optional callback to apply to result on access success
+ * @return mixed Returns a Message on access failure
+ */
+ final public function get( $name, $sCallback = null ) {
+ $msg = $this->dao->userCanAccess( $name, $this->getContext() );
+ if ( $msg !== true ) {
+ return $msg; // should be a Message object
+ } else {
+ $value = $this->dao->get( $name );
+ return $sCallback ? call_user_func( $sCallback, $value ) : $value;
+ }
+ }
+
+ /**
+ * Check whether the user can access the given field(s).
+ * @param string|array $names A field name or a list of names.
+ * @return bool
+ */
+ final public function userCanAccess( $names ) {
+ foreach ( (array)$names as $name ) {
+ if ( !$this->dao->userCanAccess( $name, $this->getContext() ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/OAuth/src/Control/SubmitControl.php b/OAuth/src/Control/SubmitControl.php
new file mode 100644
index 00000000..e80a30d5
--- /dev/null
+++ b/OAuth/src/Control/SubmitControl.php
@@ -0,0 +1,228 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Control;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Handle the logic of submitting a client request
+ */
+abstract class SubmitControl extends \ContextSource {
+ /** @var array (field name => value) */
+ protected $vals;
+
+ /**
+ * @param \IContextSource $context
+ * @param array $params
+ */
+ public function __construct( \IContextSource $context, array $params ) {
+ $this->setContext( $context );
+ $this->vals = $params;
+ }
+
+ /**
+ * @param array $params
+ */
+ public function setInputParameters( array $params ) {
+ $this->vals = $params;
+ }
+
+ /**
+ * Attempt to validate and submit this data
+ *
+ * This will check basic permissions, validate the action and parameters
+ * and route the submission handling to the internal subclass function.
+ *
+ * @throws \MWException
+ * @return \Status
+ */
+ public function submit() {
+ $status = $this->checkBasePermissions();
+ if ( !$status->isOK() ) {
+ return $status;
+ }
+
+ $action = $this->vals['action'];
+ $required = $this->getRequiredFields();
+ if ( !isset( $required[$action] ) ) {
+ // @TODO: check for field-specific message first
+ return $this->failure( 'invalid_field_action', 'mwoauth-invalid-field', 'action' );
+ }
+
+ $status = $this->validateFields( $required[$action] );
+ if ( !$status->isOK() ) {
+ return $status;
+ }
+
+ $status = $this->processAction( $action );
+ if ( $status instanceof \Status ) {
+ return $status;
+ } else {
+ throw new \MWException( "Submission action '$action' not handled." );
+ }
+ }
+
+ /**
+ * Given an HTMLForm descriptor array, register the field validation callbacks
+ *
+ * @param array $descriptors
+ * @return array
+ */
+ public function registerValidators( array $descriptors ) {
+ foreach ( $descriptors as $field => &$description ) {
+ if ( array_key_exists( 'validation-callback', $description ) ) {
+ continue; // already set to something
+ }
+ $control = $this;
+ $description['validation-callback'] =
+ function ( $value, $allValues, $form ) use ( $control, $field ) {
+ return $control->validateFieldInternal( $field, $value, $allValues, $form );
+ };
+ }
+ return $descriptors;
+ }
+
+ /**
+ * This method should not be called outside MWOAuthSubmitControl
+ *
+ * @param string $field
+ * @param string $value
+ * @param array $allValues
+ * @param \HTMLForm $form
+ * @throws \MWException
+ * @return bool|string
+ */
+ public function validateFieldInternal( $field, $value, $allValues, $form ) {
+ if ( !isset( $allValues['action'] ) && isset( $this->vals['action'] ) ) {
+ // The action may be derived, especially for multi-button forms.
+ // Such an HTMLForm will not have an action key set in $allValues.
+ $allValues['action'] = $this->vals['action']; // injected
+ }
+ if ( !isset( $allValues['action'] ) ) {
+ throw new \MWException( "No form action defined; cannot validate fields." );
+ }
+ $validators = $this->getRequiredFields();
+ if ( !isset( $validators[$allValues['action']][$field] ) ) {
+ return true; // nothing to check
+ }
+ $validator = $validators[$allValues['action']][$field];
+ $isValid = is_string( $validator ) // regex
+ ? preg_match( $validator, $value )
+ : $validator( $value, $allValues );
+ if ( !$isValid ) {
+ $errorMessage = $this->msg( 'mwoauth-invalid-field-' . $field );
+ if ( !$errorMessage->isDisabled() ) {
+ return $errorMessage->text();
+ }
+
+ $generic = '';
+ if ( $form->getField( $field )->canDisplayErrors() ) {
+ // error can be attached to the field so no need to mention the field name
+ $generic = '-generic';
+ }
+
+ $problem = 'invalid';
+ if ( $value === '' && !$generic ) {
+ $problem = 'missing';
+ }
+
+ // messages: mwoauth-missing-field, mwoauth-invalid-field, mwoauth-invalid-field-generic
+ return $this->msg( "mwoauth-$problem-field$generic", $field )->text();
+ }
+ return true;
+ }
+
+ /**
+ * Get the field names and their validation regexes or functions
+ * (which return a boolean) for each action that this controller handles.
+ * When functions are used, they take (field value, field/value map) as params.
+ *
+ * @return array (action => (field name => validation regex or function))
+ */
+ abstract protected function getRequiredFields();
+
+ /**
+ * Check action-independent permissions against the user for this submission
+ *
+ * @return \Status
+ */
+ abstract protected function checkBasePermissions();
+
+ /**
+ * Check that the action is valid and that the required fields are valid
+ *
+ * @param array $required (field => regex or callback)
+ * @return \Status
+ */
+ protected function validateFields( array $required ) {
+ foreach ( $required as $field => $validator ) {
+ if ( !isset( $this->vals[$field] ) ) {
+ // @TODO: check for field-specific message first
+ return $this->failure( "missing_field_$field", 'mwoauth-missing-field', $field );
+ } elseif ( !is_scalar( $this->vals[$field] ) && $field !== 'restrictions' ) {
+ // @TODO: check for field-specific message first
+ return $this->failure( "invalid_field_$field", 'mwoauth-invalid-field', $field );
+ }
+ if ( is_string( $this->vals[$field] ) ) {
+ $this->vals[$field] = trim( $this->vals[$field] ); // trim all input
+ }
+ $valid = is_string( $validator ) // regex
+ ? preg_match( $validator, $this->vals[$field] )
+ : $validator( $this->vals[$field], $this->vals );
+ if ( !$valid ) {
+ // @TODO: check for field-specific message first
+ return $this->failure( "invalid_field_$field", 'mwoauth-invalid-field', $field );
+ }
+ }
+ return $this->success();
+ }
+
+ /**
+ * Attempt to validate and submit this data for the given action
+ *
+ * @param string $action
+ * @return \Status
+ */
+ abstract protected function processAction( $action );
+
+ /**
+ * @param string $error API error key
+ * @param string $msg Message key
+ * @param mixed ...$params Additional arguments used as message parameters
+ * @return \Status
+ */
+ protected function failure( $error, $msg, ...$params ) {
+ // Use the same logic as wfMessage
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ $status = \Status::newFatal( $this->msg( $msg, $params ) );
+ $status->value = [ 'error' => $error, 'result' => null ];
+ return $status;
+ }
+
+ /**
+ * @param mixed|null $value
+ * @return \Status
+ */
+ protected function success( $value = null ) {
+ return \Status::newGood( [ 'error' => null, 'result' => $value ] );
+ }
+}
diff --git a/OAuth/src/Entity/AccessTokenEntity.php b/OAuth/src/Entity/AccessTokenEntity.php
new file mode 100644
index 00000000..f7ab4f3a
--- /dev/null
+++ b/OAuth/src/Entity/AccessTokenEntity.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Entity;
+
+use InvalidArgumentException;
+use League\OAuth2\Server\CryptKey;
+use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Entities\Traits\AccessTokenTrait;
+use League\OAuth2\Server\Entities\Traits\EntityTrait;
+use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\MediaWikiServices;
+use Throwable;
+use User;
+
+class AccessTokenEntity implements AccessTokenEntityInterface {
+ use AccessTokenTrait;
+ use EntityTrait;
+ use TokenEntityTrait;
+
+ /**
+ * @var ClientEntity
+ */
+ protected $client;
+
+ /**
+ * User approval of the client
+ *
+ * @var ConsumerAcceptance|bool
+ */
+ private $approval = false;
+
+ /**
+ * @param ClientEntity $clientEntity
+ * @param ScopeEntityInterface[] $scopes
+ * @param string|null $userIdentifier
+ */
+ public function __construct(
+ ClientEntity $clientEntity, array $scopes, $userIdentifier = null
+ ) {
+ $this->approval = $this->setApprovalFromClientScopesUser(
+ $clientEntity, $scopes, $userIdentifier
+ );
+
+ $this->setClient( $clientEntity );
+ if ( $clientEntity->getOwnerOnly() ) {
+ if ( $userIdentifier !== null && $userIdentifier !== $clientEntity->getUserId() ) {
+ throw new InvalidArgumentException(
+ '$userIdentifier must be null, or match the client owner user id,' .
+ ' for owner-only clients, ' . $userIdentifier . ' given'
+ );
+ }
+ foreach ( $clientEntity->getScopes() as $scope ) {
+ $this->addScope( $scope );
+ }
+ $this->setUserIdentifier( $clientEntity->getUserId() );
+ } else {
+ foreach ( $scopes as $scope ) {
+ if ( !in_array( $scope->getIdentifier(), $clientEntity->getGrants() ) ) {
+ continue;
+ }
+ $this->addScope( $scope );
+ }
+ $this->setUserIdentifier( $userIdentifier );
+ }
+
+ $this->confirmClientUsable();
+ }
+
+ /**
+ * Get the approval that allows this AT to be created
+ *
+ * @return ConsumerAcceptance
+ */
+ public function getApproval() {
+ return $this->approval;
+ }
+
+ /**
+ * Set configured private key
+ */
+ public function setPrivateKeyFromConfig() {
+ $oauthConfig = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'mwoauth' );
+ // Private key to sign the token
+ $privateKey = new CryptKey( $oauthConfig->get( 'OAuth2PrivateKey' ) );
+ $this->setPrivateKey( $privateKey );
+ }
+
+ /**
+ * Get the client that the token was issued to.
+ *
+ * @return ClientEntity
+ */
+ public function getClient() {
+ return $this->client;
+ }
+
+ /**
+ * @param ClientEntity $clientEntity
+ * @param array $scopes
+ * @param null $userIdentifier
+ * @return ConsumerAcceptance|bool
+ */
+ private function setApprovalFromClientScopesUser(
+ ClientEntity $clientEntity, array $scopes, $userIdentifier = null
+ ) {
+ if ( $clientEntity->getOwnerOnly() && $userIdentifier === null ) {
+ $userIdentifier = $clientEntity->getUserId();
+ $scopes = $clientEntity->getScopes();
+ }
+ try {
+ $user = Utils::getLocalUserFromCentralId( $userIdentifier );
+ $approval = $clientEntity->getCurrentAuthorization( $user, wfWikiID() );
+ } catch ( Throwable $ex ) {
+ return false;
+ }
+ if ( !$approval ) {
+ return $approval;
+ }
+
+ $approvedScopes = $approval->getGrants();
+ $notApproved = array_filter(
+ $scopes,
+ function ( ScopeEntityInterface $scope ) use ( $approvedScopes ) {
+ return !in_array( $scope->getIdentifier(), $approvedScopes, true );
+ }
+ );
+
+ return empty( $notApproved ) ? $approval : false;
+ }
+
+ private function confirmClientUsable() {
+ $userId = $this->getUserIdentifier() ?? 0;
+ $user = Utils::getLocalUserFromCentralId( $userId );
+ if ( !$user ) {
+ $user = User::newFromId( 0 );
+ }
+
+ if ( !$this->getClient()->isUsableBy( $user ) ) {
+ throw OAuthServerException::accessDenied(
+ 'Client ' . $this->getClient()->getIdentifier() .
+ ' is not usable by user with ID ' . $user->getId()
+ );
+ }
+ }
+
+}
diff --git a/OAuth/src/Entity/AuthCodeEntity.php b/OAuth/src/Entity/AuthCodeEntity.php
new file mode 100644
index 00000000..41d4266b
--- /dev/null
+++ b/OAuth/src/Entity/AuthCodeEntity.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Entity;
+
+use JsonSerializable;
+use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
+use League\OAuth2\Server\Entities\Traits\AuthCodeTrait;
+use League\OAuth2\Server\Entities\Traits\EntityTrait;
+use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
+
+class AuthCodeEntity implements AuthCodeEntityInterface, JsonSerializable {
+ use TokenEntityTrait;
+ use EntityTrait;
+ use AuthCodeTrait;
+
+ public function jsonSerialize() {
+ return [
+ 'user' => $this->getUserIdentifier(),
+ 'client' => $this->getClient()->getIdentifier(),
+ 'identifier' => $this->getIdentifier(),
+ 'redirectUri' => $this->getRedirectUri(),
+ 'scopes' => $this->getScopes(),
+ 'expires' => $this->getExpiryDateTime()->getTimestamp()
+ ];
+ }
+}
diff --git a/OAuth/src/Entity/ClientEntity.php b/OAuth/src/Entity/ClientEntity.php
new file mode 100644
index 00000000..0def1470
--- /dev/null
+++ b/OAuth/src/Entity/ClientEntity.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Entity;
+
+use Exception;
+use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Repository\AccessTokenRepository;
+use MWException;
+use User;
+
+class ClientEntity extends Consumer implements ClientEntityInterface {
+
+ /**
+ * Returns the registered redirect URI (as a string).
+ *
+ * Alternatively return an indexed array of redirect URIs.
+ *
+ * @return string|string[]
+ */
+ public function getRedirectUri() {
+ return $this->getCallbackUrl();
+ }
+
+ /**
+ * Returns true if the client is confidential.
+ *
+ * @return bool
+ */
+ public function isConfidential() {
+ return $this->oauth2IsConfidential;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getIdentifier() {
+ return $this->getConsumerKey();
+ }
+
+ /**
+ * @param mixed $identifier
+ */
+ public function setIdentifier( $identifier ) {
+ $this->consumerKey = $identifier;
+ }
+
+ /**
+ * Get the grant types this client is allowed to use
+ *
+ * @return array
+ */
+ public function getAllowedGrants() {
+ return $this->oauth2GrantTypes;
+ }
+
+ /**
+ * Convenience function, same as getGrants()
+ * it just returns array of ScopeEntity-es instead of strings
+ *
+ * @return ScopeEntityInterface[]
+ */
+ public function getScopes() {
+ $scopeEntities = [];
+ foreach ( $this->getGrants() as $grant ) {
+ $scopeEntities[] = new ScopeEntity( $grant );
+ }
+
+ return $scopeEntities;
+ }
+
+ /**
+ * @return bool|User
+ * @throws MWException
+ */
+ public function getUser() {
+ return Utils::getLocalUserFromCentralId( $this->getUserId() );
+ }
+
+ /**
+ * @param null|string $secret
+ * @param null|string $grantType
+ * @return bool
+ */
+ public function validate( $secret, $grantType ) {
+ if ( !$this->isSecretValid( $secret ) ) {
+ return false;
+ }
+
+ if ( !$this->isGrantAllowed( $grantType ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return int
+ */
+ public function getOAuthVersion() {
+ return static::OAUTH_VERSION_2;
+ }
+
+ private function isSecretValid( $secret ) {
+ return hash_equals( $secret, Utils::hmacDBSecret( $this->secretKey ) );
+ }
+
+ /**
+ * @param string $grantType
+ * @return bool
+ */
+ public function isGrantAllowed( $grantType ) {
+ return in_array( $grantType, $this->getAllowedGrants() );
+ }
+
+ /**
+ * @param User $mwUser
+ * @param bool $update
+ * @param array $grants
+ * @param null $requestTokenKey
+ * @return bool
+ * @throws MWException
+ * @throws MWOAuthException
+ */
+ public function authorize( User $mwUser, $update, $grants, $requestTokenKey = null ) {
+ $this->conductAuthorizationChecks( $mwUser );
+
+ $grants = $this->getVerifiedScopes( $grants );
+ $this->saveAuthorization( $mwUser, $update, $grants );
+
+ return true;
+ }
+
+ /**
+ * Get the access token to be used with a single user
+ * Should never be called outside of client registration/manage code
+ *
+ * @param ConsumerAcceptance $approval
+ * @param bool $revokeExisting - Delete all existing tokens
+ *
+ * @return AccessTokenEntityInterface
+ * @throws MWOAuthException
+ * @throws OAuthServerException
+ * @throws Exception
+ */
+ public function getOwnerOnlyAccessToken(
+ ConsumerAcceptance $approval, $revokeExisting = false
+ ) {
+ if (
+ count( $this->getAllowedGrants() ) !== 1 ||
+ $this->getAllowedGrants()[0] !== 'client_credentials'
+ ) {
+ // sanity - make sure client is allowed *only* client_credentials grant,
+ // so that this AT cannot be used in other grant type requests
+ throw new MWOAuthException( 'mwoauth-oauth2-error-owner-only-invalid-grant' );
+ }
+ $accessToken = null;
+ $accessTokenRepo = new AccessTokenRepository();
+ if ( $revokeExisting ) {
+ $accessTokenRepo->deleteForApprovalId( $approval->getId() );
+ }
+ /** @var AccessTokenEntity $accessToken */
+ $accessToken = $accessTokenRepo->getNewToken( $this, $this->getScopes(), $approval->getUserId() );
+ '@phan-var AccessTokenEntity $accessToken';
+ $accessToken->setExpiryDateTime( ( new \DateTimeImmutable() )->add(
+ new \DateInterval( 'P292277000000Y' )
+ ) );
+ $accessToken->setPrivateKeyFromConfig();
+ $accessToken->setIdentifier( bin2hex( random_bytes( 40 ) ) );
+
+ $accessTokenRepo->persistNewAccessToken( $accessToken );
+
+ return $accessToken;
+ }
+
+ /**
+ * Filter out scopes that application cannot use
+ *
+ * @param array $requested
+ * @return array
+ */
+ private function getVerifiedScopes( $requested ) {
+ return array_intersect( $requested, $this->getGrants() );
+ }
+}
diff --git a/OAuth/src/Entity/RefreshTokenEntity.php b/OAuth/src/Entity/RefreshTokenEntity.php
new file mode 100644
index 00000000..701b2aa7
--- /dev/null
+++ b/OAuth/src/Entity/RefreshTokenEntity.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Entity;
+
+use JsonSerializable;
+use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
+use League\OAuth2\Server\Entities\Traits\EntityTrait;
+use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
+
+class RefreshTokenEntity implements RefreshTokenEntityInterface, JsonSerializable {
+ use RefreshTokenTrait;
+ use EntityTrait;
+
+ public function jsonSerialize() {
+ return [
+ 'identifier' => $this->getIdentifier(),
+ 'accessToken' => $this->getAccessToken()->getIdentifier(),
+ 'expires' => $this->getExpiryDateTime()->getTimestamp()
+ ];
+ }
+}
diff --git a/OAuth/src/Entity/ScopeEntity.php b/OAuth/src/Entity/ScopeEntity.php
new file mode 100644
index 00000000..8b3843d6
--- /dev/null
+++ b/OAuth/src/Entity/ScopeEntity.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Entity;
+
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Entities\Traits\EntityTrait;
+
+class ScopeEntity implements ScopeEntityInterface {
+ use EntityTrait;
+
+ /**
+ * Create generic scope entity
+ *
+ * @param string $identifier
+ */
+ public function __construct( $identifier ) {
+ $this->identifier = $identifier;
+ }
+
+ /**
+ * @return string
+ */
+ public function jsonSerialize() {
+ return $this->getIdentifier();
+ }
+}
diff --git a/OAuth/src/Entity/UserEntity.php b/OAuth/src/Entity/UserEntity.php
new file mode 100644
index 00000000..73c803b0
--- /dev/null
+++ b/OAuth/src/Entity/UserEntity.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Entity;
+
+use League\OAuth2\Server\Entities\UserEntityInterface;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MWException;
+use User;
+
+class UserEntity implements UserEntityInterface {
+
+ /**
+ * @var int
+ */
+ private $identifier = 0;
+
+ /**
+ * @param User $user
+ * @return UserEntity|null
+ */
+ public static function newFromMWUser( User $user ) {
+ try {
+ $userId = Utils::getCentralIdFromLocalUser( $user );
+ if ( !$userId ) {
+ return null;
+ }
+ return new static( $userId );
+ } catch ( MWException $ex ) {
+ return null;
+ }
+ }
+
+ /**
+ * @param string $identifier
+ */
+ public function __construct( $identifier ) {
+ $this->identifier = $identifier;
+ }
+
+ /**
+ * Return the user's identifier.
+ *
+ * @return mixed
+ */
+ public function getIdentifier() {
+ return $this->identifier;
+ }
+
+ /**
+ * @return bool|User
+ */
+ public function getMWUser() {
+ return Utils::getLocalUserFromCentralId( $this->identifier );
+ }
+}
diff --git a/OAuth/src/Exception/ClientApprovalDenyException.php b/OAuth/src/Exception/ClientApprovalDenyException.php
new file mode 100644
index 00000000..ad32b10d
--- /dev/null
+++ b/OAuth/src/Exception/ClientApprovalDenyException.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Exception;
+
+use League\OAuth2\Server\Exception\OAuthServerException;
+
+class ClientApprovalDenyException extends OAuthServerException {
+
+ public function __construct( $redirectUri ) {
+ parent::__construct(
+ wfMessage( 'mwoauth-oauth2-error-user-approval-deny' )->plain(),
+ 401,
+ 'unauthorized_client',
+ 400,
+ null,
+ $redirectUri
+ );
+ }
+}
diff --git a/OAuth/src/Frontend/EchoOAuthStageChangePresentationModel.php b/OAuth/src/Frontend/EchoOAuthStageChangePresentationModel.php
new file mode 100644
index 00000000..f27c039b
--- /dev/null
+++ b/OAuth/src/Frontend/EchoOAuthStageChangePresentationModel.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend;
+
+use EchoAttributeManager;
+use EchoEventPresentationModel;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MWException;
+use SpecialPage;
+use User;
+
+class EchoOAuthStageChangePresentationModel extends EchoEventPresentationModel {
+ /** @var User[] OAuth admins who should be notified about additiions to the review queue */
+ protected static $oauthAdmins;
+
+ /** @var Consumer|false */
+ protected $consumer;
+
+ /** @var User|false The owner of the OAuth consumer */
+ protected $owner;
+
+ /**
+ * Helper function for $wgEchoNotifications
+ * @param string $action One of the actions from MWOAuthConsumerSubmitControl::$actions
+ * @return array
+ */
+ public static function getDefinition( $action ) {
+ if ( $action === 'propose' ) {
+ // notify admins
+ $category = 'oauth-admin';
+ } else {
+ // notify owner
+ $category = 'oauth-owner';
+ }
+
+ return [
+ EchoAttributeManager::ATTR_LOCATORS => [ Utils::class . '::locateUsersToNotify' ],
+ 'category' => $category,
+ 'presentation-model' => self::class,
+ 'icon' => 'oauth',
+ ];
+ }
+
+ public function getHeaderMessage() {
+ $action = $this->event->getExtraParam( 'action' );
+ return $this->msg( "notification-oauth-app-$action-title",
+ $this->event->getAgent(), $this->getConsumerName(), $this->getOwner() );
+ }
+
+ public function getSubjectMessage() {
+ $action = $this->event->getExtraParam( 'action' );
+ return $this->msg( "notification-oauth-app-$action-subject",
+ $this->event->getAgent(), $this->getConsumerName(), $this->getOwner() );
+ }
+
+ public function getBodyMessage() {
+ $comment = $this->event->getExtraParam( 'comment' );
+ return $comment ? $this->msg( 'notification-oauth-app-body', $comment ) : false;
+ }
+
+ public function getIconType() {
+ return 'oauth';
+ }
+
+ public function getPrimaryLink() {
+ $consumerKey = $this->event->getExtraParam( 'app-key' );
+ $action = $this->event->getExtraParam( 'action' );
+
+ if ( $action === 'propose' ) {
+ // show management interface
+ $page = SpecialPage::getSafeTitleFor( 'OAuthManageConsumers', $consumerKey );
+ } else {
+ // show public view
+ $page = SpecialPage::getSafeTitleFor( 'OAuthListConsumers', "view/$consumerKey" );
+ }
+ if ( $page === null ) {
+ throw new MWException( "Invalid app ID: $consumerKey" );
+ }
+
+ return [
+ 'url' => $page->getLocalURL(),
+ 'label' => $this->msg( "notification-oauth-app-$action-primary-link" )->text(),
+ ];
+ }
+
+ public function getSecondaryLinks() {
+ return [ $this->getAgentLink() ];
+ }
+
+ /**
+ * @return Consumer|false
+ */
+ protected function getConsumer() {
+ if ( $this->consumer === null ) {
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $this->consumer =
+ Consumer::newFromKey( $dbr, $this->event->getExtraParam( 'app-key' ) );
+ }
+ return $this->consumer;
+ }
+
+ /**
+ * @return User|false
+ */
+ protected function getOwner() {
+ if ( $this->owner === null ) {
+ $this->owner = Utils::getLocalUserFromCentralId(
+ $this->event->getExtraParam( 'owner-id' ) );
+ }
+ return $this->owner;
+ }
+
+ /**
+ * @return string
+ */
+ protected function getConsumerName() {
+ $consumer = $this->getConsumer();
+ return $consumer ? $consumer->getName() : false;
+ }
+}
diff --git a/OAuth/src/Frontend/OAuthLogFormatter.php b/OAuth/src/Frontend/OAuthLogFormatter.php
new file mode 100644
index 00000000..a078954c
--- /dev/null
+++ b/OAuth/src/Frontend/OAuthLogFormatter.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend;
+
+use LogEntry;
+use LogFormatter;
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
+use Message;
+use Title;
+
+/**
+ * Formatter for OAuth log events
+ */
+class OAuthLogFormatter extends LogFormatter {
+ /** @var LinkRenderer */
+ protected $linkRenderer;
+
+ protected function __construct( LogEntry $entry ) {
+ parent::__construct( $entry );
+ $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+ }
+
+ protected function getMessageParameters() {
+ $params = parent::getMessageParameters();
+ if ( isset( $params[3] ) ) { // sanity
+ $params[3] = $this->getConsumerLink( $params[3] );
+ }
+ return $params;
+ }
+
+ protected function getConsumerLink( $consumerKey ) {
+ $title = Title::newFromText( 'Special:OAuthListConsumers/view/' . $consumerKey );
+ if ( $this->plaintext ) {
+ return '[[' . $title->getPrefixedText() . '|' . $consumerKey . ']]';
+ } else {
+ return Message::rawParam( $this->linkRenderer->makeLink( $title, $consumerKey ) );
+ }
+ }
+}
diff --git a/OAuth/src/Frontend/Pagers/ListConsumersPager.php b/OAuth/src/Frontend/Pagers/ListConsumersPager.php
new file mode 100644
index 00000000..1e04844c
--- /dev/null
+++ b/OAuth/src/Frontend/Pagers/ListConsumersPager.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\Pagers;
+
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Frontend\SpecialPages\SpecialMWOAuthListConsumers;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Query to list out consumers
+ *
+ * @TODO: use UserCache
+ */
+class ListConsumersPager extends \AlphabeticPager {
+ /** @var SpecialMWOAuthListConsumers */
+ public $mForm;
+
+ /** @var array */
+ public $mConds;
+
+ public function __construct( $form, $conds, $name, $centralUserID, $stage ) {
+ $this->mForm = $form;
+ $this->mConds = $conds;
+
+ $this->mIndexField = null;
+ if ( $name !== '' ) {
+ $this->mConds['oarc_name'] = $name;
+ $this->mIndexField = 'oarc_id';
+ }
+ if ( $centralUserID !== null ) {
+ $this->mConds['oarc_user_id'] = $centralUserID;
+ $this->mIndexField = 'oarc_id';
+ }
+ if ( $stage >= 0 ) {
+ $this->mConds['oarc_stage'] = $stage;
+ if ( !$this->mIndexField ) {
+ $this->mIndexField = 'oarc_stage_timestamp';
+ }
+ }
+ if ( !$this->mIndexField ) {
+ $this->mIndexField = 'oarc_id';
+ }
+
+ if ( !$this->getUser()->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ $this->mConds['oarc_deleted'] = 0;
+ }
+
+ $this->mDb = Utils::getCentralDB( DB_REPLICA );
+ parent::__construct();
+
+ # Treat 20 as the default limit, since each entry takes up 5 rows.
+ $urlLimit = $this->mRequest->getInt( 'limit' );
+ $this->mLimit = $urlLimit ?: 20;
+ }
+
+ /**
+ * @return \Title
+ */
+ public function getTitle() {
+ return $this->mForm->getFullTitle();
+ }
+
+ /**
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( $row ) {
+ return $this->mForm->formatRow( $this->mDb, $row );
+ }
+
+ /**
+ * @return string
+ */
+ public function getStartBody() {
+ if ( $this->getNumRows() ) {
+ return '<ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getEndBody() {
+ if ( $this->getNumRows() ) {
+ return '</ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getQueryInfo() {
+ return [
+ 'tables' => [ 'oauth_registered_consumer' ],
+ 'fields' => [ '*' ],
+ 'conds' => $this->mConds
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public function getIndexField() {
+ return $this->mIndexField;
+ }
+}
diff --git a/OAuth/src/Frontend/Pagers/ListMyConsumersPager.php b/OAuth/src/Frontend/Pagers/ListMyConsumersPager.php
new file mode 100644
index 00000000..47d9bf1c
--- /dev/null
+++ b/OAuth/src/Frontend/Pagers/ListMyConsumersPager.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\Pagers;
+
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Frontend\SpecialPages\SpecialMWOAuthConsumerRegistration;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Query to list out consumers
+ *
+ * @TODO: use UserCache
+ */
+class ListMyConsumersPager extends \ReverseChronologicalPager {
+ /** @var SpecialMWOAuthConsumerRegistration */
+ public $mForm;
+
+ /** @var array */
+ public $mConds;
+
+ public function __construct( $form, $conds, $centralUserId ) {
+ $this->mForm = $form;
+ $this->mConds = $conds;
+ $this->mConds['oarc_user_id'] = $centralUserId;
+ if ( !$this->getUser()->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ $this->mConds['oarc_deleted'] = 0;
+ }
+
+ $this->mDb = Utils::getCentralDB( DB_REPLICA );
+ parent::__construct();
+
+ # Treat 20 as the default limit, since each entry takes up 5 rows.
+ $urlLimit = $this->mRequest->getInt( 'limit' );
+ $this->mLimit = $urlLimit ?: 20;
+ }
+
+ /**
+ * @return \Title
+ */
+ public function getTitle() {
+ return $this->mForm->getFullTitle();
+ }
+
+ /**
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( $row ) {
+ return $this->mForm->formatRow( $this->mDb, $row );
+ }
+
+ /**
+ * @return string
+ */
+ public function getStartBody() {
+ if ( $this->getNumRows() ) {
+ return '<ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getEndBody() {
+ if ( $this->getNumRows() ) {
+ return '</ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getQueryInfo() {
+ return [
+ 'tables' => [ 'oauth_registered_consumer' ],
+ 'fields' => [ '*' ],
+ 'conds' => $this->mConds
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public function getIndexField() {
+ return 'oarc_stage_timestamp';
+ }
+}
diff --git a/OAuth/src/Frontend/Pagers/ManageConsumersPager.php b/OAuth/src/Frontend/Pagers/ManageConsumersPager.php
new file mode 100644
index 00000000..ab181cfe
--- /dev/null
+++ b/OAuth/src/Frontend/Pagers/ManageConsumersPager.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\Pagers;
+
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Frontend\SpecialPages\SpecialMWOAuthManageConsumers;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Query to list out consumers
+ *
+ * @TODO: use UserCache
+ */
+class ManageConsumersPager extends \ReverseChronologicalPager {
+ /** @var SpecialMWOAuthManageConsumers */
+ public $mForm;
+
+ /** @var array */
+ public $mConds;
+
+ public function __construct( $form, $conds, $stage ) {
+ $this->mForm = $form;
+ $this->mConds = $conds;
+ $this->mConds['oarc_stage'] = $stage;
+ if ( !$this->getUser()->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ $this->mConds['oarc_deleted'] = 0;
+ }
+
+ $this->mDb = Utils::getCentralDB( DB_REPLICA );
+ parent::__construct();
+
+ # Treat 20 as the default limit, since each entry takes up 5 rows.
+ $urlLimit = $this->mRequest->getInt( 'limit' );
+ $this->mLimit = $urlLimit ?: 20;
+ }
+
+ /**
+ * @return \Title
+ */
+ public function getTitle() {
+ return $this->mForm->getFullTitle();
+ }
+
+ /**
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( $row ) {
+ return $this->mForm->formatRow( $this->mDb, $row );
+ }
+
+ /**
+ * @return string
+ */
+ public function getStartBody() {
+ if ( $this->getNumRows() ) {
+ return '<ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getEndBody() {
+ if ( $this->getNumRows() ) {
+ return '</ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getQueryInfo() {
+ return [
+ 'tables' => [ 'oauth_registered_consumer' ],
+ 'fields' => [ '*' ],
+ 'conds' => $this->mConds
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public function getIndexField() {
+ return 'oarc_stage_timestamp';
+ }
+}
diff --git a/OAuth/src/Frontend/Pagers/ManageMyGrantsPager.php b/OAuth/src/Frontend/Pagers/ManageMyGrantsPager.php
new file mode 100644
index 00000000..fbd6c425
--- /dev/null
+++ b/OAuth/src/Frontend/Pagers/ManageMyGrantsPager.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\Pagers;
+
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Frontend\SpecialPages\SpecialMWOAuthManageMyGrants;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Query to list out consumers that have an access token for this user
+ *
+ * @TODO: use UserCache
+ */
+class ManageMyGrantsPager extends \ReverseChronologicalPager {
+ public $mForm, $mConds;
+
+ /**
+ * @param SpecialMWOAuthManageMyGrants $form
+ * @param array $conds
+ * @param int $centralUserId
+ */
+ public function __construct( $form, $conds, $centralUserId ) {
+ $this->mForm = $form;
+ $this->mConds = $conds;
+ $this->mConds[] = 'oaac_consumer_id = oarc_id';
+ $this->mConds['oaac_user_id'] = $centralUserId;
+ if ( !$this->getUser()->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ $this->mConds['oarc_deleted'] = 0;
+ }
+
+ $this->mDb = Utils::getCentralDB( DB_REPLICA );
+ parent::__construct();
+
+ # Treat 20 as the default limit, since each entry takes up 5 rows.
+ $urlLimit = $this->mRequest->getInt( 'limit' );
+ $this->mLimit = $urlLimit ?: 20;
+ }
+
+ /**
+ * @return \Title
+ */
+ public function getTitle() {
+ return $this->mForm->getFullTitle();
+ }
+
+ /**
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( $row ) {
+ return $this->mForm->formatRow( $this->mDb, $row );
+ }
+
+ /**
+ * @return string
+ */
+ public function getStartBody() {
+ if ( $this->getNumRows() ) {
+ return '<ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getEndBody() {
+ if ( $this->getNumRows() ) {
+ return '</ul>';
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getQueryInfo() {
+ return [
+ 'tables' => [ 'oauth_accepted_consumer', 'oauth_registered_consumer' ],
+ 'fields' => [ '*' ],
+ 'conds' => $this->mConds
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public function getIndexField() {
+ return 'oaac_consumer_id';
+ }
+}
diff --git a/OAuth/src/Frontend/SpecialPages/SpecialMWOAuth.php b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuth.php
new file mode 100644
index 00000000..9a46a50d
--- /dev/null
+++ b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuth.php
@@ -0,0 +1,743 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\SpecialPages;
+
+/**
+ * (c) Chris Steipp, Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use Firebase\JWT\JWT;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthRequest;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthToken;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAcceptanceSubmitControl;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAccessControl;
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+use MediaWiki\Extensions\OAuth\Lib\OAuthUtil;
+use MediaWiki\Extensions\OAuth\UserStatementProvider;
+use MediaWiki\Logger\LoggerFactory;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Page that handles OAuth consumer authorization and token exchange
+ */
+class SpecialMWOAuth extends \UnlistedSpecialPage {
+ /** @var LoggerInterface */
+ protected $logger;
+
+ /** @var int Defaults to OAuth1 */
+ protected $oauthVersion = Consumer::OAUTH_VERSION_1;
+
+ public function __construct() {
+ parent::__construct( 'OAuth' );
+ $this->logger = LoggerFactory::getInstance( 'OAuth' );
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ public function getLocalName() {
+ // Force the canonical name when OAuth headers are present,
+ // otherwise SpecialPageFactory redirects and breaks the signature.
+ if ( Utils::hasOAuthHeaders( $this->getRequest() ) ) {
+ return $this->getName();
+ }
+ return parent::getLocalName();
+ }
+
+ public function execute( $subpage ) {
+ global $wgMWOAuthSecureTokenTransfer, $wgMWOAuthReadOnly, $wgBlockDisablesLogin;
+
+ $this->setHeaders();
+
+ $user = $this->getUser();
+ $request = $this->getRequest();
+ $format = $request->getVal( 'format', 'raw' );
+
+ try {
+ if ( $wgMWOAuthReadOnly &&
+ !in_array( $subpage, [ 'verified', 'grants', 'identify' ] )
+ ) {
+ throw new MWOAuthException( 'mwoauth-db-readonly' );
+ }
+
+ $this->determineOAuthVersion( $request );
+ switch ( $subpage ) {
+ case 'initiate':
+ $this->assertOAuthVersion( Consumer::OAUTH_VERSION_1 );
+ $oauthServer = Utils::newMWOAuthServer();
+ $oauthRequest = MWOAuthRequest::fromRequest( $request );
+ $this->logger->debug( __METHOD__ . ": Getting temporary credentials" );
+ // fetch_request_token does the version, freshness, and sig checks
+ $token = $oauthServer->fetch_request_token( $oauthRequest );
+ $this->returnToken( $token, $format );
+ break;
+ case 'approve':
+ $this->assertOAuthVersion( Consumer::OAUTH_VERSION_2 );
+ $format = 'html';
+ $clientId = $request->getVal( 'client_id', '' );
+ $this->logger->debug( __METHOD__ . ": doing '$subpage' for OAuth2 with " .
+ "client_id '$clientId' for '{$user->getName()}'" );
+ if ( $user->isAnon() ) {
+ // Should not happen, as user login status will already be checked at this point
+ // Just redirect back to REST, it will then redirect to login
+ return $this->redirectToREST();
+ }
+ if ( $request->wasPosted() && $request->getCheck( 'cancel' ) ) {
+ $this->showCancelPage( $clientId );
+ } else {
+ $this->handleAuthorizationForm(
+ null, $clientId, true
+ );
+ }
+
+ break;
+ case 'authorize':
+ case 'authenticate':
+ $this->assertOAuthVersion( Consumer::OAUTH_VERSION_1 );
+ $format = 'html'; // for exceptions
+
+ $requestToken = $request->getVal( 'requestToken',
+ $request->getVal( 'oauth_token' ) );
+ $consumerKey = $request->getVal( 'consumerKey',
+ $request->getVal( 'oauth_consumer_key' ) );
+ $this->logger->debug( __METHOD__ . ": doing '$subpage' with " .
+ "'$requestToken' '$consumerKey' for '{$user->getName()}'" );
+
+ // TODO? Test that $requestToken exists in memcache
+ if ( $user->isAnon() ) {
+ // Login required on provider wiki
+ $this->requireLogin( 'mwoauth-login-required-reason' );
+ } else {
+ if ( $request->wasPosted() && $request->getCheck( 'cancel' ) ) {
+ // Show acceptance cancellation confirmation
+ $this->showCancelPage( $consumerKey );
+ } else {
+ // Show form and redirect on submission for authorization
+ $this->handleAuthorizationForm(
+ $requestToken, $consumerKey, $subpage === 'authenticate'
+ );
+ }
+ }
+ break;
+ case 'token':
+ $this->assertOAuthVersion( Consumer::OAUTH_VERSION_1 );
+ $oauthServer = Utils::newMWOAuthServer();
+ $oauthRequest = MWOAuthRequest::fromRequest( $request );
+
+ $isRsa = $oauthRequest->get_parameter( "oauth_signature_method" ) === 'RSA-SHA1';
+
+ // We want to use HTTPS when returning the credentials. But
+ // for RSA we don't need to return a token secret, so HTTP is ok.
+ if ( $wgMWOAuthSecureTokenTransfer && !$isRsa
+ && $request->detectProtocol() == 'http'
+ && substr( wfExpandUrl( '/', PROTO_HTTPS ), 0, 8 ) === 'https://'
+ ) {
+ $redirUrl = str_replace(
+ 'http://', 'https://', $request->getFullRequestURL()
+ );
+ $this->getOutput()->redirect( $redirUrl );
+ $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
+ break;
+ }
+
+ $token = $oauthServer->fetch_access_token( $oauthRequest );
+ if ( $isRsa ) {
+ // RSA doesn't use the token secret, so don't return one.
+ $token->secret = '__unused__';
+ }
+ $this->returnToken( $token, $format );
+ break;
+ case 'verified':
+ $this->assertOAuthVersion( Consumer::OAUTH_VERSION_1 );
+ $format = 'html'; // for exceptions
+ $verifier = $request->getVal( 'oauth_verifier' );
+ $requestToken = $request->getVal( 'oauth_token' );
+ if ( !$verifier || !$requestToken ) {
+ throw new MWOAuthException( 'mwoauth-bad-request-missing-params', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E001',
+ 'E001',
+ true
+ ) )
+ ] );
+ }
+ $this->getOutput()->addSubtitle( $this->msg( 'mwoauth-desc' )->escaped() );
+ $this->showResponse(
+ $this->msg( 'mwoauth-verified',
+ wfEscapeWikiText( $verifier ),
+ wfEscapeWikiText( $requestToken )
+ )->parse(),
+ $format
+ );
+ break;
+ case 'grants':
+ $this->assertOAuthVersion( Consumer::OAUTH_VERSION_1 );
+ // Backwards compatibility
+ $listGrants = \SpecialPage::getTitleFor( 'ListGrants' );
+ $this->getOutput()->redirect( $listGrants->getFullURL() );
+ break;
+ case 'identify':
+ $this->assertOAuthVersion( Consumer::OAUTH_VERSION_1 );
+ $format = 'json'; // we only return JWT, so we assume json
+ $server = Utils::newMWOAuthServer();
+ $oauthRequest = MWOAuthRequest::fromRequest( $request );
+ // verify_request throws an exception if anything isn't verified
+ list( $consumer, $token ) = $server->verify_request( $oauthRequest );
+ /** @var Consumer $consumer */
+ /** @var MWOAuthToken $token */
+
+ $wiki = wfWikiID();
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $access = ConsumerAcceptance::newFromToken( $dbr, $token->key );
+ $localUser = Utils::getLocalUserFromCentralId( $access->getUserId() );
+ if ( !$localUser || !$localUser->isLoggedIn() ) {
+ throw new MWOAuthException( 'mwoauth-invalid-authorization-invalid-user', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E008',
+ 'E008',
+ true
+ ) )
+ ] );
+ } elseif ( $localUser->isLocked() ||
+ $wgBlockDisablesLogin && $localUser->isBlocked()
+ ) {
+ throw new MWOAuthException( 'mwoauth-invalid-authorization-blocked-user' );
+ }
+ // Access token is for this wiki
+ if ( $access->getWiki() !== '*' && $access->getWiki() !== $wiki ) {
+ throw new MWOAuthException(
+ 'mwoauth-invalid-authorization-wrong-wiki',
+ [ $wiki ]
+ );
+ } elseif ( !$consumer->isUsableBy( $localUser ) ) {
+ throw new MWOAuthException( 'mwoauth-invalid-authorization-not-approved',
+ $consumer->getName() );
+ }
+
+ // We know the identity of the user who granted the authorization
+ $this->outputJWT( $localUser, $consumer, $oauthRequest, $format, $access );
+ break;
+ case 'rest_redirect':
+ $query = $this->getRequest()->getQueryValues();
+ $restUrl = $query['rest_url'];
+ unset( $query['title'] );
+ unset( $query['rest_url'] );
+
+ $target = wfExpandUrl( $restUrl );
+
+ $this->getOutput()->redirect( wfAppendQuery( $target, $query ) );
+ break;
+ case '':
+ $output = $this->getOutput();
+ $this->addHelpLink( 'Help:OAuth' );
+ $output->addWikiMsg( 'mwoauth-nosubpage-explanation' );
+ break;
+ default:
+ $format = $request->getVal( 'format', 'html' );
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromKey(
+ $dbr,
+ $request->getVal( 'oauth_consumer_key', null )
+ ),
+ $this->getContext()
+ );
+
+ if ( !$cmrAc || !$cmrAc->userCanAccess( 'userId' ) ) {
+ $this->showError(
+ $this->msg( 'mwoauth-bad-request-invalid-action' )->rawParams(
+ \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E002',
+ 'E002',
+ true
+ )
+ ),
+ $format
+ );
+ } else {
+ $owner = $cmrAc->getUserName( $this->getUser() );
+ $this->showError(
+ $this->msg( 'mwoauth-bad-request-invalid-action-contact',
+ Utils::getCentralUserTalk( $owner )
+ )->rawParams( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E003',
+ 'E003',
+ true
+ ) ),
+ $format
+ );
+ }
+ }
+ } catch ( MWOAuthException $exception ) {
+ $this->logger->warning( __METHOD__ . ": Exception " . $exception->getMessage(),
+ [ 'exception' => $exception ] );
+ $this->showError( $this->msg( $exception->msg, $exception->params ), $format );
+ } catch ( OAuthException $exception ) {
+ $this->logger->warning( __METHOD__ . ": Exception " . $exception->getMessage(),
+ [ 'exception' => $exception ] );
+ $this->showError(
+ $this->msg( 'mwoauth-oauth-exception', $exception->getMessage() ),
+ $format
+ );
+ }
+
+ $this->getOutput()->addModuleStyles( 'ext.MWOAuth.styles' );
+ }
+
+ /**
+ * @param string $consumerKey
+ * @throws MWOAuthException
+ */
+ protected function showCancelPage( $consumerKey ) {
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromKey( $dbr, $consumerKey ),
+ $this->getContext()
+ );
+ if ( !$cmrAc ) {
+ throw new MWOAuthException( 'mwoauth-invalid-consumer-key' );
+ }
+
+ if ( $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ) {
+ // Respond to client with user approval denied error
+ $this->redirectToREST( [
+ 'approval_cancel' => 1
+ ] );
+ return;
+ }
+
+ $this->getOutput()->addSubtitle( $this->msg( 'mwoauth-desc' )->escaped() );
+ $this->getOutput()->addWikiMsg(
+ 'mwoauth-acceptance-cancelled',
+ $cmrAc->getName()
+ );
+ $this->getOutput()->addReturnTo( \Title::newMainPage() );
+ }
+
+ /**
+ * Make statements about the user, and sign the json with
+ * a key shared with the Consumer.
+ * @param \User $user the user who is the subject of this request
+ * @param Consumer $consumer
+ * @param MWOAuthRequest $request
+ * @param string $format the format of the response: raw, json, or html
+ * @param ConsumerAcceptance $access
+ */
+ protected function outputJWT( $user, $consumer, $request, $format, $access ) {
+ $grants = $access->getGrants();
+ $userStatementProvider = UserStatementProvider::factory( $user, $consumer, $grants );
+
+ $statement = $userStatementProvider->getUserStatement();
+ // String value used to associate a Client session with an ID Token, and to mitigate
+ // replay attacks. The value is passed through unmodified from the Authorization Request.
+ $statement['nonce'] = $request->get_parameter( 'oauth_nonce' );
+ $JWT = JWT::encode( $statement, $consumer->secret );
+ $this->showResponse( $JWT, $format );
+ }
+
+ protected function handleAuthorizationForm( $requestToken, $consumerKey, $authenticate ) {
+ $this->getOutput()->addSubtitle( $this->msg( 'mwoauth-desc' )->escaped() );
+ $user = $this->getUser();
+
+ $oauthServer = Utils::newMWOAuthServer();
+
+ if ( !$consumerKey && $this->oauthVersion === Consumer::OAUTH_VERSION_1 ) {
+ $consumerKey = $oauthServer->getConsumerKey( $requestToken );
+ }
+
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromKey( Utils::getCentralDB( DB_REPLICA ), $consumerKey ),
+ $this->getContext()
+ );
+
+ if ( !$cmrAc || !$cmrAc->userCanAccess( [ 'name', 'userId', 'grants' ] ) ) {
+ throw new MWOAuthException( 'mwoauthserver-bad-consumer-key', [
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E006',
+ 'E006',
+ true
+ ) )
+ ] );
+ } elseif (
+ !$cmrAc->getDAO()->isUsableBy( $user ) ||
+ $cmrAc->getDAO()->getOAuthVersion() !== $this->oauthVersion
+ ) {
+ throw new MWOAuthException(
+ 'mwoauthserver-bad-consumer',
+ [
+ $cmrAc->getName(),
+ Utils::getCentralUserTalk( $cmrAc->getUserName() ),
+ ]
+ );
+ }
+
+ $existing = $cmrAc->getDAO()->getCurrentAuthorization( $user, wfWikiID() );
+
+ // If only authentication was requested, and the existing authorization
+ // matches, and the only grants are 'mwoauth-authonly' or 'mwoauth-authonlyprivate',
+ // then don't bother prompting the user about it.
+ if ( $existing && $authenticate &&
+ $existing->getWiki() === $cmrAc->getDAO()->getWiki() &&
+ $existing->getGrants() === $cmrAc->getDAO()->getGrants() &&
+ !array_diff( $existing->getGrants(), [ 'mwoauth-authonly', 'mwoauth-authonlyprivate' ] )
+ ) {
+ if ( $this->oauthVersion === Consumer::OAUTH_VERSION_2 ) {
+ $this->redirectToREST( [
+ 'approval_pass' => true
+ ] );
+ } else {
+ $callback = $cmrAc->getDAO()->authorize(
+ $user, false, $cmrAc->getDAO()->getGrants(), $requestToken
+ );
+ $this->getOutput()->redirect( $callback );
+ }
+ return;
+ }
+
+ $this->getOutput()->addModuleStyles(
+ [ 'mediawiki.ui', 'mediawiki.ui.button', 'ext.MWOAuth.Styles' ]
+ );
+ $this->getOutput()->addModules( 'ext.MWOAuth.AuthorizeDialog' );
+
+ $control = new ConsumerAcceptanceSubmitControl(
+ $this->getContext(), [], Utils::getCentralDB( DB_MASTER ), $this->oauthVersion
+ );
+
+ $form = \HTMLForm::factory( 'table',
+ $control->registerValidators( $this->getRequestValidators( [
+ 'existing' => $existing,
+ 'consumerKey' => $consumerKey,
+ 'requestToken' => $requestToken
+ ] ) ),
+ $this->getContext()
+ );
+ $form->setSubmitCallback(
+ function ( array $data, \IContextSource $context ) use ( $control ) {
+ if ( $context->getRequest()->getCheck( 'cancel' ) ) { // sanity
+ throw new \MWException( 'Received request for a form cancellation.' );
+ }
+ $control->setInputParameters( $data );
+ return $control->submit();
+ }
+ );
+ $form->setId( 'mw-mwoauth-authorize-form' );
+
+ // Possible messages are:
+ // * mwoauth-form-description-allwikis
+ // * mwoauth-form-description-onewiki
+ // * mwoauth-form-description-allwikis-nogrants
+ // * mwoauth-form-description-onewiki-nogrants
+ // * mwoauth-form-description-allwikis-privateinfo
+ // * mwoauth-form-description-onewiki-privateinfo
+ // * mwoauth-form-description-allwikis-privateinfo-norealname
+ // * mwoauth-form-description-onewiki-privateinfo-norealname
+ $msgKey = 'mwoauth-form-description';
+ $params = [
+ $this->getUser()->getName(),
+ $cmrAc->getName(),
+ $cmrAc->getUserName(),
+ ];
+ if ( $cmrAc->getWiki() === '*' ) {
+ $msgKey .= '-allwikis';
+ } else {
+ $msgKey .= '-onewiki';
+ $params[] = $cmrAc->getWikiName();
+ }
+ $grants = $cmrAc->getGrants();
+ if ( $this->oauthVersion === Consumer::OAUTH_VERSION_2 ) {
+ $grants = $this->getRequestedGrants( $cmrAc );
+ }
+
+ $grantsText = \MWGrants::getGrantsWikiText( $grants, $this->getLanguage() );
+ if ( $grantsText === "\n" ) {
+ if ( in_array( 'mwoauth-authonlyprivate', $cmrAc->getGrants(), true ) ) {
+ $msgKey .= '-privateinfo';
+ if ( !$this->useRealNames() ) {
+ // If the wiki does not use real names, don't mention them in the authorization
+ // dialog to avoid scaring users. The wiki where the authorization dialog is
+ // shown and the wiki where the user is actually identified might be different;
+ // there's not much we can do about that here so it is left to the wiki
+ // administrator to set up the farm in a non-misleading way.
+ $msgKey .= '-norealname';
+ }
+ } else {
+ $msgKey .= '-nogrants';
+ }
+ } else {
+ $params[] = $grantsText;
+ }
+ $form->addHeaderText( $this->msg( $msgKey, $params )->parseAsBlock() );
+ $form->addHeaderText( $this->msg( 'mwoauth-form-legal' )->text() );
+
+ $form->suppressDefaultSubmit();
+ $form->addButton( [
+ 'name' => 'accept',
+ 'value' => $this->msg( 'mwoauth-form-button-approve' )->text(),
+ 'id' => 'mw-mwoauth-accept',
+ 'attribs' => [
+ 'class' => 'mw-mwoauth-authorize-button mw-ui-button mw-ui-progressive'
+ ]
+ ] );
+ $form->addButton( [
+ 'name' => 'cancel',
+ 'value' => $this->msg( 'mwoauth-form-button-cancel' )->text(),
+ 'attribs' => [
+ 'class' => 'mw-mwoauth-authorize-button mw-ui-button mw-ui-quiet'
+ ]
+ ] );
+
+ $form->addFooterText( $this->getSkin()->privacyLink() );
+
+ $this->getOutput()->addHTML(
+ '<div id="mw-mwoauth-authorize-dialog" class="mw-ui-container">' );
+ $status = $form->show();
+
+ $this->getOutput()->addHTML( '</div>' );
+ if ( $status instanceof \Status && $status->isOK() ) {
+ if ( $this->oauthVersion === Consumer::OAUTH_VERSION_2 ) {
+ $this->redirectToREST( [
+ 'approval_pass' => true
+ ] );
+ } else {
+ // Redirect to callback url
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $this->getOutput()->redirect( $status->value['result']['callbackUrl'] );
+ }
+ }
+ }
+
+ private function redirectToREST( $queryAppend = [] ) {
+ $redirectParams = [
+ 'returnto' => $this->getRequest()->getText(
+ 'returnto', $this->getRequest()->getText( 'returnto' )
+ ),
+ 'returntoquery' => wfCgiToArray(
+ $this->getRequest()->getText(
+ 'returntoquery', $this->getRequest()->getText( 'returntoquery' )
+ )
+ )
+ ];
+
+ $expanded = wfExpandUrl( $redirectParams['returnto'] );
+ if ( !$expanded ) {
+ return;
+ }
+
+ $returnToQuery = array_merge(
+ $redirectParams['returntoquery'],
+ $queryAppend
+ );
+ $returnToQuery = wfArrayToCgi( $returnToQuery );
+
+ $this->getOutput()->disable();
+ $this->getOutput()->getRequest()->response()->header(
+ 'Location: ' . "$expanded?{$returnToQuery}"
+ );
+ }
+
+ private function getRequestValidators( $data = [] ) {
+ $validators = [
+ 'action' => [
+ 'type' => 'hidden',
+ 'default' => 'accept',
+ ],
+ 'confirmUpdate' => [
+ 'type' => 'hidden',
+ 'default' => $data['existing'] ? 1 : 0,
+ ],
+ 'oauth_version' => [
+ 'name' => 'oauth_version',
+ 'type' => 'hidden',
+ 'default' => $this->oauthVersion
+ ],
+ ];
+ if ( $this->oauthVersion === Consumer::OAUTH_VERSION_2 ) {
+ $validators += [
+ 'client_id' => [
+ 'name' => 'client_id',
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getText( 'client_id' )
+ ],
+ 'scope' => [
+ 'name' => 'scope',
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getText( 'scope' )
+ ],
+ 'returnto' => [
+ 'name' => 'returnto',
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getText( 'returnto' )
+ ],
+ 'returntoquery' => [
+ 'name' => 'returntoquery',
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getText( 'returntoquery' )
+ ],
+ ];
+ } else {
+ $validators += [
+ 'consumerKey' => [
+ 'name' => 'consumerKey',
+ 'type' => 'hidden',
+ 'default' => $data['consumerKey']
+ ],
+ 'requestToken' => [
+ 'name' => 'requestToken',
+ 'type' => 'hidden',
+ 'default' => $data['requestToken'],
+ ],
+ ];
+ }
+
+ return $validators;
+ }
+
+ /**
+ * OAuth 2.0 only
+ * Get only the grants (scopes) that were actually requested (and are allowed)
+ *
+ * @param ConsumerAccessControl $cmrAc
+ * @return array
+ */
+ private function getRequestedGrants( $cmrAc ) {
+ $allowed = $cmrAc->getGrants();
+ $requested = explode( ' ', $this->getRequest()->getText( 'scope', '' ) );
+
+ return array_intersect( $requested, $allowed );
+ }
+
+ /**
+ * @param \Message $message to return to the user
+ * @param string $format the format of the response: html, raw, or json
+ */
+ private function showError( $message, $format ) {
+ if ( $format == 'raw' ) {
+ $this->showResponse( 'Error: ' . $message->escaped(), 'raw' );
+ } elseif ( $format == 'json' ) {
+ $error = \FormatJson::encode( [
+ 'error' => $message->getKey(),
+ 'message' => $message->text(),
+ ] );
+ $this->showResponse( $error, 'json' );
+ } elseif ( $format == 'html' ) {
+ $this->getOutput()->showErrorPage( 'mwoauth-error', $message );
+ }
+ }
+
+ /**
+ * @param OAuthToken $token
+ * @param string $format the format of the response: html, raw, or json
+ */
+ private function returnToken( OAuthToken $token, $format ) {
+ if ( $format == 'raw' ) {
+ $return = 'oauth_token=' . OAuthUtil::urlencode_rfc3986( $token->key );
+ $return .= '&oauth_token_secret=' . OAuthUtil::urlencode_rfc3986( $token->secret );
+ $return .= '&oauth_callback_confirmed=true';
+ $this->showResponse( $return, 'raw' );
+ } elseif ( $format == 'json' ) {
+ $this->showResponse( \FormatJson::encode( $token ), 'json' );
+ } elseif ( $format == 'html' ) {
+ $html = \Html::element(
+ 'li',
+ [],
+ 'oauth_token = ' . OAuthUtil::urlencode_rfc3986( $token->key )
+ );
+ $html .= \Html::element(
+ 'li',
+ [],
+ 'oauth_token_secret = ' . OAuthUtil::urlencode_rfc3986( $token->secret )
+ );
+ $html .= \Html::element(
+ 'li',
+ [],
+ 'oauth_callback_confirmed = true'
+ );
+ $html = \Html::rawElement( 'ul', [], $html );
+ $this->showResponse( $html, 'html' );
+ }
+ }
+
+ /**
+ * @param string $data html or string to pass back to the user. Already escaped.
+ * @param string $format the format of the response: raw, json, or html
+ * @param-taint $data escaped
+ */
+ private function showResponse( $data, $format ) {
+ $out = $this->getOutput();
+ if ( $format == 'raw' || $format == 'json' ) {
+ $this->getOutput()->disable();
+ // Cancel output buffering and gzipping if set
+ wfResetOutputBuffers();
+ // We must not allow the output to be Squid cached
+ $response = $this->getRequest()->response();
+ $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
+ $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
+ $response->header( 'Pragma: no-cache' );
+ $response->header( 'Content-length: ' . strlen( $data ) );
+ if ( $format == 'json' ) {
+ $response->header( 'Content-type: application/json' );
+ } else {
+ $response->header( 'Content-type: text/plain' );
+ }
+ print $data;
+ } elseif ( $format == 'html' ) { // html
+ $out->addHTML( $data );
+ }
+ }
+
+ /**
+ * Check whether the wiki is configured to use/show real names.
+ * We assume that either all or none of the OAuth wikis in a farm use real names.
+ * @return bool
+ */
+ private function useRealNames() {
+ $config = $this->getContext()->getConfig();
+ return !in_array( 'realname', $config->get( 'HiddenPrefs' ), true );
+ }
+
+ /**
+ * Get the requested OAuth version from the request
+ *
+ * @param \WebRequest $request
+ * @return string
+ */
+ private function determineOAuthVersion( \WebRequest $request ) {
+ $this->oauthVersion = $request->getInt( 'oauth_version', Consumer::OAUTH_VERSION_1 );
+
+ return $this->oauthVersion;
+ }
+
+ /**
+ * @param string $allowed Allowed version
+ * @throws MWOAuthException
+ */
+ private function assertOAuthVersion( $allowed ) {
+ if ( $this->oauthVersion !== $allowed ) {
+ throw new MWOAuthException(
+ 'mwoauth-oauth-unsupported-version',
+ $this->oauthVersion
+ );
+ }
+ }
+}
diff --git a/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthConsumerRegistration.php b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthConsumerRegistration.php
new file mode 100644
index 00000000..296f9371
--- /dev/null
+++ b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthConsumerRegistration.php
@@ -0,0 +1,618 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\SpecialPages;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAccessControl;
+use MediaWiki\Extensions\OAuth\Control\ConsumerSubmitControl;
+use MediaWiki\Extensions\OAuth\Frontend\Pagers\ListMyConsumersPager;
+use MediaWiki\Extensions\OAuth\Frontend\UIUtils;
+use MediaWiki\MediaWikiServices;
+use User;
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * Page that has registration request form and consumer update form
+ */
+class SpecialMWOAuthConsumerRegistration extends \SpecialPage {
+
+ public function __construct() {
+ parent::__construct( 'OAuthConsumerRegistration' );
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ public function userCanExecute( User $user ) {
+ return $user->isEmailConfirmed();
+ }
+
+ public function displayRestrictionError() {
+ throw new \PermissionsError( null, [ 'mwoauthconsumerregistration-need-emailconfirmed' ] );
+ }
+
+ public function execute( $par ) {
+ global $wgMWOAuthSecureTokenTransfer, $wgMWOAuthReadOnly;
+ $this->checkPermissions();
+
+ $request = $this->getRequest();
+ $user = $this->getUser();
+ $lang = $this->getLanguage();
+ $centralUserId = Utils::getCentralIdFromLocalUser( $user );
+
+ // Redirect to HTTPs if attempting to access this page via HTTP.
+ // Proposals and updates to consumers can involve sending new secrets.
+ if ( $wgMWOAuthSecureTokenTransfer
+ && $request->detectProtocol() == 'http'
+ && substr( wfExpandUrl( '/', PROTO_HTTPS ), 0, 8 ) === 'https://'
+ ) {
+ $redirUrl = str_replace( 'http://', 'https://', $request->getFullRequestURL() );
+ $this->getOutput()->redirect( $redirUrl );
+ $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
+ return;
+ }
+
+ $this->setHeaders();
+ $this->getOutput()->disallowUserJs();
+ $this->addHelpLink( 'Help:OAuth' );
+
+ $block = $user->getBlock();
+ if ( $block ) {
+ throw new \UserBlockedError( $block );
+ }
+ $this->checkReadOnly();
+ if ( !$this->getUser()->isLoggedIn() ) {
+ throw new \UserNotLoggedIn();
+ }
+
+ // Format is Special:OAuthConsumerRegistration[/propose|/list|/update/<consumer key>]
+ $navigation = explode( '/', $par );
+ $action = $navigation[0] ?? null;
+ $consumerKey = $navigation[1] ?? null;
+
+ if ( $wgMWOAuthReadOnly && $action !== 'list' ) {
+ throw new \ErrorPageError( 'mwoauth-error', 'mwoauth-db-readonly' );
+ }
+
+ switch ( $action ) {
+ case 'propose':
+ if ( !$user->isAllowed( 'mwoauthproposeconsumer' ) ) {
+ throw new \PermissionsError( 'mwoauthproposeconsumer' );
+ }
+
+ $allWikis = Utils::getAllWikiNames();
+
+ $showGrants = \MWGrants::getValidGrants();
+ $config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'mwoauth' );
+
+ $dbw = Utils::getCentralDB( DB_MASTER ); // @TODO: lazy handle
+ $control = new ConsumerSubmitControl( $this->getContext(), [], $dbw );
+ $form = \HTMLForm::factory( 'ooui',
+ $control->registerValidators( [
+ 'name' => [
+ 'type' => 'text',
+ 'label-message' => 'mwoauth-consumer-name',
+ 'size' => '45',
+ 'required' => true
+ ],
+ 'version' => [
+ 'type' => 'text',
+ 'label-message' => 'mwoauth-consumer-version',
+ 'required' => true,
+ 'default' => "1.0"
+ ],
+ 'oauthVersion' => [
+ 'type' => 'select',
+ 'label-message' => 'mwoauth-oauth-version',
+ 'options' => [
+ $this->msg( 'mwoauth-oauth-version-1' )->escaped() =>
+ Consumer::OAUTH_VERSION_1,
+ $this->msg( 'mwoauth-oauth-version-2' )->escaped() =>
+ Consumer::OAUTH_VERSION_2
+ ],
+ 'required' => true,
+ 'default' => Consumer::OAUTH_VERSION_1
+ ],
+ 'description' => [
+ 'type' => 'textarea',
+ 'label-message' => 'mwoauth-consumer-description',
+ 'required' => true,
+ 'rows' => 5
+ ],
+ 'ownerOnly' => [
+ 'type' => 'check',
+ 'label-message' => [ 'mwoauth-consumer-owner-only', $user->getName() ],
+ 'help-message' => [ 'mwoauth-consumer-owner-only-help', $user->getName() ],
+ ],
+ 'callbackUrl' => [
+ 'type' => 'text',
+ 'label-message' => 'mwoauth-consumer-callbackurl',
+ 'required' => true,
+ 'hide-if' => [ '!==', 'ownerOnly', '' ],
+ ],
+ 'callbackIsPrefix' => [
+ 'type' => 'check',
+ 'label-message' => 'mwoauth-consumer-callbackisprefix',
+ 'required' => true,
+ 'hide-if' => [ '!==', 'ownerOnly', '' ],
+ ],
+ 'email' => [
+ 'type' => 'text',
+ 'label-message' => 'mwoauth-consumer-email',
+ 'required' => true,
+ 'readonly' => true,
+ 'default' => $user->getEmail(),
+ 'help-message' => 'mwoauth-consumer-email-help',
+ ],
+ 'wiki' => [
+ 'type' => $allWikis ? 'combobox' : 'select',
+ 'options' => [
+ $this->msg( 'mwoauth-consumer-allwikis' )->escaped() => '*',
+ $this->msg( 'mwoauth-consumer-wiki-thiswiki', wfWikiID() )
+ ->escaped() => wfWikiID()
+ ] + array_flip( $allWikis ),
+ 'label-message' => 'mwoauth-consumer-wiki',
+ 'required' => true,
+ 'default' => '*'
+ ],
+ 'oauth2IsConfidential' => [
+ 'type' => 'check',
+ 'label-message' => 'mwoauth-oauth2-is-confidential',
+ 'help-message' => 'mwoauth-oauth2-is-confidential-help',
+ 'hide-if' => [ '!==', 'oauthVersion', (string)Consumer::OAUTH_VERSION_2 ],
+ 'default' => 1
+ ],
+ 'oauth2GrantTypes' => [
+ 'type' => 'multiselect',
+ 'label-message' => 'mwoauth-oauth2-granttypes',
+ 'hide-if' => [ 'OR',
+ [ '!==', 'oauthVersion', (string)Consumer::OAUTH_VERSION_2 ],
+ [ '!==', 'ownerOnly', '' ]
+ ],
+ 'options' => array_filter( [
+ $this->msg( 'mwoauth-oauth2-granttype-auth-code' )->escaped() =>
+ 'authorization_code',
+ $this->msg( 'mwoauth-oauth2-granttype-refresh-token' )->escaped() =>
+ 'refresh_token',
+ $this->msg( 'mwoauth-oauth2-granttype-client-credentials' )->escaped() =>
+ 'client_credentials',
+ ], function ( $grantType ) use ( $config ) {
+ return in_array( $grantType, $config->get( 'OAuth2EnabledGrantTypes' ) );
+ } ),
+ 'dropdown' => true,
+ 'required' => true,
+ 'default' => [ 'authorization_code', 'refresh_token' ]
+ ],
+ 'granttype' => [
+ 'type' => 'radio',
+ 'options-messages' => [
+ 'grant-mwoauth-authonly' => 'authonly',
+ 'grant-mwoauth-authonlyprivate' => 'authonlyprivate',
+ 'mwoauth-granttype-normal' => 'normal',
+ ],
+ 'label-message' => 'mwoauth-consumer-granttypes',
+ 'default' => 'normal',
+ ],
+ 'grants' => [
+ 'type' => 'checkmatrix',
+ 'label-message' => 'mwoauth-consumer-grantsneeded',
+ 'help-message' => 'mwoauth-consumer-grantshelp',
+ 'hide-if' => [ '!==', 'granttype', 'normal' ],
+ 'columns' => [
+ $this->msg( 'mwoauth-consumer-required-grant' )->escaped() => 'grant'
+ ],
+ 'rows' => array_combine(
+ array_map( 'MWGrants::getGrantsLink', $showGrants ),
+ $showGrants
+ ),
+ 'tooltips' => array_combine(
+ array_map( 'MWGrants::getGrantsLink', $showGrants ),
+ array_map(
+ function ( $rights ) use ( $lang ) {
+ return $lang->semicolonList( array_map(
+ '\User::getRightDescription', $rights ) );
+ },
+ array_intersect_key(
+ \MWGrants::getRightsByGrant(), array_flip( $showGrants ) )
+ )
+ ),
+ 'force-options-on' => array_map(
+ function ( $g ) {
+ return "grant-$g";
+ },
+ \MWGrants::getHiddenGrants()
+ ),
+ 'validation-callback' => null, // different format
+ ],
+ 'restrictions' => [
+ 'class' => 'HTMLRestrictionsField',
+ 'required' => true,
+ 'default' => \MWRestrictions::newDefault(),
+ ],
+ 'rsaKey' => [
+ 'type' => 'textarea',
+ 'label-message' => 'mwoauth-consumer-rsakey',
+ 'help-message' => 'mwoauth-consumer-rsakey-help',
+ 'required' => false,
+ 'default' => '',
+ 'rows' => 5,
+ 'hide-if' => [ '===', 'oauthVersion', (string)Consumer::OAUTH_VERSION_2 ]
+ ],
+ 'agreement' => [
+ 'type' => 'check',
+ 'label-message' => 'mwoauth-consumer-developer-agreement',
+ 'required' => true,
+ ],
+ 'action' => [
+ 'type' => 'hidden',
+ 'default' => 'propose'
+ ]
+ ] ),
+ $this->getContext()
+ );
+ $form->setSubmitCallback(
+ function ( array $data, \IContextSource $context ) use ( $control ) {
+ $data['grants'] = \FormatJson::encode( // adapt form to controller
+ preg_replace( '/^grant-/', '', $data['grants'] ) );
+ // 'callbackUrl' must be present,
+ // otherwise MWOAuthSubmitControl::validateFields() fails.
+ if ( $data['ownerOnly'] && !isset( $data['callbackUrl'] ) ) {
+ $data['callbackUrl'] = '';
+ }
+ // Force all ownerOnly clients to use client_credentials
+ if ( $data['ownerOnly'] ) {
+ $data['oauth2GrantTypes'] = [ 'client_credentials' ];
+ }
+
+ $control->setInputParameters( $data );
+ return $control->submit();
+ }
+ );
+ $form->setWrapperLegendMsg( 'mwoauthconsumerregistration-propose-legend' );
+ $form->setSubmitTextMsg( 'mwoauthconsumerregistration-propose-submit' );
+ $form->addPreText(
+ $this->msg( 'mwoauthconsumerregistration-propose-text' )->parseAsBlock() );
+
+ $status = $form->show();
+ if ( $status instanceof \Status && $status->isOK() ) {
+ /** @var Consumer $cmr */
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $cmr = $status->value['result']['consumer'];
+ if ( $cmr->getOwnerOnly() ) {
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $accessToken = $status->value['result']['accessToken'];
+ if ( $cmr->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ) {
+ // If we just add raw AT to the page, it would go 3000px wide
+ $accessToken = \Html::element( 'span', [
+ 'style' => 'overflow-wrap: break-word'
+ ], (string)$accessToken );
+
+ $this->getOutput()->addWikiMsg(
+ 'mwoauthconsumerregistration-created-owner-only-oauth2',
+ $cmr->getConsumerKey(),
+ Utils::hmacDBSecret( $cmr->getSecretKey() ),
+ \Message::rawParam( $accessToken )
+ );
+ } else {
+ $this->getOutput()->addWikiMsg(
+ 'mwoauthconsumerregistration-created-owner-only',
+ $cmr->getConsumerKey(),
+ Utils::hmacDBSecret( $cmr->getSecretKey() ),
+ $accessToken->key,
+ Utils::hmacDBSecret( $accessToken->secret )
+ );
+ }
+ } else {
+ $this->getOutput()->addWikiMsg( 'mwoauthconsumerregistration-proposed',
+ $cmr->getConsumerKey(),
+ Utils::hmacDBSecret( $cmr->getSecretKey() ) );
+ }
+ $this->getOutput()->returnToMain();
+ }
+ break;
+ case 'update':
+ if ( !$user->isAllowed( 'mwoauthupdateownconsumer' ) ) {
+ throw new \PermissionsError( 'mwoauthupdateownconsumer' );
+ }
+
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromKey( $dbr, $consumerKey ), $this->getContext() );
+ if ( !$cmrAc ) {
+ $this->getOutput()->addWikiMsg( 'mwoauth-invalid-consumer-key' );
+ break;
+ } elseif ( $cmrAc->getDAO()->getDeleted() && !$user->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ throw new \PermissionsError( 'mwoauthviewsuppressed' );
+ } elseif ( $cmrAc->getDAO()->getUserId() !== $centralUserId ) {
+ // Do not show private information to other users
+ $this->getOutput()->addWikiMsg( 'mwoauth-invalid-consumer-key' );
+ break;
+ }
+ $oldSecretKey = $cmrAc->getDAO()->getSecretKey();
+
+ $dbw = Utils::getCentralDB( DB_MASTER ); // @TODO: lazy handle
+ $control = new ConsumerSubmitControl( $this->getContext(), [], $dbw );
+ $form = \HTMLForm::factory( 'ooui',
+ $control->registerValidators( [
+ 'info' => [
+ 'type' => 'info',
+ 'raw' => true,
+ 'default' => UIUtils::generateInfoTable( [
+ 'mwoauth-consumer-name' => $cmrAc->getName(),
+ 'mwoauth-consumer-version' => $cmrAc->getVersion(),
+ 'mwoauth-oauth-version' => $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ?
+ wfMessage( 'mwoauth-oauth-version-2' )->text() :
+ wfMessage( 'mwoauth-oauth-version-1' )->text(),
+ 'mwoauth-consumer-key' => $cmrAc->getConsumerKey(),
+ ], $this->getContext() ),
+ ],
+ 'restrictions' => [
+ 'class' => 'HTMLRestrictionsField',
+ 'required' => true,
+ 'default' => $cmrAc->getDAO()->getRestrictions(),
+ ],
+ 'resetSecret' => [
+ 'type' => 'check',
+ 'label-message' => 'mwoauthconsumerregistration-resetsecretkey',
+ 'default' => false,
+ ],
+ 'rsaKey' => [
+ 'type' => 'textarea',
+ 'label-message' => 'mwoauth-consumer-rsakey',
+ 'required' => false,
+ 'default' => $cmrAc->getDAO()->getRsaKey(),
+ 'rows' => 5,
+ ],
+ 'reason' => [
+ 'type' => 'text',
+ 'label-message' => 'mwoauth-consumer-reason',
+ 'required' => true
+ ],
+ 'consumerKey' => [
+ 'type' => 'hidden',
+ 'default' => $cmrAc->getConsumerKey(),
+ ],
+ 'changeToken' => [
+ 'type' => 'hidden',
+ 'default' => $cmrAc->getDAO()->getChangeToken( $this->getContext() ),
+ ],
+ 'action' => [
+ 'type' => 'hidden',
+ 'default' => 'update'
+ ]
+ ] ),
+ $this->getContext()
+ );
+ $form->setSubmitCallback(
+ function ( array $data, \IContextSource $context ) use ( $control ) {
+ $control->setInputParameters( $data );
+ return $control->submit();
+ }
+ );
+ $form->setWrapperLegendMsg( 'mwoauthconsumerregistration-update-legend' );
+ $form->setSubmitTextMsg( 'mwoauthconsumerregistration-update-submit' );
+ $form->addPreText(
+ $this->msg( 'mwoauthconsumerregistration-update-text' )->parseAsBlock() );
+
+ $status = $form->show();
+ if ( $status instanceof \Status && $status->isOK() ) {
+ /** @var Consumer $cmr */
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $cmr = $status->value['result']['consumer'];
+ $this->getOutput()->addWikiMsg( 'mwoauthconsumerregistration-updated' );
+ $curSecretKey = $cmr->getSecretKey();
+ if ( $oldSecretKey !== $curSecretKey ) { // token reset?
+ if ( $cmr->getOwnerOnly() ) {
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $accessToken = $status->value['result']['accessToken'];
+ if ( $cmr->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ) {
+ // If we just add raw AT to the page, it would go 3000px wide
+ $accessToken = \Html::element( 'span', [
+ 'style' => 'overflow-wrap: break-word'
+ ], (string)$accessToken );
+
+ $this->getOutput()->addWikiMsg(
+ 'mwoauthconsumerregistration-secretreset-owner-only-oauth2',
+ $cmr->getConsumerKey(),
+ Utils::hmacDBSecret( $cmr->getSecretKey() ),
+ \Message::rawParam( $accessToken )
+ );
+ } else {
+ $this->getOutput()->addWikiMsg(
+ 'mwoauthconsumerregistration-secretreset-owner-only',
+ $cmr->getConsumerKey(),
+ Utils::hmacDBSecret( $curSecretKey ),
+ $accessToken->key,
+ Utils::hmacDBSecret( $accessToken->secret )
+ );
+ }
+ } else {
+ $this->getOutput()->addWikiMsg( 'mwoauthconsumerregistration-secretreset',
+ Utils::hmacDBSecret( $curSecretKey ) );
+ }
+ }
+ $this->getOutput()->returnToMain();
+ } else {
+ $out = $this->getOutput();
+ // Show all of the status updates
+ $logPage = new \LogPage( 'mwoauthconsumer' );
+ $out->addHTML( \Xml::element( 'h2', null, $logPage->getName()->text() ) );
+ \LogEventsList::showLogExtract( $out, 'mwoauthconsumer', '', '', [
+ 'conds' => [
+ 'ls_field' => 'OAuthConsumer',
+ 'ls_value' => $cmrAc->getConsumerKey(),
+ ],
+ 'flags' => \LogEventsList::NO_EXTRA_USER_LINKS,
+ ] );
+ }
+ break;
+ case 'list':
+ $pager = new ListMyConsumersPager( $this, [], $centralUserId );
+ if ( $pager->getNumRows() ) {
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ $this->getOutput()->addHTML( $pager->getBody() );
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ } else {
+ $this->getOutput()->addWikiMsg( "mwoauthconsumerregistration-none" );
+ }
+ # Every 30th view, prune old deleted items
+ if ( 0 == mt_rand( 0, 29 ) ) {
+ Utils::runAutoMaintenance( Utils::getCentralDB( DB_MASTER ) );
+ }
+ break;
+ default:
+ $this->getOutput()->addWikiMsg( 'mwoauthconsumerregistration-maintext' );
+ }
+
+ $this->addSubtitleLinks( $action, $consumerKey );
+
+ $this->getOutput()->addModuleStyles( 'ext.MWOAuth.styles' );
+ }
+
+ /**
+ * Show navigation links
+ *
+ * @param string $action
+ * @param string $consumerKey
+ * @return void
+ */
+ protected function addSubtitleLinks( $action, $consumerKey ) {
+ $listLinks = [];
+ if ( $consumerKey || $action !== 'propose' ) {
+ $listLinks[] = \Linker::linkKnown(
+ $this->getPageTitle( 'propose' ),
+ $this->msg( 'mwoauthconsumerregistration-propose' )->escaped() );
+ } else {
+ $listLinks[] = $this->msg( 'mwoauthconsumerregistration-propose' )->escaped();
+ }
+ if ( $consumerKey || $action !== 'list' ) {
+ $listLinks[] = \Linker::linkKnown(
+ $this->getPageTitle( 'list' ),
+ $this->msg( 'mwoauthconsumerregistration-list' )->escaped() );
+ } else {
+ $listLinks[] = $this->msg( 'mwoauthconsumerregistration-list' )->escaped();
+ }
+ if ( $consumerKey && $action == 'update' ) {
+ $listLinks[] = \Linker::linkKnown(
+ \SpecialPage::getTitleFor( 'OAuthListConsumers', "view/$consumerKey" ),
+ $this->msg( 'mwoauthconsumer-consumer-view' )->escaped() );
+ }
+
+ $linkHtml = $this->getLanguage()->pipeList( $listLinks );
+
+ $viewall = $this->msg( 'parentheses' )->rawParams(
+ \Linker::linkKnown(
+ $this->getPageTitle(),
+ $this->msg( 'mwoauthconsumerregistration-main' )->escaped()
+ )
+ )->escaped();
+
+ $this->getOutput()->setSubtitle(
+ "<strong>" . $this->msg( 'mwoauthconsumerregistration-navigation' )->escaped() .
+ "</strong> [{$linkHtml}] <strong>{$viewall}</strong>" );
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( DBConnRef $db, $row ) {
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromRow( $db, $row ), $this->getContext() );
+ $cmrKey = $cmrAc->getConsumerKey();
+
+ $links = [];
+ $links[] = \Linker::linkKnown(
+ \SpecialPage::getTitleFor( 'OAuthListConsumers', "view/$cmrKey" ),
+ $this->msg( 'mwoauthlistconsumers-view' )->escaped()
+ );
+
+ $links[] = \Linker::linkKnown(
+ $this->getPageTitle( 'update/' . $cmrKey ),
+ $this->msg( 'mwoauthconsumerregistration-manage' )->escaped()
+ );
+
+ $links = $this->getLanguage()->pipeList( $links );
+
+ $time = htmlspecialchars( $this->getLanguage()->timeanddate(
+ wfTimestamp( TS_MW, $cmrAc->getRegistration() ), true ) );
+
+ $stageKey = Consumer::$stageNames[$cmrAc->getStage()];
+ $encStageKey = htmlspecialchars( $stageKey ); // sanity
+ // Show last log entry (@TODO: title namespace?)
+ // @TODO: inject DB
+ $logHtml = '';
+ \LogEventsList::showLogExtract( $logHtml, 'mwoauthconsumer', '', '', [
+ 'conds' => [
+ 'ls_field' => 'OAuthConsumer',
+ 'ls_value' => $cmrAc->getConsumerKey(),
+ ],
+ 'lim' => 1,
+ 'flags' => \LogEventsList::NO_EXTRA_USER_LINKS,
+ ] );
+
+ $lang = $this->getLanguage();
+ $oauthVersionMessage = $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ?
+ wfMessage( 'mwoauth-oauth-version-2' )->text() :
+ wfMessage( 'mwoauth-oauth-version-1' )->text();
+ $data = [
+ 'mwoauthconsumerregistration-name' => $cmrAc->escapeForHtml( $cmrAc->getNameAndVersion() ),
+ 'mwoauth-oauth-version' => $cmrAc->escapeForHtml( $oauthVersionMessage ),
+ // Messages: mwoauth-consumer-stage-proposed, mwoauth-consumer-stage-rejected,
+ // mwoauth-consumer-stage-expired, mwoauth-consumer-stage-approved,
+ // mwoauth-consumer-stage-disabled
+ 'mwoauthconsumerregistration-stage' =>
+ $this->msg( "mwoauth-consumer-stage-$stageKey" )->escaped(),
+ 'mwoauthconsumerregistration-description' => $cmrAc->escapeForHtml(
+ $cmrAc->get( 'description', function ( $s ) use ( $lang ) {
+ return $lang->truncateForVisual( $s, 10024 );
+ } )
+ ),
+ 'mwoauthconsumerregistration-email' => $cmrAc->escapeForHtml( $cmrAc->getEmail() ),
+ 'mwoauthconsumerregistration-consumerkey' => $cmrAc->escapeForHtml( $cmrAc->getConsumerKey() ),
+ 'mwoauthconsumerregistration-lastchange' => $logHtml,
+ ];
+
+ $r = "<li class='mw-mwoauthconsumerregistration-{$encStageKey}'>";
+ $r .= "<span>$time (<strong>{$links}</strong>)</span>";
+ $r .= "<table class='mw-mwoauthconsumerregistration-body' " .
+ "cellspacing='1' cellpadding='3' border='1' width='100%'>";
+ foreach ( $data as $msg => $encValue ) {
+ $r .= '<tr>' .
+ '<td><strong>' . $this->msg( $msg )->escaped() . '</strong></td>' .
+ '<td width=\'90%\'>' . $encValue . '</td>' .
+ '</tr>';
+ }
+ $r .= '</table>';
+
+ $r .= '</li>';
+
+ return $r;
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+}
diff --git a/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthListConsumers.php b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthListConsumers.php
new file mode 100644
index 00000000..57a36176
--- /dev/null
+++ b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthListConsumers.php
@@ -0,0 +1,408 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\SpecialPages;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use Html;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAccessControl;
+use MediaWiki\Extensions\OAuth\Frontend\Pagers\ListConsumersPager;
+use MediaWiki\Extensions\OAuth\Frontend\UIUtils;
+use MediaWiki\MediaWikiServices;
+use OOUI\HtmlSnippet;
+use SpecialPage;
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * Special page for listing the queue of consumer requests and managing
+ * their approval/rejection and also for listing approved/disabled consumers
+ */
+class SpecialMWOAuthListConsumers extends \SpecialPage {
+ public function __construct() {
+ parent::__construct( 'OAuthListConsumers' );
+ }
+
+ public function execute( $par ) {
+ $this->setHeaders();
+ $this->addHelpLink( 'Help:OAuth' );
+
+ // Format is Special:OAuthListConsumers[/list|/view/[<consumer key>]]
+ $navigation = explode( '/', $par );
+ $type = $navigation[0] ?? null;
+ $consumerKey = $navigation[1] ?? null;
+
+ $this->showConsumerListForm();
+
+ switch ( $type ) {
+ case 'view':
+ $this->showConsumerInfo( $consumerKey );
+ break;
+ default:
+ $this->showConsumerList();
+ break;
+ }
+
+ $this->getOutput()->addModuleStyles( 'ext.MWOAuth.styles' );
+ }
+
+ /**
+ * Show the form to approve/reject/disable/re-enable consumers
+ *
+ * @param string $consumerKey
+ * @throws \PermissionsError
+ */
+ protected function showConsumerInfo( $consumerKey ) {
+ $user = $this->getUser();
+ $out = $this->getOutput();
+
+ if ( !$consumerKey ) {
+ $out->addWikiMsg( 'mwoauth-missing-consumer-key' );
+ }
+
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromKey( $dbr, $consumerKey ), $this->getContext() );
+ if ( !$cmrAc ) {
+ $out->addWikiMsg( 'mwoauth-invalid-consumer-key' );
+ return;
+ } elseif ( $cmrAc->getDeleted() && !$user->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ throw new \PermissionsError( 'mwoauthviewsuppressed' );
+ }
+
+ $grants = $cmrAc->getGrants();
+ if ( $grants === [ 'mwoauth-authonly' ] || $grants === [ 'mwoauth-authonlyprivate' ] ) {
+ $s = $this->msg( 'grant-' . $grants[0] )->plain() . "\n";
+ } else {
+ $s = \MWGrants::getGrantsWikiText( $grants, $this->getLanguage() );
+ if ( $s == '' ) {
+ $s = $this->msg( 'mwoauthlistconsumers-basicgrantsonly' )->plain();
+ } else {
+ $s .= "\n";
+ }
+ }
+
+ $stageKey = Consumer::$stageNames[$cmrAc->getDAO()->getStage()];
+ $data = [
+ 'mwoauthlistconsumers-name' => $cmrAc->getName(),
+ 'mwoauthlistconsumers-version' => $cmrAc->getVersion(),
+ 'mwoauth-oauth-version' => $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2
+ ? $this->msg( 'mwoauth-oauth-version-2' )
+ : $this->msg( 'mwoauth-oauth-version-1' ),
+ 'mwoauthlistconsumers-user' => $cmrAc->getUserName(),
+ 'mwoauthlistconsumers-status' => $this->msg( "mwoauthlistconsumers-status-$stageKey" ),
+ 'mwoauthlistconsumers-description' => $cmrAc->getDescription(),
+ 'mwoauthlistconsumers-wiki' => $cmrAc->getWikiName(),
+ 'mwoauthlistconsumers-callbackurl' => $cmrAc->getCallbackUrl(),
+ 'mwoauthlistconsumers-callbackisprefix' => $cmrAc->getCallbackIsPrefix() ?
+ $this->msg( 'htmlform-yes' ) : $this->msg( 'htmlform-no' ),
+ ];
+
+ if ( $grants !== [ 'basic' ] ) {
+ $data[ 'mwoauthlistconsumers-grants' ] = new HtmlSnippet( $out->parseInlineAsInterface( $s ) );
+ }
+
+ $out->addHTML( UIUtils::generateInfoTable( $data, $this->getContext() ) );
+
+ $this->addNavigationSubtitle( $cmrAc );
+
+ if ( Utils::isCentralWiki() ) {
+ // Show all of the status updates
+ $logPage = new \LogPage( 'mwoauthconsumer' );
+ $out->addHTML( \Xml::element( 'h2', null, $logPage->getName()->text() ) );
+ \LogEventsList::showLogExtract( $out, 'mwoauthconsumer', '', '', [
+ 'conds' => [
+ 'ls_field' => 'OAuthConsumer',
+ 'ls_value' => $cmrAc->getConsumerKey(),
+ ],
+ 'flags' => \LogEventsList::NO_EXTRA_USER_LINKS,
+ ] );
+ }
+ }
+
+ /**
+ * Show a form for the paged list of consumers
+ */
+ protected function showConsumerListForm() {
+ $form = \HTMLForm::factory( 'ooui',
+ [
+ 'name' => [
+ 'name' => 'name',
+ 'type' => 'text',
+ 'label-message' => 'mwoauth-consumer-name',
+ 'required' => false,
+ ],
+ 'publisher' => [
+ 'name' => 'publisher',
+ 'type' => 'text',
+ 'label-message' => 'mwoauth-consumer-user',
+ 'required' => false
+ ],
+ 'stage' => [
+ 'name' => 'stage',
+ 'type' => 'select',
+ 'label-message' => 'mwoauth-consumer-stage',
+ 'options' => [
+ $this->msg( 'mwoauth-consumer-stage-any' )->escaped() => -1,
+ $this->msg( 'mwoauth-consumer-stage-proposed' )->escaped()
+ => Consumer::STAGE_PROPOSED,
+ $this->msg( 'mwoauth-consumer-stage-approved' )->escaped()
+ => Consumer::STAGE_APPROVED,
+ $this->msg( 'mwoauth-consumer-stage-rejected' )->escaped()
+ => Consumer::STAGE_REJECTED,
+ $this->msg( 'mwoauth-consumer-stage-disabled' )->escaped()
+ => Consumer::STAGE_DISABLED,
+ $this->msg( 'mwoauth-consumer-stage-expired' )->escaped()
+ => Consumer::STAGE_EXPIRED
+ ],
+ 'default' => Consumer::STAGE_APPROVED,
+ 'required' => false
+ ]
+ ],
+ $this->getContext()
+ );
+ $form->setAction( $this->getPageTitle()->getFullURL() ); // always go back to listings
+ $form->setSubmitCallback( function () {
+ return false;
+ } );
+ $form->setMethod( 'get' );
+ $form->setSubmitTextMsg( 'go' );
+ $form->setWrapperLegendMsg( 'mwoauthlistconsumers-legend' );
+ $form->show();
+ }
+
+ /**
+ * Show a paged list of consumers with links to details
+ * @suppress SecurityCheck-XSS For getNavigationBar, see T201811 for more information
+ */
+ protected function showConsumerList() {
+ $request = $this->getRequest();
+
+ $name = $request->getVal( 'name', '' );
+ $stage = $request->getInt( 'stage', Consumer::STAGE_APPROVED );
+ if ( $request->getVal( 'publisher', '' ) !== '' ) {
+ $centralId = Utils::getCentralIdFromUserName( $request->getVal( 'publisher' ) );
+ } else {
+ $centralId = null;
+ }
+
+ $pager = new ListConsumersPager( $this, [], $name, $centralId, $stage );
+ if ( $pager->getNumRows() ) {
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ $this->getOutput()->addHTML( $pager->getBody() );
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ } else {
+ // Messages: mwoauthlistconsumers-none-proposed, mwoauthlistconsumers-none-rejected,
+ // mwoauthlistconsumers-none-expired, mwoauthlistconsumers-none-approved,
+ // mwoauthlistconsumers-none-disabled
+ $this->getOutput()->addWikiMsg( "mwoauthlistconsumers-none" );
+ }
+ # Every 30th view, prune old deleted items
+ if ( 0 == mt_rand( 0, 29 ) ) {
+ Utils::runAutoMaintenance( Utils::getCentralDB( DB_MASTER ) );
+ }
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( DBConnRef $db, $row ) {
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromRow( $db, $row ), $this->getContext() );
+
+ $cmrKey = $cmrAc->getConsumerKey();
+ $stageKey = Consumer::$stageNames[$cmrAc->getStage()];
+
+ $links = [];
+ $links[] = \Linker::linkKnown(
+ $this->getPageTitle( "view/{$cmrKey}" ),
+ $this->msg( 'mwoauthlistconsumers-view' )->escaped(),
+ [],
+ $this->getRequest()->getValues( 'name', 'publisher', 'stage' ) // stick
+ );
+ if ( $this->getUser()->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ $links[] = \Linker::linkKnown(
+ \SpecialPage::getTitleFor( 'OAuthManageConsumers', $cmrKey ),
+ $this->msg( 'mwoauthmanageconsumers-review' )->escaped()
+ );
+ }
+ $links = $this->getLanguage()->pipeList( $links );
+
+ $encStageKey = htmlspecialchars( $stageKey ); // sanity
+ $r = "<li class=\"mw-mwoauthlistconsumers-{$encStageKey}\">";
+
+ $name = $cmrAc->getNameAndVersion();
+ $r .= '<strong>' . $cmrAc->escapeForHtml( $name ) . '</strong> ' . $this->msg( 'parentheses' )
+ ->rawParams( "<strong>{$links}</strong>" )->escaped();
+
+ $lang = $this->getLanguage();
+ $data = [
+ 'mwoauth-oauth-version' => $cmrAc->escapeForHtml(
+ $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2
+ ? $this->msg( 'mwoauth-oauth-version-2' )
+ : $this->msg( 'mwoauth-oauth-version-1' )
+ ),
+ 'mwoauthlistconsumers-user' => $cmrAc->escapeForHtml( $cmrAc->getUserName() ),
+ 'mwoauthlistconsumers-description' => $cmrAc->escapeForHtml(
+ $cmrAc->get( 'description', function ( $s ) use ( $lang ) {
+ return $lang->truncateForVisual( $s, 10024 );
+ } )
+ ),
+ 'mwoauthlistconsumers-wiki' => $cmrAc->escapeForHtml( $cmrAc->getWikiName() ),
+ 'mwoauthlistconsumers-status' =>
+ $this->msg( "mwoauthlistconsumers-status-$stageKey" )->escaped(),
+ ];
+
+ foreach ( $data as $msg => $encValue ) {
+ $r .= '<p>' . $this->msg( $msg )->escaped() . ': ' . $encValue . '</p>';
+ }
+
+ $rcUrl = SpecialPage::getTitleFor( 'Recentchanges' )
+ ->getFullURL( [ 'tagfilter' => Utils::getTagName( $cmrAc->getId() ) ] );
+ $rcLink = Html::element( 'a', [ 'href' => $rcUrl ],
+ $this->msg( 'mwoauthlistconsumers-rclink' )->plain() );
+ $r .= '<p>' . $rcLink . '</p>';
+
+ $r .= '</li>';
+
+ return $r;
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+
+ /**
+ * @param ConsumerAccessControl $cmrAc
+ * @throws \MWException
+ */
+ private function addNavigationSubtitle( ConsumerAccessControl $cmrAc ): void {
+ $user = $this->getUser();
+ $centralUserId = Utils::getCentralIdFromLocalUser( $user );
+ $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+ $consumer = $cmrAc->getDAO();
+
+ $siteLinks = array_merge(
+ $this->updateLink( $cmrAc, $centralUserId, $linkRenderer ),
+ $this->manageConsumerLink( $consumer, $user, $linkRenderer ),
+ $this->manageMyGrantsLink( $consumer, $centralUserId, $linkRenderer )
+ );
+
+ if ( $siteLinks ) {
+ $links = $this->getLanguage()->pipeList( $siteLinks );
+ $this->getOutput()->setSubtitle(
+ "<strong>" . $this->msg( 'mwoauthlistconsumers-navigation' )->escaped() .
+ "</strong> [{$links}]" );
+ }
+ }
+
+ /**
+ * @param ConsumerAccessControl $cmrAc
+ * @param int $centralUserId Add update link for this user id, if they can update the consumer
+ * @param \MediaWiki\Linker\LinkRenderer $linkRenderer
+ * @return array
+ * @throws \MWException
+ */
+ private function updateLink(
+ ConsumerAccessControl $cmrAc, $centralUserId,
+ \MediaWiki\Linker\LinkRenderer $linkRenderer
+ ): array {
+ if ( Utils::isCentralWiki() && $cmrAc->getDAO()->getUserId() === $centralUserId ) {
+ return [
+ $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'OAuthConsumerRegistration',
+ 'update/' . $cmrAc->getDAO()->getConsumerKey() ),
+ $this->msg( 'mwoauthlistconsumers-update-link' )->text() )
+ ];
+ }
+
+ return [];
+ }
+
+ /**
+ * @param Consumer $consumer
+ * @param \User $user
+ * @param \MediaWiki\Linker\LinkRenderer $linkRenderer
+ * @return array
+ * @throws \MWException
+ */
+ private function manageConsumerLink(
+ Consumer $consumer, \User $user, \MediaWiki\Linker\LinkRenderer $linkRenderer
+ ): array {
+ if ( Utils::isCentralWiki() && $user->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ return [
+ $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'OAuthManageConsumers',
+ $consumer->getConsumerKey() ),
+ $this->msg( 'mwoauthlistconsumers-manage-link' )->text() )
+ ];
+ }
+
+ return [];
+ }
+
+ /**
+ * @param Consumer $consumer
+ * @param int $centralUserId Add link to manage grants for this user, if they've granted this
+ * consumer
+ * @param \MediaWiki\Linker\LinkRenderer $linkRenderer
+ * @return array
+ * @throws \MWException
+ */
+ private function manageMyGrantsLink(
+ Consumer $consumer, $centralUserId, \MediaWiki\Linker\LinkRenderer $linkRenderer
+ ): array {
+ $acceptance = $this->userGrantedAcceptance( $consumer, $centralUserId );
+ if ( $acceptance !== false ) {
+ return [
+ $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'OAuthManageMyGrants',
+ 'update/' . $acceptance->getId() ),
+ $this->msg( 'mwoauthlistconsumers-grants-link' )->text() )
+ ];
+ }
+
+ return [];
+ }
+
+ /**
+ * @param Consumer $consumer
+ * @param int $centralUserId UserId to retrieve the grants for
+ * @return bool|ConsumerAcceptance
+ */
+ private function userGrantedAcceptance( Consumer $consumer, $centralUserId ) {
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $wikiSpecificGrant =
+ ConsumerAcceptance::newFromUserConsumerWiki(
+ $dbr, $centralUserId, $consumer, wfWikiId() );
+
+ $allWikiGrant = ConsumerAcceptance::newFromUserConsumerWiki(
+ $dbr, $centralUserId, $consumer, '*' );
+
+ if ( $wikiSpecificGrant !== false ) {
+ return $wikiSpecificGrant;
+ }
+ if ( $allWikiGrant !== false ) {
+ return $allWikiGrant;
+ }
+ return false;
+ }
+}
diff --git a/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageConsumers.php b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageConsumers.php
new file mode 100644
index 00000000..85083221
--- /dev/null
+++ b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageConsumers.php
@@ -0,0 +1,521 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\SpecialPages;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAccessControl;
+use MediaWiki\Extensions\OAuth\Control\ConsumerSubmitControl;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+use MediaWiki\Extensions\OAuth\Frontend\Pagers\ManageConsumersPager;
+use MediaWiki\Extensions\OAuth\Frontend\UIUtils;
+use OOUI\HtmlSnippet;
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * Special page for listing the queue of consumer requests and managing
+ * their approval/rejection and also for listing approved/disabled consumers
+ */
+class SpecialMWOAuthManageConsumers extends \SpecialPage {
+ /** @var bool|int An Consumer::STAGE_* constant on queue/list subpages, false otherwise */
+ protected $stage = false;
+ /** @var string A stage key from Consumer::$stageNames */
+ protected $stageKey;
+
+ /**
+ * Stages which are shown in a queue (they are in an actionable state and can form a backlog)
+ * @var array
+ */
+ public static $queueStages = [ Consumer::STAGE_PROPOSED,
+ Consumer::STAGE_REJECTED, Consumer::STAGE_EXPIRED ];
+
+ /**
+ * Stages which cannot form a backlog and are shown in a list
+ * @var array
+ */
+ public static $listStages = [ Consumer::STAGE_APPROVED,
+ Consumer::STAGE_DISABLED ];
+
+ public function __construct() {
+ parent::__construct( 'OAuthManageConsumers', 'mwoauthmanageconsumer' );
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ public function execute( $par ) {
+ global $wgMWOAuthReadOnly;
+
+ $user = $this->getUser();
+
+ $this->setHeaders();
+ $this->getOutput()->disallowUserJs();
+ $this->addHelpLink( 'Help:OAuth' );
+
+ if ( !$user->isLoggedIn() ) {
+ $this->getOutput()->addWikiMsg( 'mwoauthmanageconsumers-notloggedin' );
+ return;
+ } elseif ( !$user->isAllowed( 'mwoauthmanageconsumer' ) ) {
+ throw new \PermissionsError( 'mwoauthmanageconsumer' );
+ }
+
+ if ( $wgMWOAuthReadOnly ) {
+ throw new \ErrorPageError( 'mwoauth-error', 'mwoauth-db-readonly' );
+ }
+
+ // Format is Special:OAuthManageConsumers[/<stage>|/<consumer key>]
+ // B/C format is Special:OAuthManageConsumers/<stage>/<consumer key>
+ $consumerKey = null;
+ $navigation = explode( '/', $par );
+ if ( count( $navigation ) === 2 ) {
+ $this->stage = false;
+ $consumerKey = $navigation[1];
+ } elseif ( count( $navigation ) === 1 && $navigation[0] ) {
+ $this->stage = array_search( $navigation[0], Consumer::$stageNames, true );
+ if ( $this->stage !== false ) {
+ $consumerKey = null;
+ $this->stageKey = $navigation[0];
+ } else {
+ $consumerKey = $navigation[0];
+ }
+ }
+
+ if ( $consumerKey ) {
+ $this->handleConsumerForm( $consumerKey );
+ } elseif ( $this->stage !== false ) {
+ $this->showConsumerList();
+ } else {
+ $this->showMainHub();
+ }
+
+ $this->addQueueSubtitleLinks( $consumerKey );
+
+ $this->getOutput()->addModuleStyles( 'ext.MWOAuth.styles' );
+ }
+
+ /**
+ * Show other sub-queue links. Grey out the current one.
+ * When viewing a request, show them all and a link to current consumer view.
+ *
+ * @param string $consumerKey
+ * @return void
+ */
+ protected function addQueueSubtitleLinks( $consumerKey ) {
+ $listLinks = [];
+ foreach ( self::$queueStages as $stage ) {
+ $stageKey = Consumer::$stageNames[$stage];
+ if ( $consumerKey || $this->stageKey !== $stageKey ) {
+ $listLinks[] = \Linker::linkKnown(
+ $this->getPageTitle( $stageKey ),
+ // Messages: mwoauthmanageconsumers-showproposed,
+ // mwoauthmanageconsumers-showrejected, mwoauthmanageconsumers-showexpired,
+ $this->msg( 'mwoauthmanageconsumers-show' . $stageKey )->escaped() );
+ } else {
+ $listLinks[] = $this->msg( 'mwoauthmanageconsumers-show' . $stageKey )->escaped();
+ }
+ }
+
+ if ( $consumerKey ) {
+ $consumerViewLink = "[" . \Linker::linkKnown(
+ \SpecialPage::getTitleFor( 'OAuthListConsumers', "view/$consumerKey" ),
+ $this->msg( 'mwoauthconsumer-consumer-view' )->escaped() ) . "]";
+ } else {
+ $consumerViewLink = '';
+ }
+
+ $linkHtml = $this->getLanguage()->pipeList( $listLinks );
+
+ $viewall = $this->msg( 'parentheses' )->rawParams( \Linker::linkKnown(
+ $this->getPageTitle(),
+ $this->msg( 'mwoauthmanageconsumers-main' )->escaped()
+ ) )->escaped();
+
+ $this->getOutput()->setSubtitle(
+ "<strong>" . $this->msg( 'mwoauthmanageconsumers-type' )->escaped() .
+ "</strong> [{$linkHtml}] {$consumerViewLink} <strong>{$viewall}</strong>" );
+ }
+
+ /**
+ * Show the links to all the queues and how many requests are in each.
+ * Also show the list of enabled and disabled consumers and how many there are of each.
+ *
+ * @return void
+ */
+ protected function showMainHub() {
+ $keyStageMapQ = array_intersect( array_flip( Consumer::$stageNames ),
+ self::$queueStages );
+ $keyStageMapL = array_intersect( array_flip( Consumer::$stageNames ),
+ self::$listStages );
+
+ $out = $this->getOutput();
+
+ $out->addWikiMsg( 'mwoauthmanageconsumers-maintext' );
+
+ $counts = Utils::getConsumerStateCounts( Utils::getCentralDB( DB_REPLICA ) );
+
+ $out->wrapWikiMsg( "<p><strong>$1</strong></p>", 'mwoauthmanageconsumers-queues' );
+ $out->addHTML( '<ul>' );
+ foreach ( $keyStageMapQ as $stageKey => $stage ) {
+ $tag = ( $stage === Consumer::STAGE_EXPIRED ) ? 'i' : 'b';
+ $out->addHTML(
+ '<li>' .
+ "<$tag>" .
+ \Linker::linkKnown(
+ $this->getPageTitle( $stageKey ),
+ // Messages: mwoauthmanageconsumers-q-proposed, mwoauthmanageconsumers-q-rejected,
+ // mwoauthmanageconsumers-q-expired
+ $this->msg( 'mwoauthmanageconsumers-q-' . $stageKey )->escaped()
+ ) .
+ "</$tag> [$counts[$stage]]" .
+ '</li>'
+ );
+ }
+ $out->addHTML( '</ul>' );
+
+ $out->wrapWikiMsg( "<p><strong>$1</strong></p>", 'mwoauthmanageconsumers-lists' );
+ $out->addHTML( '<ul>' );
+ foreach ( $keyStageMapL as $stageKey => $stage ) {
+ $out->addHTML(
+ '<li>' .
+ \Linker::linkKnown(
+ $this->getPageTitle( $stageKey ),
+ // Messages: mwoauthmanageconsumers-l-approved, mwoauthmanageconsumers-l-disabled
+ $this->msg( 'mwoauthmanageconsumers-l-' . $stageKey )->escaped()
+ ) .
+ " [$counts[$stage]]" .
+ '</li>'
+ );
+ }
+ $out->addHTML( '</ul>' );
+ }
+
+ /**
+ * Show the form to approve/reject/disable/re-enable consumers
+ *
+ * @param string $consumerKey
+ * @throws \PermissionsError
+ */
+ protected function handleConsumerForm( $consumerKey ) {
+ $user = $this->getUser();
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromKey( $dbr, $consumerKey ), $this->getContext() );
+ if ( !$cmrAc ) {
+ $this->getOutput()->addWikiMsg( 'mwoauth-invalid-consumer-key' );
+ return;
+ } elseif ( $cmrAc->getDeleted() && !$user->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ throw new \PermissionsError( 'mwoauthviewsuppressed' );
+ }
+ $startingStage = $cmrAc->getStage();
+ $pending = !in_array( $startingStage, [
+ Consumer::STAGE_APPROVED, Consumer::STAGE_DISABLED ] );
+
+ if ( $pending ) {
+ $opts = [
+ $this->msg( 'mwoauthmanageconsumers-approve' )->escaped() => 'approve',
+ $this->msg( 'mwoauthmanageconsumers-reject' )->escaped() => 'reject'
+ ];
+ if ( $this->getUser()->isAllowed( 'mwoauthsuppress' ) ) {
+ $msg = $this->msg( 'mwoauthmanageconsumers-rsuppress' )->escaped();
+ $opts["<strong>$msg</strong>"] = 'rsuppress';
+ }
+ } else {
+ $opts = [
+ $this->msg( 'mwoauthmanageconsumers-disable' )->escaped() => 'disable',
+ $this->msg( 'mwoauthmanageconsumers-reenable' )->escaped() => 'reenable'
+ ];
+ if ( $this->getUser()->isAllowed( 'mwoauthsuppress' ) ) {
+ $msg = $this->msg( 'mwoauthmanageconsumers-dsuppress' )->escaped();
+ $opts["<strong>$msg</strong>"] = 'dsuppress';
+ }
+ }
+
+ $dbw = Utils::getCentralDB( DB_MASTER ); // @TODO: lazy handle
+ $control = new ConsumerSubmitControl( $this->getContext(), [], $dbw );
+ $form = \HTMLForm::factory( 'ooui',
+ $control->registerValidators( [
+ 'info' => [
+ 'type' => 'info',
+ 'raw' => true,
+ 'default' => UIUtils::generateInfoTable(
+ $this->getInfoTableOptions( $cmrAc ),
+ $this->getContext()
+ ),
+ ],
+ 'action' => [
+ 'type' => 'radio',
+ 'label-message' => 'mwoauthmanageconsumers-action',
+ 'required' => true,
+ 'options' => $opts,
+ 'default' => '', // no validate on GET
+ ],
+ 'reason' => [
+ 'type' => 'text',
+ 'label-message' => 'mwoauthmanageconsumers-reason',
+ 'required' => true,
+ ],
+ 'consumerKey' => [
+ 'type' => 'hidden',
+ 'default' => $cmrAc->getConsumerKey(),
+ ],
+ 'changeToken' => [
+ 'type' => 'hidden',
+ 'default' => $cmrAc->getDAO()->getChangeToken( $this->getContext() ),
+ ],
+ ] ),
+ $this->getContext()
+ );
+ $form->setSubmitCallback(
+ function ( array $data, \IContextSource $context ) use ( $control ) {
+ $data['suppress'] = 0;
+ if ( $data['action'] === 'dsuppress' ) {
+ $data = [ 'action' => 'disable', 'suppress' => 1 ] + $data;
+ } elseif ( $data['action'] === 'rsuppress' ) {
+ $data = [ 'action' => 'reject', 'suppress' => 1 ] + $data;
+ }
+ $control->setInputParameters( $data );
+ return $control->submit();
+ }
+ );
+
+ $form->setWrapperLegendMsg( 'mwoauthmanageconsumers-confirm-legend' );
+ $form->setSubmitTextMsg( 'mwoauthmanageconsumers-confirm-submit' );
+ $form->addPreText(
+ $this->msg( 'mwoauthmanageconsumers-confirm-text' )->parseAsBlock() );
+
+ $status = $form->show();
+ if ( $status instanceof \Status && $status->isOK() ) {
+ /** @var Consumer $cmr */
+ // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
+ $cmr = $status->value['result'];
+ '@phan-var Consumer $cmr';
+ $oldStageKey = Consumer::$stageNames[$startingStage];
+ $newStageKey = Consumer::$stageNames[$cmr->getStage()];
+ // Messages: mwoauthmanageconsumers-success-approved, mwoauthmanageconsumers-success-rejected,
+ // mwoauthmanageconsumers-success-disabled
+ $this->getOutput()->addWikiMsg( "mwoauthmanageconsumers-success-$newStageKey" );
+ $returnTo = \Title::newFromText( 'Special:OAuthManageConsumers/' . $oldStageKey );
+ $this->getOutput()->addReturnTo( $returnTo, [],
+ // Messages: mwoauthmanageconsumers-linkproposed,
+ // mwoauthmanageconsumers-linkrejected, mwoauthmanageconsumers-linkexpired,
+ // mwoauthmanageconsumers-linkapproved, mwoauthmanageconsumers-linkdisabled
+ $this->msg( 'mwoauthmanageconsumers-link' . $oldStageKey )->text() );
+ } else {
+ $out = $this->getOutput();
+ // Show all of the status updates
+ $logPage = new \LogPage( 'mwoauthconsumer' );
+ $out->addHTML( \Xml::element( 'h2', null, $logPage->getName()->text() ) );
+ \LogEventsList::showLogExtract( $out, 'mwoauthconsumer', '', '', [
+ 'conds' => [
+ 'ls_field' => 'OAuthConsumer',
+ 'ls_value' => $cmrAc->getConsumerKey(),
+ ],
+ 'flags' => \LogEventsList::NO_EXTRA_USER_LINKS,
+ ] );
+ }
+ }
+
+ /**
+ * @param ConsumerAccessControl $cmrAc
+ * @return array
+ */
+ protected function getInfoTableOptions( $cmrAc ) {
+ $owner = $cmrAc->getUserName();
+ $lang = $this->getLanguage();
+
+ $link = \Linker::linkKnown(
+ $title = \SpecialPage::getTitleFor( 'OAuthListConsumers' ),
+ $this->msg( 'mwoauthmanageconsumers-search-publisher' )->escaped(),
+ [],
+ [ 'publisher' => $owner ]
+ );
+ $ownerLink = $cmrAc->escapeForHtml( $owner ) . ' ' .
+ $this->msg( 'parentheses' )->rawParams( $link )->escaped();
+ $ownerOnly = $cmrAc->getDAO()->getOwnerOnly();
+ $restrictions = $cmrAc->getRestrictions();
+
+ $options = [
+ // Messages: mwoauth-consumer-stage-proposed, mwoauth-consumer-stage-rejected,
+ // mwoauth-consumer-stage-expired, mwoauth-consumer-stage-approved,
+ // mwoauth-consumer-stage-disabled
+ 'mwoauth-consumer-stage' => $cmrAc->getDeleted()
+ ? $this->msg( 'mwoauth-consumer-stage-suppressed' )
+ : $this->msg( 'mwoauth-consumer-stage-' .
+ Consumer::$stageNames[$cmrAc->getStage()] ),
+ 'mwoauth-consumer-key' => $cmrAc->getConsumerKey(),
+ 'mwoauth-consumer-name' => new HtmlSnippet( $cmrAc->get( 'name', function ( $s ) {
+ $link = \Linker::linkKnown(
+ \SpecialPage::getTitleFor( 'OAuthListConsumers' ),
+ $this->msg( 'mwoauthmanageconsumers-search-name' )->escaped(),
+ [],
+ [ 'name' => $s ]
+ );
+ return htmlspecialchars( $s ) . ' ' .
+ $this->msg( 'parentheses' )->rawParams( $link )->escaped();
+ } ) ),
+ 'mwoauth-consumer-version' => $cmrAc->getVersion(),
+ 'mwoauth-oauth-version' => $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2
+ ? $this->msg( 'mwoauth-oauth-version-2' )
+ : $this->msg( 'mwoauth-oauth-version-1' ),
+ 'mwoauth-consumer-user' => new HtmlSnippet( $ownerLink ),
+ 'mwoauth-consumer-description' => $cmrAc->getDescription(),
+ 'mwoauth-consumer-owner-only-label' => $ownerOnly ?
+ $this->msg( 'mwoauth-consumer-owner-only', $owner ) : null,
+ 'mwoauth-consumer-callbackurl' => $ownerOnly ?
+ null : $cmrAc->getCallbackUrl(),
+ 'mwoauth-consumer-callbackisprefix' => $ownerOnly ?
+ null : ( $cmrAc->getCallbackIsPrefix() ?
+ $this->msg( 'htmlform-yes' ) : $this->msg( 'htmlform-no' ) ),
+ 'mwoauth-consumer-grantsneeded' => $cmrAc->get( 'grants',
+ function ( $grants ) use ( $lang ) {
+ return $lang->semicolonList( \MWGrants::grantNames( $grants, $lang ) );
+ } ),
+ 'mwoauth-consumer-email' => $cmrAc->getEmail(),
+ 'mwoauth-consumer-wiki' => $cmrAc->getWiki()
+ ];
+
+ // Add OAuth2 specific parameters
+ if ( $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ) {
+ /** @var ClientEntity $consumer */
+ $consumer = $cmrAc->getDAO();
+ $options += [
+ 'mwoauth-oauth2-is-confidential' => $consumer->isConfidential() ?
+ $this->msg( 'htmlform-yes' ) : $this->msg( 'htmlform-no' ),
+ 'mwoauth-oauth2-granttypes' => implode( ', ', array_map( function ( $grant ) {
+ $map = [
+ 'authorization_code' => 'mwoauth-oauth2-granttype-auth-code',
+ 'refresh_token' => 'mwoauth-oauth2-granttype-refresh-token',
+ 'client_credentials' => 'mwoauth-oauth2-granttype-client-credentials'
+ ];
+ return isset( $map[$grant] ) ? $this->msg( $map[$grant] ) : '';
+ }, $consumer->getAllowedGrants() ) )
+ ];
+ }
+
+ // Add optional parameters
+ $options += [
+ 'mwoauth-consumer-restrictions-json' => $restrictions instanceof \MWRestrictions ?
+ $restrictions->toJson( true ) : $restrictions,
+ 'mwoauth-consumer-rsakey' => $cmrAc->getRsaKey(),
+ ];
+
+ return $options;
+ }
+
+ /**
+ * Show a paged list of consumers with links to details
+ */
+ protected function showConsumerList() {
+ $pager = new ManageConsumersPager( $this, [], $this->stage );
+ if ( $pager->getNumRows() ) {
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ $this->getOutput()->addHTML( $pager->getBody() );
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ } else {
+ // Messages: mwoauthmanageconsumers-none-proposed, mwoauthmanageconsumers-none-rejected,
+ // mwoauthmanageconsumers-none-expired, mwoauthmanageconsumers-none-approved,
+ // mwoauthmanageconsumers-none-disabled
+ $this->getOutput()->addWikiMsg( "mwoauthmanageconsumers-none-{$this->stageKey}" );
+ }
+ # Every 30th view, prune old deleted items
+ if ( 0 == mt_rand( 0, 29 ) ) {
+ Utils::runAutoMaintenance( Utils::getCentralDB( DB_MASTER ) );
+ }
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( DBConnRef $db, $row ) {
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromRow( $db, $row ), $this->getContext() );
+
+ $cmrKey = $cmrAc->getConsumerKey();
+ $stageKey = Consumer::$stageNames[$cmrAc->getStage()];
+
+ $link = \Linker::linkKnown(
+ $this->getPageTitle( $cmrKey ),
+ $this->msg( 'mwoauthmanageconsumers-review' )->escaped()
+ );
+
+ $time = $this->getLanguage()->timeanddate(
+ wfTimestamp( TS_MW, $cmrAc->getRegistration() ), true );
+
+ $encStageKey = htmlspecialchars( $stageKey ); // sanity
+ $r = "<li class='mw-mwoauthmanageconsumers-{$encStageKey}'>";
+
+ $r .= $time . " (<strong>{$link}</strong>)";
+
+ // Show last log entry (@TODO: title namespace?)
+ // @TODO: inject DB
+ $logHtml = '';
+ \LogEventsList::showLogExtract( $logHtml, 'mwoauthconsumer', '', '', [
+ 'action' => Consumer::$stageActionNames[$cmrAc->getStage()],
+ 'conds' => [
+ 'ls_field' => 'OAuthConsumer',
+ 'ls_value' => $cmrAc->getConsumerKey(),
+ ],
+ 'lim' => 1,
+ 'flags' => \LogEventsList::NO_EXTRA_USER_LINKS,
+ ] );
+
+ $lang = $this->getLanguage();
+ $data = [
+ 'mwoauthmanageconsumers-name' => $cmrAc->escapeForHtml( $cmrAc->getNameAndVersion() ),
+ 'mwoauthmanageconsumers-user' => $cmrAc->escapeForHtml( $cmrAc->getUserName() ),
+ 'mwoauth-oauth-version' => $cmrAc->escapeForHtml(
+ $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ?
+ $this->msg( 'mwoauth-oauth-version-2' ) :
+ $this->msg( 'mwoauth-oauth-version-1' )
+ ),
+ 'mwoauthmanageconsumers-description' => $cmrAc->escapeForHtml(
+ $cmrAc->get( 'description', function ( $s ) use ( $lang ) {
+ return $lang->truncateForVisual( $s, 10024 );
+ } )
+ ),
+ 'mwoauthmanageconsumers-email' => $cmrAc->escapeForHtml( $cmrAc->getEmail() ),
+ 'mwoauthmanageconsumers-consumerkey' => $cmrAc->escapeForHtml( $cmrAc->getConsumerKey() ),
+ 'mwoauthmanageconsumers-lastchange' => $logHtml,
+ ];
+
+ $r .= "<table class='mw-mwoauthmanageconsumers-body' " .
+ "cellspacing='1' cellpadding='3' border='1' width='100%'>";
+ foreach ( $data as $msg => $encValue ) {
+ $r .= '<tr>' .
+ '<td><strong>' . $this->msg( $msg )->escaped() . '</strong></td>' .
+ '<td width=\'90%\'>' . $encValue . '</td>' .
+ '</tr>';
+ }
+ $r .= '</table>';
+
+ $r .= '</li>';
+
+ return $r;
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+}
diff --git a/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageMyGrants.php b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageMyGrants.php
new file mode 100644
index 00000000..62def2e3
--- /dev/null
+++ b/OAuth/src/Frontend/SpecialPages/SpecialMWOAuthManageMyGrants.php
@@ -0,0 +1,348 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend\SpecialPages;
+
+use Html;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAcceptanceAccessControl;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAcceptanceSubmitControl;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAccessControl;
+use MediaWiki\Extensions\OAuth\Frontend\Pagers\ManageMyGrantsPager;
+use MediaWiki\Extensions\OAuth\Frontend\UIUtils;
+use SpecialPage;
+use Wikimedia\Rdbms\DBConnRef;
+
+/**
+ * (c) Aaron Schulz 2013, GPL
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Special page for listing consumers this user granted access to and
+ * for manage the specific grants given or revoking access for the consumer
+ */
+class SpecialMWOAuthManageMyGrants extends SpecialPage {
+ private static $irrevocableGrants = null;
+
+ public function __construct() {
+ parent::__construct( 'OAuthManageMyGrants', 'mwoauthmanagemygrants' );
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ public function execute( $par ) {
+ global $wgMWOAuthReadOnly;
+
+ $user = $this->getUser();
+
+ $this->setHeaders();
+ $this->getOutput()->disallowUserJs();
+ $this->addHelpLink( 'Help:OAuth' );
+
+ if ( !$this->getUser()->isLoggedIn() ) {
+ throw new \UserNotLoggedIn();
+ }
+ if ( !$user->isAllowed( 'mwoauthmanagemygrants' ) ) {
+ throw new \PermissionsError( 'mwoauthmanagemygrants' );
+ }
+
+ // Format is Special:OAuthManageMyGrants[/list|/manage/<accesstoken>]
+ $navigation = explode( '/', $par );
+ $typeKey = $navigation[0] ?? null;
+ $acceptanceId = $navigation[1] ?? null;
+
+ if ( $wgMWOAuthReadOnly && in_array( $typeKey, [ 'update', 'revoke' ] ) ) {
+ throw new \ErrorPageError( 'mwoauth-error', 'mwoauth-db-readonly' );
+ }
+
+ switch ( $typeKey ) {
+ case 'update':
+ case 'revoke':
+ $this->handleConsumerForm( $acceptanceId, $typeKey );
+ break;
+ default:
+ $this->showConsumerList();
+ break;
+ }
+
+ $this->addSubtitleLinks( $acceptanceId );
+
+ $this->getOutput()->addModuleStyles( 'ext.MWOAuth.styles' );
+ }
+
+ /**
+ * Show navigation links
+ *
+ * @param string|null $acceptanceId
+ * @return void
+ */
+ protected function addSubtitleLinks( $acceptanceId ) {
+ $listLinks = [];
+
+ if ( $acceptanceId ) {
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $cmraAc = ConsumerAcceptance::newFromId( $dbr, $acceptanceId );
+ $listLinks[] = \Linker::linkKnown(
+ $this->getPageTitle(),
+ $this->msg( 'mwoauthmanagemygrants-showlist' )->escaped() );
+
+ if ( $cmraAc ) {
+ $cmrAc = Consumer::newFromId( $dbr, $cmraAc->getConsumerId() );
+ $consumerKey = $cmrAc->getConsumerKey();
+ $listLinks[] = \Linker::linkKnown(
+ \SpecialPage::getTitleFor( 'OAuthListConsumers', "view/$consumerKey" ),
+ $this->msg( 'mwoauthconsumer-application-view' )->escaped() );
+ }
+ } else {
+ $listLinks[] = $this->msg( 'mwoauthmanagemygrants-showlist' )->escaped();
+ }
+
+ $linkHtml = $this->getLanguage()->pipeList( $listLinks );
+
+ $this->getOutput()->setSubtitle(
+ "<strong>" . $this->msg( 'mwoauthmanagemygrants-navigation' )->escaped() .
+ "</strong> [{$linkHtml}]" );
+ }
+
+ /**
+ * Show the form to approve/reject/disable/re-enable consumers
+ *
+ * @param string $acceptanceId
+ * @param string $type One of (update,revoke)
+ * @throws \PermissionsError
+ */
+ protected function handleConsumerForm( $acceptanceId, $type ) {
+ $user = $this->getUser();
+ $lang = $this->getLanguage();
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+
+ $centralUserId = Utils::getCentralIdFromLocalUser( $user );
+ if ( !$centralUserId ) {
+ $this->getOutput()->addWikiMsg( 'badaccess-group0' );
+ return;
+ }
+
+ $cmraAc = ConsumerAcceptanceAccessControl::wrap(
+ ConsumerAcceptance::newFromId( $dbr, $acceptanceId ), $this->getContext() );
+ if ( !$cmraAc || $cmraAc->getUserId() !== $centralUserId ) {
+ $this->getOutput()->addHTML( $this->msg( 'mwoauth-invalid-access-token' )->escaped() );
+ return;
+ }
+
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromId( $dbr, $cmraAc->getConsumerId() ), $this->getContext() );
+ if ( $cmrAc->getDeleted() && !$user->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ throw new \PermissionsError( 'mwoauthviewsuppressed' );
+ }
+
+ $this->getOutput()->addModuleStyles( 'mediawiki.ui.button' );
+
+ $action = '';
+ if ( $this->getRequest()->getCheck( 'renounce' ) ) {
+ $action = 'renounce';
+ } elseif ( $this->getRequest()->getCheck( 'update' ) ) {
+ $action = 'update';
+ }
+
+ $data = [ 'action' => $action ];
+ $control = new ConsumerAcceptanceSubmitControl(
+ $this->getContext(), $data, $dbr, $cmraAc->getDAO()->getOAuthVersion()
+ );
+ $form = \HTMLForm::factory( 'ooui',
+ $control->registerValidators( [
+ 'info' => [
+ 'type' => 'info',
+ 'raw' => true,
+ 'default' => UIUtils::generateInfoTable( [
+ 'mwoauth-consumer-name' => $cmrAc->getNameAndVersion(),
+ 'mwoauth-consumer-user' => $cmrAc->getUserName(),
+ 'mwoauth-consumer-description' => $cmrAc->getDescription(),
+ 'mwoauthmanagemygrants-wikiallowed' => $cmraAc->getWikiName(),
+ ], $this->getContext() ),
+ ],
+ 'grants' => [
+ 'type' => 'checkmatrix',
+ 'label-message' => 'mwoauthmanagemygrants-applicablegrantsallowed',
+ 'columns' => [
+ $this->msg( 'mwoauthmanagemygrants-grantaccept' )->escaped() => 'grant'
+ ],
+ 'rows' => array_combine(
+ array_map( 'MWGrants::getGrantsLink', $cmrAc->getGrants() ),
+ $cmrAc->getGrants()
+ ),
+ 'default' => array_map(
+ function ( $g ) {
+ return "grant-$g";
+ },
+ $cmraAc->getGrants()
+ ),
+ 'tooltips' => [
+ \MWGrants::getGrantsLink( 'basic' ) =>
+ $this->msg( 'mwoauthmanagemygrants-basic-tooltip' )->text(),
+ \MWGrants::getGrantsLink( 'mwoauth-authonly' ) =>
+ $this->msg( 'mwoauthmanagemygrants-authonly-tooltip' )->text(),
+ \MWGrants::getGrantsLink( 'mwoauth-authonlyprivate' ) =>
+ $this->msg( 'mwoauthmanagemygrants-authonly-tooltip' )->text(),
+ ],
+ 'force-options-on' => array_map(
+ function ( $g ) {
+ return "grant-$g";
+ },
+ ( $type === 'revoke' )
+ ? array_merge( \MWGrants::getValidGrants(), self::irrevocableGrants() )
+ : self::irrevocableGrants()
+ ),
+ 'validation-callback' => null // different format
+ ],
+ 'acceptanceId' => [
+ 'type' => 'hidden',
+ 'default' => $cmraAc->getId(),
+ ]
+ ] ),
+ $this->getContext()
+ );
+ $form->setSubmitCallback(
+ function ( array $data, \IContextSource $context ) use ( $action, $cmraAc ) {
+ $data['action'] = $action;
+ $data['grants'] = \FormatJson::encode( // adapt form to controller
+ preg_replace( '/^grant-/', '', $data['grants'] ) );
+
+ $dbw = Utils::getCentralDB( DB_MASTER );
+ $control = new ConsumerAcceptanceSubmitControl(
+ $context, $data, $dbw, $cmraAc->getDAO()->getOAuthVersion()
+ );
+ return $control->submit();
+ }
+ );
+
+ $form->setWrapperLegendMsg( 'mwoauthmanagemygrants-confirm-legend' );
+ $form->suppressDefaultSubmit();
+ if ( $type === 'revoke' ) {
+ $form->addButton( [
+ 'name' => 'renounce',
+ 'value' => $this->msg( 'mwoauthmanagemygrants-renounce' )->text(),
+ 'flags' => [ 'primary', 'destructive' ],
+ ] );
+ } else {
+ $form->addButton( [
+ 'name' => 'update',
+ 'value' => $this->msg( 'mwoauthmanagemygrants-update' )->text(),
+ 'flags' => [ 'primary', 'progressive' ],
+ ] );
+ }
+ $form->addPreText(
+ $this->msg( "mwoauthmanagemygrants-$type-text" )->parseAsBlock() );
+
+ $status = $form->show();
+ if ( $status instanceof \Status && $status->isOK() ) {
+ // Messages: mwoauthmanagemygrants-success-update, mwoauthmanagemygrants-success-renounce
+ $this->getOutput()->addWikiMsg( "mwoauthmanagemygrants-success-$action" );
+ }
+ }
+
+ /**
+ * Show a paged list of consumers with links to details
+ *
+ * @return void
+ */
+ protected function showConsumerList() {
+ $this->getOutput()->addWikiMsg( 'mwoauthmanagemygrants-text' );
+
+ $centralUserId = Utils::getCentralIdFromLocalUser( $this->getUser() );
+ $pager = new ManageMyGrantsPager( $this, [], $centralUserId );
+ if ( $pager->getNumRows() ) {
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ $this->getOutput()->addHTML( $pager->getBody() );
+ $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ } else {
+ $this->getOutput()->addWikiMsg( "mwoauthmanagemygrants-none" );
+ }
+ }
+
+ /**
+ * @param DBConnRef $db
+ * @param \stdClass $row
+ * @return string
+ */
+ public function formatRow( DBConnRef $db, $row ) {
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromRow( $db, $row ), $this->getContext() );
+ $cmraAc = ConsumerAcceptanceAccessControl::wrap(
+ ConsumerAcceptance::newFromRow( $db, $row ), $this->getContext() );
+
+ $links = [];
+ if ( array_diff( $cmrAc->getGrants(), self::irrevocableGrants() ) ) {
+ $links[] = \Linker::linkKnown(
+ $this->getPageTitle( 'update/' . $cmraAc->getId() ),
+ $this->msg( 'mwoauthmanagemygrants-review' )->escaped()
+ );
+ }
+ $links[] = \Linker::linkKnown(
+ $this->getPageTitle( 'revoke/' . $cmraAc->getId() ),
+ $this->msg( 'mwoauthmanagemygrants-revoke' )->escaped()
+ );
+ $reviewLinks = $this->getLanguage()->pipeList( $links );
+
+ $encName = $cmrAc->escapeForHtml( $cmrAc->getNameAndVersion() );
+
+ $r = '<li class="mw-mwoauthmanagemygrants-list-item">';
+ $r .= "<strong dir='ltr'>{$encName}</strong> (<strong>$reviewLinks</strong>)";
+ $data = [
+ 'mwoauthmanagemygrants-user' => $cmrAc->getUserName(),
+ 'mwoauthmanagemygrants-wikiallowed' => $cmraAc->getWikiName(),
+ ];
+
+ foreach ( $data as $msg => $val ) {
+ $r .= '<p>' . $this->msg( $msg )->escaped() . ' ' . $cmrAc->escapeForHtml( $val ) . '</p>';
+ }
+
+ $editsUrl = SpecialPage::getTitleFor( 'Contributions', $this->getUser()->getName() )
+ ->getFullURL( [ 'tagfilter' => Utils::getTagName( $cmrAc->getId() ) ] );
+ $editsLink = Html::element( 'a', [ 'href' => $editsUrl ],
+ $this->msg( 'mwoauthmanagemygrants-editslink', $this->getUser() )->text() );
+ $r .= '<p>' . $editsLink . '</p>';
+ $actionsUrl = SpecialPage::getTitleFor( 'Log' )->getFullURL( [
+ 'user' => $this->getUser()->getName(),
+ 'tagfilter' => Utils::getTagName( $cmrAc->getId() ),
+ ] );
+ $actionsLink = Html::element( 'a', [ 'href' => $actionsUrl ],
+ $this->msg( 'mwoauthmanagemygrants-actionslink', $this->getUser() )->text() );
+ $r .= '<p>' . $actionsLink . '</p>';
+
+ $r .= '</li>';
+
+ return $r;
+ }
+
+ private static function irrevocableGrants() {
+ if ( self::$irrevocableGrants === null ) {
+ self::$irrevocableGrants = array_merge(
+ \MWGrants::getHiddenGrants(),
+ [ 'mwoauth-authonly', 'mwoauth-authonlyprivate' ]
+ );
+ }
+ return self::$irrevocableGrants;
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+}
diff --git a/OAuth/src/Frontend/UIHooks.php b/OAuth/src/Frontend/UIHooks.php
new file mode 100644
index 00000000..5d6b2602
--- /dev/null
+++ b/OAuth/src/Frontend/UIHooks.php
@@ -0,0 +1,241 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend;
+
+use HTMLForm;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Control\ConsumerAccessControl;
+use MediaWiki\Extensions\OAuth\Control\ConsumerSubmitControl;
+use MediaWiki\Extensions\OAuth\Frontend\SpecialPages\SpecialMWOAuthConsumerRegistration;
+use MediaWiki\Extensions\OAuth\Frontend\SpecialPages\SpecialMWOAuthManageConsumers;
+use SpecialPage;
+
+/**
+ * Class containing GUI even handler functions for an OAuth environment
+ */
+class UIHooks {
+
+ /**
+ * @param \User $user
+ * @param array &$preferences
+ * @return bool
+ * @throws \MWException
+ */
+ public static function onGetPreferences( $user, &$preferences ) {
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $conds = [
+ 'oaac_consumer_id = oarc_id',
+ 'oaac_user_id' => Utils::getCentralIdFromLocalUser( $user ),
+ ];
+ if ( !$user->isAllowed( 'mwoauthviewsuppressed' ) ) {
+ $conds['oarc_deleted'] = 0;
+ }
+ $count = $dbr->selectField(
+ [ 'oauth_accepted_consumer', 'oauth_registered_consumer' ],
+ 'COUNT(*)',
+ $conds,
+ __METHOD__
+ );
+
+ $control = new \OOUI\ButtonWidget( [
+ 'href' => SpecialPage::getTitleFor( 'OAuthManageMyGrants' )->getLinkURL(),
+ 'label' => wfMessage( 'mwoauth-prefs-managegrantslink' )->numParams( $count )->text()
+ ] );
+
+ $prefInsert = [ 'mwoauth-prefs-managegrants' =>
+ [
+ 'section' => 'personal/info',
+ 'label-message' => 'mwoauth-prefs-managegrants',
+ 'type' => 'info',
+ 'raw' => true,
+ 'default' => (string)$control
+ ],
+ ];
+
+ if ( array_key_exists( 'usergroups', $preferences ) ) {
+ $preferences = wfArrayInsertAfter( $preferences, $prefInsert, 'usergroups' );
+ } else {
+ $preferences += $prefInsert;
+ }
+
+ return true;
+ }
+
+ /**
+ * Override MediaWiki namespace for a message
+ * @param string $title Message name (no prefix)
+ * @param string &$message Message wikitext
+ * @param string $code Language code
+ * @return bool false if we replaced $message
+ */
+ public static function onMessagesPreLoad( $title, &$message, $code ) {
+ // Quick fail check
+ if ( substr( $title, 0, 15 ) !== 'Tag-OAuth_CID:_' ) {
+ return true;
+ }
+
+ // More expensive check
+ if ( !preg_match( '!^Tag-OAuth_CID:_(\d+)((?:-description)?)(?:/|$)!', $title, $m ) ) {
+ return true;
+ }
+
+ // Put the correct language in the context, so that later uses of $context->msg() will use it
+ $context = new \DerivativeContext( \RequestContext::getMain() );
+ $context->setLanguage( $code );
+
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $cmrAc = ConsumerAccessControl::wrap(
+ Consumer::newFromId( $dbr, $m[1] ), $context
+ );
+ if ( !$cmrAc ) {
+ // Invalid consumer, skip it
+ return true;
+ }
+
+ if ( $m[2] ) {
+ $message = $cmrAc->escapeForWikitext( $cmrAc->getDescription() );
+ } else {
+ $target = \SpecialPage::getTitleFor( 'OAuthListConsumers',
+ 'view/' . $cmrAc->getConsumerKey()
+ );
+ $encName = $cmrAc->escapeForWikitext( $cmrAc->getNameAndVersion() );
+ $message = "[[$target|$encName]]";
+ }
+ return false;
+ }
+
+ /**
+ * Append OAuth-specific grants to Special:ListGrants
+ * @param SpecialPage $special
+ * @param string $par
+ * @return bool
+ */
+ public static function onSpecialPageAfterExecute( SpecialPage $special, $par ) {
+ if ( $special->getName() != 'Listgrants' ) {
+ return true;
+ }
+
+ $out = $special->getOutput();
+
+ $out->addWikiMsg( 'mwoauth-listgrants-extra-summary' );
+
+ $out->addHTML(
+ \Html::openElement( 'table',
+ [ 'class' => 'wikitable mw-listgrouprights-table' ] ) .
+ '<tr>' .
+ \Html::element( 'th', null, $special->msg( 'listgrants-grant' )->text() ) .
+ \Html::element( 'th', null, $special->msg( 'listgrants-rights' )->text() ) .
+ '</tr>'
+ );
+
+ $grants = [
+ 'mwoauth-authonly' => [],
+ 'mwoauth-authonlyprivate' => [],
+ ];
+
+ foreach ( $grants as $grant => $rights ) {
+ $descs = [];
+ $rights = array_filter( $rights ); // remove ones with 'false'
+ foreach ( $rights as $permission => $granted ) {
+ $descs[] = $special->msg(
+ 'listgrouprights-right-display',
+ \User::getRightDescription( $permission ),
+ '<span class="mw-listgrants-right-name">' . $permission . '</span>'
+ )->parse();
+ }
+ if ( !count( $descs ) ) {
+ $grantCellHtml = '';
+ } else {
+ sort( $descs );
+ $grantCellHtml = '<ul><li>' . implode( "</li>\n<li>", $descs ) . '</li></ul>';
+ }
+
+ $id = \Sanitizer::escapeIdForAttribute( $grant );
+ $out->addHTML( \Html::rawElement( 'tr', [ 'id' => $id ],
+ "<td>" . $special->msg( "grant-$grant" )->escaped() . "</td>" .
+ "<td>" . $grantCellHtml . '</td>'
+ ) );
+ }
+
+ $out->addHTML( \Html::closeElement( 'table' ) );
+
+ return true;
+ }
+
+ /**
+ * Add additional text to Special:BotPasswords
+ * @param string $name Special page name
+ * @param HTMLForm $form
+ * @return bool
+ */
+ public static function onSpecialPageBeforeFormDisplay( $name, HTMLForm $form ) {
+ global $wgMWOAuthCentralWiki;
+
+ if ( $name === 'BotPasswords' ) {
+ if ( Utils::isCentralWiki() ) {
+ $url = SpecialPage::getTitleFor( 'OAuthConsumerRegistration' )->getFullURL();
+ } else {
+ $url = \WikiMap::getForeignURL(
+ $wgMWOAuthCentralWiki,
+ 'Special:OAuthConsumerRegistration' // Cross-wiki, so don't localize
+ );
+ }
+ $form->addPreText( $form->msg( 'mwoauth-botpasswords-note', $url )->parseAsBlock() );
+ }
+ return true;
+ }
+
+ /**
+ * @param array &$notifications
+ * @param array &$notificationCategories
+ * @param array &$icons
+ */
+ public static function onBeforeCreateEchoEvent(
+ &$notifications, &$notificationCategories, &$icons
+ ) {
+ global $wgOAuthGroupsToNotify;
+
+ if ( !Utils::isCentralWiki() ) {
+ return;
+ }
+
+ $notificationCategories['oauth-owner'] = [
+ 'tooltip' => 'echo-pref-tooltip-oauth-owner',
+ ];
+ $notificationCategories['oauth-admin'] = [
+ 'tooltip' => 'echo-pref-tooltip-oauth-admin',
+ 'usergroups' => $wgOAuthGroupsToNotify,
+ ];
+
+ foreach ( ConsumerSubmitControl::$actions as $eventName ) {
+ // oauth-app-propose and oauth-app-update notifies admins of the app.
+ // oauth-app-approve, oauth-app-reject, oauth-app-disable and oauth-app-reenable
+ // notify owner of the change.
+ $notifications["oauth-app-$eventName"] =
+ EchoOAuthStageChangePresentationModel::getDefinition( $eventName );
+ }
+
+ $icons['oauth'] = [ 'path' => 'OAuth/resources/assets/echo-icon.png' ];
+ }
+
+ /**
+ * @param array &$specialPages
+ */
+ public static function onSpecialPage_initList( array &$specialPages ) {
+ if ( Utils::isCentralWiki() ) {
+ $specialPages['OAuthConsumerRegistration'] = SpecialMWOAuthConsumerRegistration::class;
+ $specialPages['OAuthManageConsumers'] = SpecialMWOAuthManageConsumers::class;
+ }
+ }
+
+ /**
+ * Show help text when a user is redirected to provider login page
+ * @param array &$messages
+ * @return bool
+ */
+ public static function onLoginFormValidErrorMessages( &$messages ) {
+ $messages[] = 'mwoauth-login-required-reason';
+ return true;
+ }
+}
diff --git a/OAuth/src/Frontend/UIUtils.php b/OAuth/src/Frontend/UIUtils.php
new file mode 100644
index 00000000..58387d14
--- /dev/null
+++ b/OAuth/src/Frontend/UIUtils.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Frontend;
+
+use IContextSource;
+use Message;
+use OOUI\Tag;
+
+/**
+ * Static utility class for the special pages
+ */
+class UIUtils {
+ /**
+ * Generate an information table for a consumer. The result will be suitable for use as a
+ * HTMLForm field with no label.
+ * @param array $info fieldname-message => description; description will be escaped (use
+ * HtmlSnippet to avoid); fields with null value will be ignored; messages will be interpreted
+ * as plaintext
+ * @param IContextSource $context
+ * @return string
+ */
+ public static function generateInfoTable( $info, $context ) {
+ $dl = new Tag( 'dl' );
+ $dl->addClasses( [ 'mw-mwoauth-infotable' ] );
+ foreach ( $info as $fieldname => $description ) {
+ if ( $description === null ) {
+ continue;
+ } elseif ( $description instanceof Message ) {
+ $description = $description->plain();
+ }
+ $dt = new Tag( 'dt' );
+ $dd = new Tag( 'dd' );
+ $dt->appendContent( $context->msg( $fieldname )->text() );
+ $dd->appendContent( $description );
+ $dl->appendContent( $dt, $dd );
+ }
+ return $dl->toString();
+ }
+}
diff --git a/OAuth/src/Lib/OAuthConsumer.php b/OAuth/src/Lib/OAuthConsumer.php
new file mode 100644
index 00000000..a0c348fa
--- /dev/null
+++ b/OAuth/src/Lib/OAuthConsumer.php
@@ -0,0 +1,43 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+*/
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+class OAuthConsumer {
+ public $key;
+ public $secret;
+ public $callback_url;
+
+ function __construct( $key, $secret, $callback_url = NULL ) {
+ $this->key = $key;
+ $this->secret = $secret;
+ $this->callback_url = $callback_url;
+ }
+
+ function __toString() {
+ return "OAuthConsumer[key=$this->key,secret=$this->secret]";
+ }
+}
diff --git a/OAuth/src/Lib/OAuthDataStore.php b/OAuth/src/Lib/OAuthDataStore.php
new file mode 100644
index 00000000..b34d1b3b
--- /dev/null
+++ b/OAuth/src/Lib/OAuthDataStore.php
@@ -0,0 +1,53 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+class OAuthDataStore {
+ function lookup_consumer( $consumer_key ) {
+ // implement me
+ }
+
+ function lookup_token( $consumer, $token_type, $token ) {
+ // implement me
+ }
+
+ function lookup_nonce( $consumer, $token, $nonce, $timestamp ) {
+ // implement me
+ }
+
+ function new_request_token( $consumer, $callback = null ) {
+ // return a new token attached to this consumer
+ }
+
+ function new_access_token( $token, $consumer, $verifier = null ) {
+ // return a new access token attached to this consumer
+ // for the user associated with this token if the request token
+ // is authorized
+ // should also invalidate the request token
+ }
+
+}
diff --git a/OAuth/src/Lib/OAuthException.php b/OAuth/src/Lib/OAuthException.php
new file mode 100644
index 00000000..bac7b119
--- /dev/null
+++ b/OAuth/src/Lib/OAuthException.php
@@ -0,0 +1,34 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+/**
+ * Generic exception class
+ */
+class OAuthException extends \Exception {
+ // pass
+}
diff --git a/OAuth/src/Lib/OAuthRequest.php b/OAuth/src/Lib/OAuthRequest.php
new file mode 100644
index 00000000..5b5050ae
--- /dev/null
+++ b/OAuth/src/Lib/OAuthRequest.php
@@ -0,0 +1,297 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Lib\OAuthUtil;
+use MediaWiki\Logger\LoggerFactory;
+
+class OAuthRequest {
+ protected $parameters;
+ protected $http_method;
+ protected $http_url;
+ // for debug purposes
+ public $base_string;
+ public static $version = '1.0';
+ public static $POST_INPUT = 'php://input';
+
+ /** @var \\Psr\\Log\\LoggerInterface */
+ protected $logger;
+
+ function __construct( $http_method, $http_url, $parameters = null ) {
+ $parameters = ( $parameters ) ? $parameters : array();
+ $parameters = array_merge(
+ OAuthUtil::parse_parameters( parse_url( $http_url, PHP_URL_QUERY ) ),
+ $parameters
+ );
+ $this->parameters = $parameters;
+ $this->http_method = $http_method;
+ $this->http_url = $http_url;
+ $this->logger = LoggerFactory::getInstance( 'OAuth' );
+ }
+
+ /**
+ * attempt to build up a request from what was passed to the server
+ */
+ public static function from_request( $http_method = null, $http_url = null, $parameters = null ) {
+ $scheme = ( !isset( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS'] != "on" ) ? 'http' : 'https';
+ $http_url = ( $http_url ) ? $http_url : $scheme . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
+ $http_method = ( $http_method ) ? $http_method : $_SERVER['REQUEST_METHOD'];
+
+ // We weren't handed any parameters, so let's find the ones relevant to
+ // this request.
+ // If you run XML-RPC or similar you should use this to provide your own
+ // parsed parameter-list
+ if ( !$parameters ) {
+ // Find request headers
+ $request_headers = OAuthUtil::get_headers();
+
+ // Parse the query-string to find GET parameters
+ $parameters = OAuthUtil::parse_parameters( $_SERVER['QUERY_STRING'] );
+
+ // It's a POST request of the proper content-type, so parse POST
+ // parameters and add those overriding any duplicates from GET
+ if ( $http_method == "POST" && isset( $request_headers['Content-Type'] ) && strstr(
+ $request_headers['Content-Type'],
+ 'application/x-www-form-urlencoded'
+ )
+ ) {
+ $post_data = OAuthUtil::parse_parameters( file_get_contents( self::$POST_INPUT ) );
+ $parameters = array_merge( $parameters, $post_data );
+ }
+
+ // We have a Authorization-header with OAuth data. Parse the header
+ // and add those overriding any duplicates from GET or POST
+ if ( isset( $request_headers['Authorization'] ) && substr(
+ $request_headers['Authorization'],
+ 0,
+ 6
+ ) == 'OAuth '
+ ) {
+ $header_parameters = OAuthUtil::split_header( $request_headers['Authorization'] );
+ $parameters = array_merge( $parameters, $header_parameters );
+ }
+ }
+
+ return new OAuthRequest( $http_method, $http_url, $parameters );
+ }
+
+ /**
+ * pretty much a helper function to set up the request
+ */
+ public static function from_consumer_and_token( $consumer, $token, $http_method, $http_url, $parameters = null ) {
+ $parameters = ( $parameters ) ? $parameters : array();
+ $defaults = array(
+ "oauth_version" => OAuthRequest::$version,
+ "oauth_nonce" => OAuthRequest::generate_nonce(),
+ "oauth_timestamp" => OAuthRequest::generate_timestamp(),
+ "oauth_consumer_key" => $consumer->key
+ );
+ if ( $token ) {
+ $defaults['oauth_token'] = $token->key;
+ }
+
+ $parameters = array_merge( $defaults, $parameters );
+
+ return new OAuthRequest( $http_method, $http_url, $parameters );
+ }
+
+ public function set_parameter( $name, $value, $allow_duplicates = true ) {
+ if ( $allow_duplicates && isset( $this->parameters[$name] ) ) {
+ // We have already added parameter( s ) with this name, so add to the list
+ if ( is_scalar( $this->parameters[$name] ) ) {
+ // This is the first duplicate, so transform scalar ( string )
+ // into an array so we can add the duplicates
+ $this->parameters[$name] = array( $this->parameters[$name] );
+ }
+
+ $this->parameters[$name][] = $value;
+ } else {
+ $this->parameters[$name] = $value;
+ }
+ }
+
+ public function get_parameter( $name ) {
+ return isset( $this->parameters[$name] ) ? $this->parameters[$name] : null;
+ }
+
+ public function get_parameters() {
+ return $this->parameters;
+ }
+
+ public function unset_parameter( $name ) {
+ unset( $this->parameters[$name] );
+ }
+
+ /**
+ * The request parameters, sorted and concatenated into a normalized string.
+ * @return string
+ */
+ public function get_signable_parameters() {
+ // Grab all parameters
+ $params = $this->parameters;
+
+ // Remove oauth_signature if present
+ // Ref: Spec: 9.1.1 ( "The oauth_signature parameter MUST be excluded." )
+ if ( isset( $params['oauth_signature'] ) ) {
+ unset( $params['oauth_signature'] );
+ }
+
+ return OAuthUtil::build_http_query( $params );
+ }
+
+ /**
+ * Returns the base string of this request
+ *
+ * The base string defined as the method, the url
+ * and the parameters ( normalized ), each urlencoded
+ * and the concated with &.
+ */
+ public function get_signature_base_string() {
+ $parts = array(
+ $this->get_normalized_http_method(),
+ $this->get_normalized_http_url(),
+ $this->get_signable_parameters()
+ );
+
+ $parts = OAuthUtil::urlencode_rfc3986( $parts );
+
+ return implode( '&', $parts );
+ }
+
+ /**
+ * just uppercases the http method
+ */
+ public function get_normalized_http_method() {
+ return strtoupper( $this->http_method );
+ }
+
+ /**
+ * parses the url and rebuilds it to be
+ * scheme://host/path
+ */
+ public function get_normalized_http_url() {
+ $parts = parse_url( $this->http_url );
+
+ $scheme = ( isset( $parts['scheme'] ) ) ? $parts['scheme'] : 'http';
+ $port = ( isset( $parts['port'] ) ) ? $parts['port'] : ( ( $scheme == 'https' ) ? '443' : '80' );
+ $host = ( isset( $parts['host'] ) ) ? strtolower( $parts['host'] ) : '';
+ $path = ( isset( $parts['path'] ) ) ? $parts['path'] : '';
+
+ if ( ( $scheme == 'https' && $port != '443' ) || ( $scheme == 'http' && $port != '80' ) ) {
+ $host = "$host:$port";
+ }
+
+ return "$scheme://$host$path";
+ }
+
+ /**
+ * builds a url usable for a GET request
+ */
+ public function to_url() {
+ $post_data = $this->to_postdata();
+ $out = $this->get_normalized_http_url();
+ if ( $post_data ) {
+ $out .= '?' . $post_data;
+ }
+
+ return $out;
+ }
+
+ /**
+ * builds the data one would send in a POST request
+ */
+ public function to_postdata() {
+ return OAuthUtil::build_http_query( $this->parameters );
+ }
+
+ /**
+ * builds the Authorization: header
+ */
+ public function to_header( $realm = null ) {
+ $first = true;
+ if ( $realm ) {
+ $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986( $realm ) . '"';
+ $first = false;
+ } else {
+ $out = 'Authorization: OAuth';
+ }
+ $total = array();
+ foreach ( $this->parameters as $k => $v ) {
+ if ( substr( $k, 0, 5 ) != "oauth" ) {
+ continue;
+ }
+ if ( is_array( $v ) ) {
+ throw new OAuthException( 'Arrays not supported in headers' );
+ }
+ $out .= ( $first ) ? ' ' : ',';
+ $out .= OAuthUtil::urlencode_rfc3986( $k ) . '="' . OAuthUtil::urlencode_rfc3986(
+ $v
+ ) . '"';
+ $first = false;
+ }
+
+ return $out;
+ }
+
+ public function __toString() {
+ return $this->to_url();
+ }
+
+ public function sign_request( $signature_method, $consumer, $token ) {
+ $this->set_parameter(
+ "oauth_signature_method",
+ $signature_method->get_name(),
+ false
+ );
+ $signature = $this->build_signature( $signature_method, $consumer, $token );
+ $this->set_parameter( "oauth_signature", $signature, false );
+ }
+
+ public function build_signature( $signature_method, $consumer, $token ) {
+ $signature = $signature_method->build_signature( $this, $consumer, $token );
+
+ return $signature;
+ }
+
+ /**
+ * util function: current timestamp
+ */
+ private static function generate_timestamp() {
+ return time();
+ }
+
+ /**
+ * util function: current nonce
+ */
+ private static function generate_nonce() {
+ $mt = microtime();
+ $rand = mt_rand();
+
+ return md5( $mt . $rand ); // md5s look nicer than numbers
+ }
+}
diff --git a/OAuth/src/Lib/OAuthServer.php b/OAuth/src/Lib/OAuthServer.php
new file mode 100644
index 00000000..49d2aac6
--- /dev/null
+++ b/OAuth/src/Lib/OAuthServer.php
@@ -0,0 +1,270 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthDataStore;
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+use MediaWiki\Logger\LoggerFactory;
+
+class OAuthServer {
+ protected $timestamp_threshold = 300; // in seconds, five minutes
+ protected $version = '1.0'; // hi blaine
+ protected $signature_methods = array();
+
+ /** @var OAuthDataStore */
+ protected $data_store;
+
+ /** @var \\Psr\\Log\\LoggerInterface */
+ protected $logger;
+
+ function __construct( $data_store ) {
+ $this->data_store = $data_store;
+ $this->logger = LoggerFactory::getInstance( 'OAuth' );
+ }
+
+ public function add_signature_method( $signature_method ) {
+ $this->signature_methods[$signature_method->get_name()] = $signature_method;
+ }
+
+ // high level functions
+
+ /**
+ * process a request_token request
+ * returns the request token on success
+ */
+ public function fetch_request_token( &$request ) {
+ $this->get_version( $request );
+
+ $consumer = $this->get_consumer( $request );
+
+ // no token required for the initial token request
+ $token = null;
+
+ $this->check_signature( $request, $consumer, $token );
+
+ // Rev A change
+ $callback = $request->get_parameter( 'oauth_callback' );
+ $new_token = $this->data_store->new_request_token( $consumer, $callback );
+
+ return $new_token;
+ }
+
+ /**
+ * process an access_token request
+ * returns the access token on success
+ */
+ public function fetch_access_token( &$request ) {
+ $this->get_version( $request );
+
+ $consumer = $this->get_consumer( $request );
+
+ // requires authorized request token
+ $token = $this->get_token( $request, $consumer, "request" );
+
+ $this->check_signature( $request, $consumer, $token );
+
+ // Rev A change
+ $verifier = $request->get_parameter( 'oauth_verifier' );
+ $new_token = $this->data_store->new_access_token( $token, $consumer, $verifier );
+
+ return $new_token;
+ }
+
+ /**
+ * verify an api call, checks all the parameters
+ */
+ public function verify_request( &$request ) {
+ $this->get_version( $request );
+ $consumer = $this->get_consumer( $request );
+ $token = $this->get_token( $request, $consumer, "access" );
+ $this->check_signature( $request, $consumer, $token );
+
+ return array(
+ $consumer,
+ $token
+ );
+ }
+
+ // Internals from here
+
+ /**
+ * version 1
+ */
+ protected function get_version( &$request ) {
+ $version = $request->get_parameter( "oauth_version" );
+ if ( !$version ) {
+ // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
+ // Chapter 7.0 ( "Accessing Protected Ressources" )
+ $version = '1.0';
+ }
+ if ( $version !== $this->version ) {
+ throw new OAuthException( "OAuth version '$version' not supported" );
+ }
+
+ return $version;
+ }
+
+ /**
+ * figure out the signature with some defaults
+ */
+ private function get_signature_method( $request ) {
+ $signature_method = $request instanceof OAuthRequest ? $request->get_parameter(
+ "oauth_signature_method"
+ ) : null;
+
+ if ( !$signature_method ) {
+ // According to chapter 7 ( "Accessing Protected Ressources" ) the signature-method
+ // parameter is required, and we can't just fallback to PLAINTEXT
+ throw new OAuthException( 'No signature method parameter. This parameter is required' );
+ }
+
+ if ( !in_array( $signature_method, array_keys( $this->signature_methods ) ) ) {
+ throw new OAuthException(
+ "Signature method '$signature_method' not supported " . "try one of the following: " . implode(
+ ", ",
+ array_keys( $this->signature_methods )
+ )
+ );
+ }
+
+ return $this->signature_methods[$signature_method];
+ }
+
+ /**
+ * try to find the consumer for the provided request's consumer key
+ */
+ protected function get_consumer( $request ) {
+ $consumer_key = $request instanceof OAuthRequest ? $request->get_parameter(
+ "oauth_consumer_key"
+ ) : null;
+
+ if ( !$consumer_key ) {
+ throw new OAuthException( "Invalid consumer key" );
+ }
+ $this->logger->debug( __METHOD__ . ": getting consumer for '$consumer_key'" );
+ $consumer = $this->data_store->lookup_consumer( $consumer_key );
+ if ( !$consumer ) {
+ throw new OAuthException( "Invalid consumer" );
+ }
+
+ return $consumer;
+ }
+
+ /**
+ * try to find the token for the provided request's token key
+ */
+ protected function get_token( $request, $consumer, $token_type = "access" ) {
+ $token_field = $request instanceof OAuthRequest ? $request->get_parameter(
+ 'oauth_token'
+ ) : null;
+
+ $token = $this->data_store->lookup_token(
+ $consumer,
+ $token_type,
+ $token_field
+ );
+ if ( !$token ) {
+ throw new OAuthException( "Invalid $token_type token: $token_field" );
+ }
+
+ return $token;
+ }
+
+ /**
+ * all-in-one function to check the signature on a request
+ * should guess the signature method appropriately
+ */
+ protected function check_signature( $request, $consumer, $token ) {
+ // this should probably be in a different method
+ $timestamp = $request instanceof OAuthRequest ? $request->get_parameter(
+ 'oauth_timestamp'
+ ) : null;
+ $nonce = $request instanceof OAuthRequest ? $request->get_parameter( 'oauth_nonce' ) : null;
+
+ $this->check_timestamp( $timestamp );
+ $this->check_nonce( $consumer, $token, $nonce, $timestamp );
+
+ $signature_method = $this->get_signature_method( $request );
+ $signature = $request->get_parameter( 'oauth_signature' );
+ $valid_sig = $signature_method->check_signature(
+ $request,
+ $consumer,
+ $token,
+ $signature
+ );
+
+ if ( !$valid_sig ) {
+ $this->logger->info(
+ __METHOD__ . ': Signature check (' . get_class( $signature_method ) . ') failed'
+ );
+ throw new OAuthException( "Invalid signature" );
+ }
+ }
+
+ /**
+ * check that the timestamp is new enough
+ */
+ private function check_timestamp( $timestamp ) {
+ if ( !$timestamp ) {
+ throw new OAuthException(
+ 'Missing timestamp parameter. The parameter is required'
+ );
+ }
+
+ // verify that timestamp is recentish
+ $now = time();
+ if ( abs( $now - $timestamp ) > $this->timestamp_threshold ) {
+ throw new OAuthException(
+ "Expired timestamp, yours $timestamp, ours $now"
+ );
+ }
+ }
+
+ /**
+ * check that the nonce is not repeated
+ */
+ private function check_nonce( $consumer, $token, $nonce, $timestamp ) {
+ if ( !$nonce ) {
+ throw new OAuthException(
+ 'Missing nonce parameter. The parameter is required'
+ );
+ }
+
+ // verify that the nonce is uniqueish
+ $found = $this->data_store->lookup_nonce(
+ $consumer,
+ $token,
+ $nonce,
+ $timestamp
+ );
+ if ( $found ) {
+ throw new OAuthException( "Nonce already used: $nonce" );
+ }
+ }
+
+}
diff --git a/OAuth/src/Lib/OAuthSignatureMethod.php b/OAuth/src/Lib/OAuthSignatureMethod.php
new file mode 100644
index 00000000..e417eff3
--- /dev/null
+++ b/OAuth/src/Lib/OAuthSignatureMethod.php
@@ -0,0 +1,94 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+use MediaWiki\Logger\LoggerFactory;
+
+/**
+ * A class for implementing a Signature Method
+ * See section 9 ( "Signing Requests" ) in the spec
+ */
+abstract class OAuthSignatureMethod {
+
+ /** @var \\Psr\\Log\\LoggerInterface */
+ protected $logger;
+
+ public function __construct() {
+ $this->logger = LoggerFactory::getInstance( 'OAuth' );
+ }
+
+ /**
+ * Needs to return the name of the Signature Method ( ie HMAC-SHA1 )
+ * @return string
+ */
+ abstract public function get_name();
+
+ /**
+ * Build up the signature
+ * NOTE: The output of this function MUST NOT be urlencoded.
+ * the encoding is handled in OAuthRequest when the final
+ * request is serialized
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @return string
+ */
+ abstract public function build_signature( $request, $consumer, $token );
+
+ /**
+ * Verifies that a given signature is correct
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @param string $signature
+ * @return bool
+ */
+ public function check_signature( $request, $consumer, $token, $signature ) {
+ $this->logger->debug( __METHOD__ . ": Expecting: '$signature'" );
+ $built = $this->build_signature( $request, $consumer, $token );
+ $this->logger->debug( __METHOD__ . ": Built: '$built'" );
+ // Check for zero length, although unlikely here
+ if ( strlen( $built ) == 0 || strlen( $signature ) == 0 ) {
+ return false;
+ }
+
+ if ( strlen( $built ) != strlen( $signature ) ) {
+ return false;
+ }
+
+ // Avoid a timing leak with a ( hopefully ) time insensitive compare
+ $result = 0;
+ for ( $i = 0; $i < strlen( $signature ); $i++ ) {
+ $result |= ord( $built[$i] ) ^ ord( $signature[$i] );
+ }
+
+ return $result == 0;
+ }
+}
diff --git a/OAuth/src/Lib/OAuthSignatureMethod_HMAC_SHA1.php b/OAuth/src/Lib/OAuthSignatureMethod_HMAC_SHA1.php
new file mode 100644
index 00000000..b4d00dae
--- /dev/null
+++ b/OAuth/src/Lib/OAuthSignatureMethod_HMAC_SHA1.php
@@ -0,0 +1,60 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod;
+use MediaWiki\Extensions\OAuth\Lib\OAuthUtil;
+
+/**
+ * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
+ * where the Signature Base String is the text and the key is the concatenated values ( each first
+ * encoded per Parameter Encoding ) of the Consumer Secret and Token Secret, separated by an '&'
+ * character ( ASCII code 38 ) even if empty.
+ * - Chapter 9.2 ( "HMAC-SHA1" )
+ */
+class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
+ function get_name() {
+ return "HMAC-SHA1";
+ }
+
+ public function build_signature( $request, $consumer, $token ) {
+ $base_string = $request->get_signature_base_string();
+ $this->logger->debug( __METHOD__ . ": Base string: '$base_string'" );
+ $request->base_string = $base_string;
+
+ $key_parts = array(
+ $consumer->secret,
+ ( $token ) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986( $key_parts );
+ $key = implode( '&', $key_parts );
+ $this->logger->debug( __METHOD__ . ": HMAC Key: '$key'" );
+
+ return base64_encode( hash_hmac( 'sha1', $base_string, $key, true ) );
+ }
+}
diff --git a/OAuth/src/Lib/OAuthSignatureMethod_PLAINTEXT.php b/OAuth/src/Lib/OAuthSignatureMethod_PLAINTEXT.php
new file mode 100644
index 00000000..5e888439
--- /dev/null
+++ b/OAuth/src/Lib/OAuthSignatureMethod_PLAINTEXT.php
@@ -0,0 +1,63 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod;
+use MediaWiki\Extensions\OAuth\Lib\OAuthUtil;
+
+/**
+ * The PLAINTEXT method does not provide any security protection and SHOULD only be used
+ * over a secure channel such as HTTPS. It does not use the Signature Base String.
+ * - Chapter 9.4 ( "PLAINTEXT" )
+ */
+class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
+ public function get_name() {
+ return "PLAINTEXT";
+ }
+
+ /**
+ * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
+ * Token Secret, separated by a '&' character ( ASCII code 38 ), even if either secret is
+ * empty. The result MUST be encoded again.
+ * - Chapter 9.4.1 ( "Generating Signatures" )
+ *
+ * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
+ * OAuthRequest handles this!
+ */
+ public function build_signature( $request, $consumer, $token ) {
+ $key_parts = array(
+ $consumer->secret,
+ ( $token ) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986( $key_parts );
+ $key = implode( '&', $key_parts );
+ $request->base_string = $key;
+
+ return $key;
+ }
+}
diff --git a/OAuth/src/Lib/OAuthSignatureMethod_RSA_SHA1.php b/OAuth/src/Lib/OAuthSignatureMethod_RSA_SHA1.php
new file mode 100644
index 00000000..3b442108
--- /dev/null
+++ b/OAuth/src/Lib/OAuthSignatureMethod_RSA_SHA1.php
@@ -0,0 +1,96 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod;
+
+/**
+ * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
+ * [RFC3447] section 8.2 ( more simply known as PKCS#1 ), using SHA-1 as the hash function for
+ * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
+ * verified way to the Service Provider, in a manner which is beyond the scope of this
+ * specification.
+ * - Chapter 9.3 ( "RSA-SHA1" )
+ */
+abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
+ public function get_name() {
+ return "RSA-SHA1";
+ }
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // ( 1 ) do a lookup in a table of trusted certs keyed off of consumer
+ // ( 2 ) fetch via http using a url provided by the requester
+ // ( 3 ) some sort of specific discovery code based on request
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_public_cert( &$request );
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // ( 1 ) do a lookup in a table of trusted certs keyed off of consumer
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_private_cert( &$request );
+
+ public function build_signature( $request, $consumer, $token ) {
+ $base_string = $request->get_signature_base_string();
+ $request->base_string = $base_string;
+
+ // Fetch the private key cert based on the request
+ $cert = $this->fetch_private_cert( $request );
+
+ // Pull the private key ID from the certificate
+ $privatekeyid = openssl_get_privatekey( $cert );
+
+ // Sign using the key
+ $ok = openssl_sign( $base_string, $signature, $privatekeyid );
+
+ // Release the key resource
+ openssl_free_key( $privatekeyid );
+
+ return base64_encode( $signature );
+ }
+
+ public function check_signature( $request, $consumer, $token, $signature ) {
+ $decoded_sig = base64_decode( $signature );
+
+ $base_string = $request->get_signature_base_string();
+
+ // Fetch the public key cert based on the request
+ $cert = $this->fetch_public_cert( $request );
+
+ // Pull the public key ID from the certificate
+ $publickeyid = openssl_get_publickey( $cert );
+
+ // Check the computed signature against the one passed in the query
+ $ok = openssl_verify( $base_string, $decoded_sig, $publickeyid );
+
+ // Release the key resource
+ openssl_free_key( $publickeyid );
+
+ return $ok == 1;
+ }
+}
diff --git a/OAuth/src/Lib/OAuthToken.php b/OAuth/src/Lib/OAuthToken.php
new file mode 100644
index 00000000..669393aa
--- /dev/null
+++ b/OAuth/src/Lib/OAuthToken.php
@@ -0,0 +1,58 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthUtil;
+
+class OAuthToken {
+ // access tokens and request tokens
+ public $key;
+ public $secret;
+
+ /**
+ * key = the token
+ * secret = the token secret
+ */
+ function __construct( $key, $secret ) {
+ $this->key = $key;
+ $this->secret = $secret;
+ }
+
+ /**
+ * generates the basic string serialization of a token that a server
+ * would respond to request_token and access_token calls with
+ */
+ function to_string() {
+ return "oauth_token=" . OAuthUtil::urlencode_rfc3986(
+ $this->key
+ ) . "&oauth_token_secret=" . OAuthUtil::urlencode_rfc3986( $this->secret );
+ }
+
+ function __toString() {
+ return $this->to_string();
+ }
+}
diff --git a/OAuth/src/Lib/OAuthUtil.php b/OAuth/src/Lib/OAuthUtil.php
new file mode 100644
index 00000000..8dca8c06
--- /dev/null
+++ b/OAuth/src/Lib/OAuthUtil.php
@@ -0,0 +1,209 @@
+<?php
+// vim: foldmethod=marker
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Lib;
+
+use MediaWiki\Logger\LoggerFactory;
+
+class OAuthUtil {
+ public static function urlencode_rfc3986( $input ) {
+ if ( is_array( $input ) ) {
+ return array_map(
+ array(
+ self::class,
+ 'urlencode_rfc3986'
+ ),
+ $input
+ );
+ } else {
+ if ( is_scalar( $input ) ) {
+ return str_replace(
+ '+',
+ ' ',
+ str_replace( '%7E', '~', rawurlencode( $input ) )
+ );
+ } else {
+ return '';
+ }
+ }
+ }
+
+
+ // This decode function isn't taking into consideration the above
+ // modifications to the encoding process. However, this method doesn't
+ // seem to be used anywhere so leaving it as is.
+ public static function urldecode_rfc3986( $string ) {
+ return urldecode( $string );
+ }
+
+ // Utility function for turning the Authorization: header into
+ // parameters, has to do some unescaping
+ // Can filter out any non-oauth parameters if needed ( default behaviour )
+ // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
+ // see http://code.google.com/p/oauth/issues/detail?id = 163
+ public static function split_header( $header, $only_allow_oauth_parameters = true ) {
+ $logger = LoggerFactory::getInstance( 'OAuth' );
+ $logger->debug( __METHOD__ . ": pulling headers from '$header'" );
+ $params = array();
+ if ( preg_match_all(
+ '/(' . ( $only_allow_oauth_parameters ? 'oauth_' : '' ) . '[a-z_-]*)=(:?"([^"]*)"|([^,]*))/',
+ $header,
+ $matches
+ )
+ ) {
+ foreach ( $matches[1] as $i => $h ) {
+ $params[$h] = OAuthUtil::urldecode_rfc3986(
+ empty( $matches[3][$i] ) ? $matches[4][$i] : $matches[3][$i]
+ );
+ }
+ if ( isset( $params['realm'] ) ) {
+ unset( $params['realm'] );
+ }
+ }
+
+ return $params;
+ }
+
+ // helper to try to sort out headers for people who aren't running apache
+ public static function get_headers() {
+ if ( function_exists( 'apache_request_headers' ) ) {
+ // we need this to get the actual Authorization: header
+ // because apache tends to tell us it doesn't exist
+ $headers = apache_request_headers();
+
+ // sanitize the output of apache_request_headers because
+ // we always want the keys to be Cased-Like-This and arh()
+ // returns the headers in the same case as they are in the
+ // request
+ $out = array();
+ foreach ( $headers as $key => $value ) {
+ $key = str_replace(
+ " ",
+ "-",
+ ucwords( strtolower( str_replace( "-", " ", $key ) ) )
+ );
+ $out[$key] = $value;
+ }
+ } else {
+ // otherwise we don't have apache and are just going to have to hope
+ // that $_SERVER actually contains what we need
+ $out = array();
+ if ( isset( $_SERVER['CONTENT_TYPE'] ) ) {
+ $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
+ }
+ if ( isset( $_ENV['CONTENT_TYPE'] ) ) {
+ $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
+ }
+
+ foreach ( $_SERVER as $key => $value ) {
+ if ( substr( $key, 0, 5 ) == "HTTP_" ) {
+ // this is chaos, basically it is just there to capitalize the first
+ // letter of every word that is not an initial HTTP and strip HTTP
+ // code from przemek
+ $key = str_replace(
+ " ",
+ "-",
+ ucwords( strtolower( str_replace( "_", " ", substr( $key, 5 ) ) ) )
+ );
+ $out[$key] = $value;
+ }
+ }
+ }
+
+ return $out;
+ }
+
+ // This function takes a input like a=b&a=c&d=e and returns the parsed
+ // parameters like this
+ // array( 'a' => array( 'b','c' ), 'd' => 'e' )
+ public static function parse_parameters( $input ) {
+ if ( !isset( $input ) || !$input ) {
+ return array();
+ }
+
+ $pairs = explode( '&', $input );
+
+ $parsed_parameters = array();
+ foreach ( $pairs as $pair ) {
+ $split = explode( '=', $pair, 2 );
+ $parameter = OAuthUtil::urldecode_rfc3986( $split[0] );
+ $value = isset( $split[1] ) ? OAuthUtil::urldecode_rfc3986( $split[1] ) : '';
+
+ if ( isset( $parsed_parameters[$parameter] ) ) {
+ // We have already recieved parameter( s ) with this name, so add to the list
+ // of parameters with this name
+
+ if ( is_scalar( $parsed_parameters[$parameter] ) ) {
+ // This is the first duplicate, so transform scalar ( string ) into an array
+ // so we can add the duplicates
+ $parsed_parameters[$parameter] = array( $parsed_parameters[$parameter] );
+ }
+
+ $parsed_parameters[$parameter][] = $value;
+ } else {
+ $parsed_parameters[$parameter] = $value;
+ }
+ }
+
+ return $parsed_parameters;
+ }
+
+ public static function build_http_query( $params ) {
+ LoggerFactory::getInstance( 'OAuth' )->debug(
+ __METHOD__ . " called with params:\n" . print_r( $params, true )
+ );
+ if ( !$params ) {
+ return '';
+ }
+
+ // Urlencode both keys and values
+ $keys = OAuthUtil::urlencode_rfc3986( array_keys( $params ) );
+ $values = OAuthUtil::urlencode_rfc3986( array_values( $params ) );
+ $params = array_combine( $keys, $values );
+
+ // Parameters are sorted by name, using lexicographical byte value ordering.
+ // Ref: Spec: 9.1.1 ( 1 )
+ uksort( $params, 'strcmp' );
+
+ $pairs = array();
+ foreach ( $params as $parameter => $value ) {
+ if ( is_array( $value ) ) {
+ // If two or more parameters share the same name, they are sorted by their value
+ // Ref: Spec: 9.1.1 ( 1 )
+ // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
+ sort( $value, SORT_STRING );
+ foreach ( $value as $duplicate_value ) {
+ $pairs[] = $parameter . '=' . $duplicate_value;
+ }
+ } else {
+ $pairs[] = $parameter . '=' . $value;
+ }
+ }
+ // For each parameter, the name is separated from the corresponding value by an ' = ' character ( ASCII code 61 )
+ // Each name-value pair is separated by an '&' character ( ASCII code 38 )
+ return implode( '&', $pairs );
+ }
+}
diff --git a/OAuth/src/Repository/AccessTokenRepository.php b/OAuth/src/Repository/AccessTokenRepository.php
new file mode 100644
index 00000000..28a7fcc6
--- /dev/null
+++ b/OAuth/src/Repository/AccessTokenRepository.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Repository;
+
+use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
+use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Entity\AccessTokenEntity;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+
+class AccessTokenRepository extends DatabaseRepository implements AccessTokenRepositoryInterface {
+ const FIELD_EXPIRES = 'oaat_expires';
+ const FIELD_ACCEPTANCE_ID = 'oaat_acceptance_id';
+ const FIELD_REVOKED = 'oaat_revoked';
+
+ /**
+ * Create a new access token
+ *
+ * @param ClientEntityInterface|ClientEntity $clientEntity
+ * @param ScopeEntityInterface[] $scopes
+ * @param mixed|null $userIdentifier
+ * @throws MWOAuthException
+ * @return AccessTokenEntityInterface
+ */
+ public function getNewToken( ClientEntityInterface $clientEntity,
+ array $scopes, $userIdentifier = null ) {
+ return new AccessTokenEntity( $clientEntity, $scopes, $userIdentifier );
+ }
+
+ /**
+ * Persists a new access token to permanent storage.
+ *
+ * @param AccessTokenEntityInterface|AccessTokenEntity $accessTokenEntity
+ *
+ * @throws UniqueTokenIdentifierConstraintViolationException
+ */
+ public function persistNewAccessToken( AccessTokenEntityInterface $accessTokenEntity ) {
+ if ( $this->identifierExists( $accessTokenEntity->getIdentifier() ) ) {
+ throw UniqueTokenIdentifierConstraintViolationException::create();
+ }
+
+ $data = $this->getDbDataFromTokenEntity( $accessTokenEntity );
+
+ $this->getDB( DB_MASTER )->insert(
+ $this->getTableName(),
+ $data,
+ __METHOD__
+ );
+ }
+
+ /**
+ * Revoke an access token.
+ *
+ * @param string $tokenId
+ */
+ public function revokeAccessToken( $tokenId ) {
+ if ( $this->identifierExists( $tokenId ) ) {
+ $this->getDB( DB_MASTER )->update(
+ $this->getTableName(),
+ [ static::FIELD_REVOKED => 1 ],
+ [ $this->getIdentifierField() => $tokenId ],
+ __METHOD__
+ );
+ }
+ }
+
+ /**
+ * Check if the access token has been revoked.
+ *
+ * @param string $tokenId
+ *
+ * @return bool Return true if this token has been revoked
+ */
+ public function isAccessTokenRevoked( $tokenId ) {
+ $row = $this->getDB()->selectRow(
+ $this->getTableName(),
+ [ static::FIELD_REVOKED ],
+ [ $this->getIdentifierField() => $tokenId ],
+ __METHOD__
+ );
+ if ( !$row ) {
+ return true;
+ }
+ return (bool)$row->{static::FIELD_REVOKED};
+ }
+
+ /**
+ * Delete all access tokens issued with provided approval
+ *
+ * @param int $approvalId
+ */
+ public function deleteForApprovalId( $approvalId ) {
+ $this->getDB( DB_MASTER )->delete(
+ $this->getTableName(),
+ [
+ static::FIELD_ACCEPTANCE_ID => $approvalId
+ ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * Get ID of the approval bound to this AT
+ *
+ * @param string $tokenId
+ * @return bool|int
+ */
+ public function getApprovalId( $tokenId ) {
+ $row = $this->getDB()->selectRow(
+ $this->getTableName(),
+ [ static::FIELD_ACCEPTANCE_ID ],
+ [ $this->getIdentifierField() => $tokenId ],
+ __METHOD__
+ );
+
+ if ( $row ) {
+ return (int)$row->{static::FIELD_ACCEPTANCE_ID};
+ }
+
+ return false;
+ }
+
+ private function getDbDataFromTokenEntity( AccessTokenEntity $accessTokenEntity ) {
+ $expiry = $accessTokenEntity->getExpiryDateTime()->getTimestamp();
+ if ( $expiry > 9223371197536780800 ) {
+ $expiry = 'infinity';
+ }
+ return [
+ $this->getIdentifierField() => $accessTokenEntity->getIdentifier(),
+ static::FIELD_EXPIRES => $this->getDB()->encodeExpiry( $expiry ),
+ static::FIELD_ACCEPTANCE_ID => $accessTokenEntity->getApproval() ?
+ $accessTokenEntity->getApproval()->getId() :
+ 0
+ ];
+ }
+
+ protected function getTableName(): string {
+ return 'oauth2_access_tokens';
+ }
+
+ protected function getIdentifierField(): string {
+ return 'oaat_identifier';
+ }
+}
diff --git a/OAuth/src/Repository/AuthCodeRepository.php b/OAuth/src/Repository/AuthCodeRepository.php
new file mode 100644
index 00000000..beb620b5
--- /dev/null
+++ b/OAuth/src/Repository/AuthCodeRepository.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Repository;
+
+use InvalidArgumentException;
+use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
+use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
+use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
+use MediaWiki\Extensions\OAuth\Entity\AuthCodeEntity;
+
+class AuthCodeRepository extends CacheRepository implements AuthCodeRepositoryInterface {
+
+ /**
+ * Creates a new AuthCode
+ *
+ * @return AuthCodeEntityInterface
+ */
+ public function getNewAuthCode() {
+ return new AuthCodeEntity();
+ }
+
+ /**
+ * Persists a new auth code to permanent storage.
+ *
+ * @param AuthCodeEntityInterface $authCodeEntity
+ *
+ * @throws UniqueTokenIdentifierConstraintViolationException
+ */
+ public function persistNewAuthCode( AuthCodeEntityInterface $authCodeEntity ) {
+ if ( !$authCodeEntity instanceof AuthCodeEntity ) {
+ throw new InvalidArgumentException(
+ '$authCodeEntity must be instance of ' .
+ AuthCodeEntity::class . ', got ' . get_class( $authCodeEntity ) . ' instead'
+ );
+ }
+ if ( $this->has( $authCodeEntity->getIdentifier() ) ) {
+ throw UniqueTokenIdentifierConstraintViolationException::create();
+ }
+
+ $this->set(
+ $authCodeEntity->getIdentifier(),
+ $authCodeEntity->jsonSerialize(),
+ $authCodeEntity->getExpiryDateTime()->getTimestamp()
+ );
+ }
+
+ /**
+ * Revoke an auth code.
+ *
+ * @param string $codeId
+ */
+ public function revokeAuthCode( $codeId ) {
+ $this->delete( $codeId );
+ }
+
+ /**
+ * Check if the auth code has been revoked.
+ *
+ * @param string $codeId
+ *
+ * @return bool Return true if this code has been revoked
+ */
+ public function isAuthCodeRevoked( $codeId ) {
+ return $this->has( $codeId ) === false;
+ }
+
+ /**
+ * Get object type for session key
+ *
+ * @return string
+ */
+ protected function getCacheKeyType(): string {
+ return 'AuthCode';
+ }
+}
diff --git a/OAuth/src/Repository/CacheRepository.php b/OAuth/src/Repository/CacheRepository.php
new file mode 100644
index 00000000..825b38f7
--- /dev/null
+++ b/OAuth/src/Repository/CacheRepository.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Repository;
+
+use BagOStuff;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+
+abstract class CacheRepository {
+
+ /**
+ * @var BagOStuff
+ */
+ protected $cache;
+
+ /**
+ * @return static
+ */
+ public static function factory() {
+ $cache = Utils::getSessionCache();
+
+ // @phan-suppress-next-line PhanTypeInstantiateAbstractStatic
+ return new static( $cache );
+ }
+
+ /**
+ * @param BagOStuff $cache
+ */
+ protected function __construct( BagOStuff $cache ) {
+ $this->cache = $cache;
+ }
+
+ /**
+ * Get object type for session key
+ *
+ * @return string
+ */
+ abstract protected function getCacheKeyType() : string;
+
+ /**
+ * Get the cache key based on unique identifier
+ *
+ * @param string $id
+ * @return string
+ */
+ protected function getCacheKey( $id ) {
+ return Utils::getCacheKey( $this->getCacheKeyType(), $id );
+ }
+
+ /**
+ * @param string $identifier
+ * @param int $flags
+ * @return mixed
+ */
+ protected function get( $identifier, $flags = 0 ) {
+ return $this->cache->get( $this->getCacheKey( $identifier ), $flags );
+ }
+
+ /**
+ * @param string $identifier
+ * @param mixed $value
+ * @param int $expires
+ * @param int $flags
+ */
+ protected function set( $identifier, $value, $expires = 0, $flags = 0 ) {
+ $this->cache->add( $this->getCacheKey( $identifier ), $value, $expires, $flags );
+ }
+
+ /**
+ * @param string $identifier
+ * @param int $flags
+ */
+ protected function delete( $identifier, $flags = 0 ) {
+ $this->cache->delete( $this->getCacheKey( $identifier ), $flags );
+ }
+
+ /**
+ * Convenience method to determine if given key exists in cache
+ *
+ * @param string $identifier
+ * @return bool
+ */
+ protected function has( $identifier ) {
+ return $this->cache->get( $this->getCacheKey( $identifier ) ) !== false;
+ }
+}
diff --git a/OAuth/src/Repository/ClientRepository.php b/OAuth/src/Repository/ClientRepository.php
new file mode 100644
index 00000000..97b3efef
--- /dev/null
+++ b/OAuth/src/Repository/ClientRepository.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Repository;
+
+use InvalidArgumentException;
+use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+
+class ClientRepository implements ClientRepositoryInterface {
+
+ /**
+ * Get a client.
+ *
+ * @param string $clientIdentifier The client's identifier
+ *
+ * @return ClientEntity|bool
+ */
+ public function getClientEntity( $clientIdentifier ) {
+ $client = ClientEntity::newFromKey(
+ Utils::getCentralDB( DB_REPLICA ),
+ $clientIdentifier
+ );
+ if ( !$client instanceof ClientEntity ) {
+ return false;
+ }
+
+ return $client;
+ }
+
+ /**
+ * @param int $clientId
+ * @return ClientEntity|bool
+ */
+ public function getClientEntityByDBId( $clientId ) {
+ $client = ClientEntity::newFromId( Utils::getCentralDB( DB_REPLICA ), $clientId );
+ if ( !$client instanceof ClientEntity ) {
+ return false;
+ }
+
+ return $client;
+ }
+
+ /**
+ * Validate a client's secret.
+ *
+ * @param string $clientIdentifier The client's identifier
+ * @param null|string $clientSecret The client's secret (if sent)
+ * @param null|string $grantType The type of grant the client is using (if sent)
+ *
+ * @return bool
+ * @throws InvalidArgumentException
+ */
+ public function validateClient( $clientIdentifier, $clientSecret, $grantType ) {
+ $client = $this->getClientEntity( $clientIdentifier );
+ if ( !$client || !$client instanceof ClientEntity ) {
+ throw new InvalidArgumentException(
+ "Client with identifier $clientIdentifier does not exist!"
+ );
+ }
+
+ return $client->validate( $clientSecret, $grantType );
+ }
+}
diff --git a/OAuth/src/Repository/DatabaseRepository.php b/OAuth/src/Repository/DatabaseRepository.php
new file mode 100644
index 00000000..328008a1
--- /dev/null
+++ b/OAuth/src/Repository/DatabaseRepository.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Repository;
+
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use Wikimedia\Rdbms\DBConnRef;
+
+abstract class DatabaseRepository {
+
+ /**
+ * @param int $index
+ * @return DBConnRef
+ */
+ public function getDB( $index = DB_REPLICA ) {
+ return Utils::getCentralDB( $index );
+ }
+
+ /**
+ * Is given identifier stored in the DB
+ *
+ * @param string $identifier
+ * @return bool
+ */
+ public function identifierExists( $identifier ) {
+ return $this->getDB()->selectRow(
+ $this->getTableName(),
+ [ $this->getIdentifierField() ],
+ [ $this->getIdentifierField() => $identifier ],
+ __METHOD__
+ ) !== false;
+ }
+
+ abstract protected function getTableName() : string;
+
+ abstract protected function getIdentifierField() : string;
+}
diff --git a/OAuth/src/Repository/RefreshTokenRepository.php b/OAuth/src/Repository/RefreshTokenRepository.php
new file mode 100644
index 00000000..b6a9a350
--- /dev/null
+++ b/OAuth/src/Repository/RefreshTokenRepository.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Repository;
+
+use InvalidArgumentException;
+use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
+use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
+use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
+use MediaWiki\Extensions\OAuth\Entity\RefreshTokenEntity;
+
+class RefreshTokenRepository extends CacheRepository implements RefreshTokenRepositoryInterface {
+
+ /**
+ * Creates a new refresh token
+ *
+ * @return RefreshTokenEntityInterface|null
+ */
+ public function getNewRefreshToken() {
+ return new RefreshTokenEntity();
+ }
+
+ /**
+ * Create a new refresh token_name.
+ *
+ * @param RefreshTokenEntityInterface $refreshTokenEntity
+ *
+ * @throws UniqueTokenIdentifierConstraintViolationException
+ */
+ public function persistNewRefreshToken( RefreshTokenEntityInterface $refreshTokenEntity ) {
+ if ( !$refreshTokenEntity instanceof RefreshTokenEntity ) {
+ throw new InvalidArgumentException(
+ '$refreshTokenEntity must be instance of ' .
+ RefreshTokenEntity::class . ', got ' . get_class( $refreshTokenEntity ) . ' instead'
+ );
+ }
+ if ( $this->has( $refreshTokenEntity->getIdentifier() ) ) {
+ throw UniqueTokenIdentifierConstraintViolationException::create();
+ }
+
+ $this->set(
+ $refreshTokenEntity->getIdentifier(),
+ $refreshTokenEntity->jsonSerialize(),
+ $refreshTokenEntity->getExpiryDateTime()->getTimestamp()
+ );
+ }
+
+ /**
+ * Revoke the refresh token.
+ *
+ * @param string $tokenId
+ */
+ public function revokeRefreshToken( $tokenId ) {
+ $this->delete( $tokenId );
+ }
+
+ /**
+ * Check if the refresh token has been revoked.
+ *
+ * @param string $tokenId
+ *
+ * @return bool Return true if this token has been revoked
+ */
+ public function isRefreshTokenRevoked( $tokenId ) {
+ return $this->has( $tokenId ) === false;
+ }
+
+ /**
+ * Get object type for session key
+ *
+ * @return string
+ */
+ protected function getCacheKeyType(): string {
+ return "RefreshToken";
+ }
+}
diff --git a/OAuth/src/Repository/ScopeRepository.php b/OAuth/src/Repository/ScopeRepository.php
new file mode 100644
index 00000000..5c6a5979
--- /dev/null
+++ b/OAuth/src/Repository/ScopeRepository.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Repository;
+
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+use MediaWiki\Extensions\OAuth\Entity\ScopeEntity;
+use MediaWiki\Extensions\OAuth\Entity\UserEntity;
+use MWGrants;
+
+class ScopeRepository implements ScopeRepositoryInterface {
+ /**
+ * @var array
+ */
+ protected $allowedScopes = [
+ '#default',
+ 'mwoauth-authonly',
+ 'mwoauth-authonlyprivate'
+ ];
+
+ public function __construct() {
+ $this->allowedScopes = array_merge( $this->allowedScopes, MWGrants::getValidGrants() );
+ }
+
+ /**
+ * Return information about a scope.
+ *
+ * @param string $identifier The scope identifier
+ *
+ * @return ScopeEntityInterface|null
+ */
+ public function getScopeEntityByIdentifier( $identifier ) {
+ if ( in_array( $identifier, $this->allowedScopes, true ) ) {
+ return new ScopeEntity( $identifier );
+ }
+
+ return null;
+ }
+
+ /**
+ * Given a client, grant type and optional user identifier
+ * validate the set of scopes requested are valid and optionally
+ * append additional scopes or remove requested scopes.
+ *
+ * @param ScopeEntityInterface[] $scopes
+ * @param string $grantType
+ * @param ClientEntityInterface|ClientEntity $clientEntity
+ * @param null|string $userIdentifier
+ *
+ * @return ScopeEntityInterface[]
+ */
+ public function finalizeScopes( array $scopes, $grantType,
+ ClientEntityInterface $clientEntity, $userIdentifier = null ) {
+ $scopes = $this->replaceDefaultScope( $scopes, $clientEntity );
+
+ if ( $grantType !== 'authorization_code' ) {
+ // For grants that do not require approval,
+ // just filter out the scopes that are not allowed for the client
+ return array_filter(
+ $scopes,
+ function ( ScopeEntityInterface $scope ) use ( $clientEntity ) {
+ return in_array( $scope->getIdentifier(), $clientEntity->getGrants(), true );
+ }
+ );
+ }
+ if ( !is_numeric( $userIdentifier ) ) {
+ return [];
+ }
+
+ $mwUser = Utils::getLocalUserFromCentralId( $userIdentifier );
+ $userEntity = UserEntity::newFromMWUser( $mwUser );
+ if ( $userEntity === null ) {
+ return [];
+ }
+
+ // Filter out not approved scopes
+ try {
+ $approval = $clientEntity->getCurrentAuthorization( $mwUser, wfWikiID() );
+ $approvedScopeIds = $approval->getGrants();
+ } catch ( MWOAuthException $ex ) {
+ $approvedScopeIds = [];
+ }
+
+ return array_filter(
+ $scopes,
+ function ( ScopeEntityInterface $scope ) use ( $approvedScopeIds ) {
+ return in_array( $scope->getIdentifier(), $approvedScopeIds, true );
+ }
+ );
+ }
+
+ /**
+ * Detect "#default" scope and replace it with all client's allowed scopes
+ *
+ * @param array $scopes
+ * @param ClientEntityInterface|ClientEntity $client
+ * @return array
+ */
+ private function replaceDefaultScope( array $scopes, ClientEntityInterface $client ) {
+ // Normally, #default scope would be an only scope set, but go through whole array in case
+ // someone explicitly made a request with that scope set
+ $index = array_search( '#default', array_map( function ( ScopeEntityInterface $scope ) {
+ return $scope->getIdentifier();
+ }, $scopes ) );
+
+ if ( $index === false ) {
+ return $scopes;
+ }
+
+ return $client->getScopes();
+ }
+}
diff --git a/OAuth/src/ResourceServer.php b/OAuth/src/ResourceServer.php
new file mode 100644
index 00000000..c5f715e6
--- /dev/null
+++ b/OAuth/src/ResourceServer.php
@@ -0,0 +1,232 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+use MediaWiki\Extensions\OAuth\Entity\ScopeEntity;
+use MediaWiki\Extensions\OAuth\Repository\AccessTokenRepository;
+use MediaWiki\Extensions\OAuth\Repository\ScopeRepository;
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Rest\HttpException;
+use MWException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use User;
+use WebRequest;
+
+class ResourceServer {
+ /** @var ResourceServerMiddleware */
+ protected $middleware;
+ /** @var User */
+ protected $user;
+ /** @var ClientEntity */
+ protected $client;
+ /** @var ScopeEntity[] */
+ protected $scopes;
+ /** @var string */
+ protected $accessTokenId;
+ /** @var bool */
+ protected $verified = false;
+
+ public static function factory() {
+ $config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'mwoauth' );
+ return new static( $config->get( 'OAuth2PublicKey' ) );
+ }
+
+ /**
+ * @param string $publicKey
+ */
+ protected function __construct( $publicKey ) {
+ $accessTokenRepository = new AccessTokenRepository();
+
+ $server = new \League\OAuth2\Server\ResourceServer(
+ $accessTokenRepository,
+ $publicKey
+ );
+ $this->middleware = new ResourceServerMiddleware( $server );
+ }
+
+ /**
+ * Check if the request is an OAuth2 request
+ *
+ * @param WebRequest|ServerRequestInterface $request
+ * @return bool
+ */
+ public static function isOAuth2Request( $request ) {
+ $authHeader = $request->getHeader( 'authorization' );
+
+ // Normalize to array
+ if ( is_string( $authHeader ) ) {
+ $authHeader = [ $authHeader ];
+ }
+ if ( !empty( $authHeader ) && strpos( $authHeader[0], 'Bearer' ) === 0 ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ * @param callable $callback
+ * @return ResponseInterface
+ */
+ public function verify( $request, $response, $callback ) {
+ $this->verified = false;
+
+ return $this->middleware->__invoke(
+ $request,
+ $response,
+ function ( $request, $response ) use ( $callback ) {
+ $this->setVerifiedInfo( $request );
+ return $callback( $request, $response );
+ }
+ );
+ }
+
+ /**
+ * @return User
+ * @throws MWOAuthException
+ */
+ public function getUser() {
+ $this->assertVerified();
+ return $this->user;
+ }
+
+ /**
+ * @return ClientEntity
+ * @throws MWOAuthException
+ */
+ public function getClient() {
+ $this->assertVerified();
+ return $this->client;
+ }
+
+ /**
+ * @return ScopeEntity[]
+ * @throws MWOAuthException
+ */
+ public function getScopes() {
+ $this->assertVerified();
+ return $this->scopes;
+ }
+
+ /**
+ * Get access token this request was made with
+ *
+ * @return string
+ * @throws MWOAuthException
+ */
+ public function getAccessTokenId() {
+ $this->assertVerified();
+ return $this->accessTokenId;
+ }
+
+ /**
+ * Check if the scope is allowed
+ *
+ * @param string|ScopeEntityInterface $scope
+ * @return bool
+ * @throws MWOAuthException
+ */
+ public function isScopeAllowed( $scope ) {
+ $this->assertVerified();
+
+ if ( $scope instanceof ScopeEntityInterface ) {
+ $scope = $scope->getIdentifier();
+ }
+
+ return isset( $this->scopes[$scope] );
+ }
+
+ /**
+ * Read out the verified request and get relevant information
+ *
+ * @param ServerRequestInterface $request
+ * @throws HttpException
+ */
+ public function setVerifiedInfo( ServerRequestInterface $request ) {
+ $this->setUser( $request );
+ $this->setClient( $request );
+ $this->setScopes( $request );
+ $this->setAccessTokenId( $request );
+
+ $this->verified = true;
+ }
+
+ /**
+ * Set authorized user to the global context
+ *
+ * @param ServerRequestInterface $request
+ * @throws HttpException
+ */
+ private function setUser( ServerRequestInterface $request ) {
+ $userId = $request->getAttribute( 'oauth_user_id', 0 );
+ if ( !$userId ) {
+ // Set anon user when no user id is present in the AT (machine grant)
+ $this->user = User::newFromId( 0 );
+ return;
+ }
+
+ try {
+ $user = Utils::getLocalUserFromCentralId( $userId );
+ } catch ( MWException $ex ) {
+ throw new HttpException( $ex->getMessage(), 403 );
+ }
+
+ $this->user = $user;
+ }
+
+ /**
+ * Set the ClientEntity from validated request
+ *
+ * @param ServerRequestInterface $request
+ * @throws HttpException
+ */
+ private function setClient( ServerRequestInterface $request ) {
+ $this->client = ClientEntity::newFromKey(
+ Utils::getCentralDB( DB_REPLICA ),
+ $request->getAttribute( 'oauth_client_id' )
+ );
+ if ( !$this->client || $this->client->getOAuthVersion() !== Consumer::OAUTH_VERSION_2 ) {
+ throw new HttpException( 'Client represented by given access token is invalid', 403 );
+ }
+ }
+
+ /**
+ * Set validated scopes
+ *
+ * @param ServerRequestInterface $request
+ */
+ private function setScopes( ServerRequestInterface $request ) {
+ $scopeNames = $request->getAttribute( 'oauth_scopes', [] );
+ $scopeRepo = new ScopeRepository();
+ foreach ( $scopeNames as $scopeName ) {
+ $scope = $scopeRepo->getScopeEntityByIdentifier( $scopeName );
+ if ( !$scope ) {
+ continue;
+ }
+ $this->scopes[$scope->getIdentifier()] = $scope;
+ }
+ }
+
+ /**
+ * Set the access token this request was made with
+ *
+ * @param ServerRequestInterface $request
+ */
+ private function setAccessTokenId( ServerRequestInterface $request ) {
+ $this->accessTokenId = $request->getAttribute( 'oauth_access_token_id' );
+ }
+
+ private function assertVerified() {
+ if ( !$this->verified ) {
+ throw new MWOAuthException( 'mwoauth-oauth2-error-request-not-verified' );
+ }
+ }
+}
diff --git a/OAuth/src/Response.php b/OAuth/src/Response.php
new file mode 100644
index 00000000..890ccb37
--- /dev/null
+++ b/OAuth/src/Response.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+use MediaWiki\Rest\Response as RestResponse;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+class Response extends RestResponse implements ResponseInterface {
+
+ public function __construct( $bodyContents = '' ) {
+ parent::__construct( $bodyContents );
+ }
+
+ /**
+ * Return an instance with the specified HTTP protocol version.
+ *
+ * The version string MUST contain only the HTTP version number (e.g.,
+ * "1.1", "1.0").
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new protocol version.
+ *
+ * @param string $version HTTP protocol version
+ * @return static
+ */
+ public function withProtocolVersion( $version ) {
+ $response = clone $this;
+ $response->setProtocolVersion( $version );
+ return $response;
+ }
+
+ /**
+ * Return an instance with the provided value replacing the specified header.
+ *
+ * While header names are case-insensitive, the casing of the header will
+ * be preserved by this function, and returned from getHeaders().
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new and/or updated header and value.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @param string|string[] $value Header value(s).
+ * @return static
+ * @throws \InvalidArgumentException for invalid header names or values.
+ */
+ public function withHeader( $name, $value ) {
+ $response = clone $this;
+ $response->setHeader( $name, $value );
+ return $response;
+ }
+
+ /**
+ * Return an instance with the specified header appended with the given value.
+ *
+ * Existing values for the specified header will be maintained. The new
+ * value(s) will be appended to the existing list. If the header did not
+ * exist previously, it will be added.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new header and/or value.
+ *
+ * @param string $name Case-insensitive header field name to add.
+ * @param string|string[] $value Header value(s).
+ * @return static
+ * @throws \InvalidArgumentException for invalid header names or values.
+ */
+ public function withAddedHeader( $name, $value ) {
+ $response = clone $this;
+ $response->addHeader( $name, $value );
+ return $response;
+ }
+
+ /**
+ * Return an instance without the specified header.
+ *
+ * Header resolution MUST be done without case-sensitivity.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that removes
+ * the named header.
+ *
+ * @param string $name Case-insensitive header field name to remove.
+ * @return static
+ */
+ public function withoutHeader( $name ) {
+ $response = clone $this;
+ $response->removeHeader( $name );
+ return $response;
+ }
+
+ /**
+ * Return an instance with the specified message body.
+ *
+ * The body MUST be a StreamInterface object.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return a new instance that has the
+ * new body stream.
+ *
+ * @param StreamInterface $body Body.
+ * @return static
+ * @throws \InvalidArgumentException When the body is not valid.
+ */
+ public function withBody( StreamInterface $body ) {
+ $response = clone $this;
+ $response->setBody( $body );
+ return $response;
+ }
+
+ /**
+ * Return an instance with the specified status code and, optionally, reason phrase.
+ *
+ * If no reason phrase is specified, implementations MAY choose to default
+ * to the RFC 7231 or IANA recommended reason phrase for the response's
+ * status code.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated status and reason phrase.
+ *
+ * @link http://tools.ietf.org/html/rfc7231#section-6
+ * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+ * @param int $code The 3-digit integer result code to set.
+ * @param string $reasonPhrase The reason phrase to use with the
+ * provided status code; if none is provided, implementations MAY
+ * use the defaults as suggested in the HTTP specification.
+ * @return static
+ * @throws \InvalidArgumentException For invalid status code arguments.
+ */
+ public function withStatus( $code, $reasonPhrase = '' ) {
+ $response = clone $this;
+ $response->setStatus( $code, $reasonPhrase );
+ return $response;
+ }
+}
diff --git a/OAuth/src/Rest/Handler/AccessToken.php b/OAuth/src/Rest/Handler/AccessToken.php
new file mode 100644
index 00000000..2ddc90d4
--- /dev/null
+++ b/OAuth/src/Rest/Handler/AccessToken.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Rest\Handler;
+
+use GuzzleHttp\Psr7\ServerRequest;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAccessTokens;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\ClientCredentials;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\RefreshToken;
+use MediaWiki\Extensions\OAuth\Response;
+use MWExceptionHandler;
+use Throwable;
+use Wikimedia\ParamValidator\ParamValidator;
+
+/**
+ * Handles the oauth2/access_token endpoint, which can be used after the user has returned from
+ * the authorization dialog to trade the off the received authorization code for an access token.
+ */
+class AccessToken extends AuthenticationHandler {
+
+ const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials';
+ const GRANT_TYPE_AUTHORIZATION_CODE = 'authorization_code';
+ const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token';
+
+ /**
+ * @inheritDoc
+ */
+ public function execute() {
+ $response = new Response();
+
+ try {
+ if ( $this->queuedError ) {
+ throw $this->queuedError;
+ }
+ $request = ServerRequest::fromGlobals()->withParsedBody(
+ $this->getValidatedParams()
+ );
+
+ $authProvider = $this->getAuthorizationProvider();
+ return $authProvider->getAccessTokens( $request, $response );
+ } catch ( OAuthServerException $exception ) {
+ return $this->errorResponse( $exception, $response );
+ } catch ( Throwable $exception ) {
+ MWExceptionHandler::logException( $exception );
+ return $this->errorResponse(
+ OAuthServerException::serverError( $exception->getMessage(), $exception ),
+ $response
+ );
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getParamSettings() {
+ return [
+ 'grant_type' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => [
+ self::GRANT_TYPE_CLIENT_CREDENTIALS,
+ self::GRANT_TYPE_AUTHORIZATION_CODE,
+ self::GRANT_TYPE_REFRESH_TOKEN,
+ ],
+ ParamValidator::PARAM_REQUIRED => true,
+ ],
+ 'client_id' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'client_secret' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'redirect_uri' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'scope' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'code' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'refresh_token' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'code_verifier' => [
+ self::PARAM_SOURCE => 'post',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ]
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ protected function getGrantKey() {
+ return 'grant_type';
+ }
+
+ /**
+ * @param string $grantKey
+ * @return string|false
+ */
+ protected function getGrantClass( $grantKey ) {
+ switch ( $grantKey ) {
+ case static::GRANT_TYPE_AUTHORIZATION_CODE:
+ return AuthorizationCodeAccessTokens::class;
+ case static::GRANT_TYPE_CLIENT_CREDENTIALS:
+ return ClientCredentials::class;
+ case static::GRANT_TYPE_REFRESH_TOKEN:
+ return RefreshToken::class;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/OAuth/src/Rest/Handler/AuthenticationHandler.php b/OAuth/src/Rest/Handler/AuthenticationHandler.php
new file mode 100644
index 00000000..f44a0b63
--- /dev/null
+++ b/OAuth/src/Rest/Handler/AuthenticationHandler.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Rest\Handler;
+
+use Config;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\AccessToken as AccessTokenProvider;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAuthorization;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Response;
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Rest\Handler;
+use MediaWiki\Rest\HttpException;
+use MediaWiki\Rest\Response as RestResponse;
+use MediaWiki\Rest\StringStream;
+use MediaWiki\Rest\Validator\Validator;
+use Psr\Http\Message\ResponseInterface;
+use RequestContext;
+use User;
+
+abstract class AuthenticationHandler extends Handler {
+
+ /**
+ * @var User
+ */
+ protected $user;
+
+ /**
+ * @var Config
+ */
+ protected $config;
+
+ /**
+ * @var OAuthServerException|null
+ */
+ protected $queuedError;
+
+ /**
+ * @return AuthenticationHandler
+ */
+ public static function factory() {
+ $centralId = Utils::getCentralIdFromLocalUser( RequestContext::getMain()->getUser() );
+ $user = $centralId ? Utils::getLocalUserFromCentralId( $centralId ) : User::newFromId( 0 );
+ $config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'mwoauth' );
+ // @phan-suppress-next-line PhanTypeInstantiateAbstractStatic
+ return new static( $user, $config );
+ }
+
+ /**
+ * @param User $user
+ * @param Config $config
+ */
+ protected function __construct( User $user, Config $config ) {
+ $this->user = $user;
+ $this->config = $config;
+ }
+
+ /**
+ * We do not want any permission checks
+ *
+ * @return bool
+ */
+ public function needsReadAccess() {
+ return false;
+ }
+
+ /**
+ * We do not want any permission checks
+ *
+ * @return bool
+ */
+ public function needsWriteAccess() {
+ return false;
+ }
+
+ /**
+ * @throws HttpException
+ * @return AccessTokenProvider|AuthorizationCodeAuthorization
+ */
+ protected function getAuthorizationProvider() {
+ $grantKey = $this->getGrantKey();
+ $validated = $this->getValidatedParams();
+ $grantKeyValue = $validated[$grantKey];
+
+ $class = $this->getGrantClass( $grantKeyValue );
+ if ( !$class || !is_callable( [ $class, 'factory' ] ) ) {
+ throw new HttpException( 'invalid_request', 400 );
+ }
+
+ /** @var AccessTokenProvider|AuthorizationCodeAuthorization $authProvider */
+ $authProvider = $class::factory();
+ '@phan-var AccessTokenProvider|AuthorizationCodeAuthorization $authProvider';
+ return $authProvider;
+ }
+
+ public function validate( Validator $restValidator ) {
+ try {
+ parent::validate( $restValidator );
+ } catch ( HttpException $exception ) {
+ // Catch and store any validation errors, so they can be thrown
+ // during the execution, and get caught by appropriate error handling code
+ $type = $exception->getErrorData()['error'] ?? 'parameter-validation-failed';
+ if ( $type === 'parameter-validation-failed' ) {
+ $missingParam = $exception->getErrorData()['name'] ?? '';
+ return $this->queueError( OAuthServerException::invalidRequest( $missingParam ) );
+ }
+ $this->queueError( OAuthServerException::serverError( $exception->getMessage() ) );
+ }
+ }
+
+ /**
+ * @param OAuthServerException $ex
+ */
+ protected function queueError( OAuthServerException $ex ) {
+ // If already set, do not override, since we cannot throw more than one error,
+ // and it will probably be more useful to throw first error that occurred
+ if ( !$this->queuedError ) {
+ $this->queuedError = $ex;
+ }
+ }
+
+ /**
+ * @param array $query
+ * @return string
+ */
+ protected function getQueryParamsCgi( $query = [] ) {
+ $queryParams = $this->getRequest()->getQueryParams();
+ unset( $queryParams['title'] );
+
+ $queryParams = array_merge( $queryParams, $query );
+ return wfArrayToCgi( $queryParams );
+ }
+
+ /**
+ * @param OAuthServerException $exception
+ * @param Response|null $response
+ * @return ResponseInterface|RestResponse
+ */
+ protected function errorResponse( $exception, $response = null ) {
+ $response = $response ?? new Response();
+ $response = $exception->generateHttpResponse( $response );
+ if ( $exception->hasRedirect() || $this->getRequest()->getMethod() === 'POST' ) {
+ return $response;
+ }
+
+ $out = RequestContext::getMain()->getOutput();
+ // TODO: Should we include message/hint eventhough they are not localized?
+ $out->showErrorPage(
+ 'mwoauth-error',
+ $this->getLocalizedErrorMessage( $exception->getErrorType() )
+ );
+
+ ob_start();
+ $out->output();
+ $html = ob_get_clean();
+
+ $response = $this->getResponseFactory()->create();
+ $stream = new StringStream( $html );
+ $response->setHeader( 'Content-Type', 'text/html' );
+ $response->setBody( $stream );
+
+ return $response;
+ }
+
+ /**
+ * @param string $type
+ * @return string
+ */
+ private function getLocalizedErrorMessage( $type ) {
+ $map = [
+ 'invalid_client' => 'mwoauth-oauth2-error-invalid-client',
+ 'server_error' => 'mwoauth-oauth2-error-server-error',
+ 'invalid_request' => 'mwoauth-oauth2-error-invalid-request',
+ 'unauthorized_client' => 'mwoauth-oauth2-error-unauthorized-client',
+ 'access_denied' => 'mwoauth-oauth2-error-access-denied',
+ 'unsupported_response_type' => 'mwoauth-oauth2-error-unsupported-response-type',
+ 'invalid_scope' => 'mwoauth-oauth2-error-invalid-scope',
+ 'temporarily_unavailable' => 'mwoauth-oauth2-error-temporarily-unavailable'
+ ];
+ if ( isset( $map[$type] ) ) {
+ return $map[$type];
+ }
+
+ return 'mwoauth-oauth2-error-server-error';
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function getGrantKey();
+
+ /**
+ * @param string $grantKey
+ * @return string|false
+ */
+ abstract protected function getGrantClass( $grantKey );
+}
diff --git a/OAuth/src/Rest/Handler/Authorize.php b/OAuth/src/Rest/Handler/Authorize.php
new file mode 100644
index 00000000..a1dd71b6
--- /dev/null
+++ b/OAuth/src/Rest/Handler/Authorize.php
@@ -0,0 +1,264 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Rest\Handler;
+
+use Exception;
+use GuzzleHttp\Psr7\ServerRequest;
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAuthorization;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+use MediaWiki\Extensions\OAuth\Entity\UserEntity;
+use MediaWiki\Extensions\OAuth\Exception\ClientApprovalDenyException;
+use MediaWiki\Extensions\OAuth\Response;
+use MediaWiki\Rest\Response as RestResponse;
+use MWException;
+use MWExceptionHandler;
+use SpecialPage;
+use Throwable;
+use User;
+use Wikimedia\ParamValidator\ParamValidator;
+
+/**
+ * Handles the oauth2/authorize endpoint, which displays an authorization dialog to the user if
+ * needed (by redirecting to Special:OAuth/approve), and returns an authorization code that can be
+ * traded for the access token.
+ */
+class Authorize extends AuthenticationHandler {
+ const RESPONSE_TYPE_CODE = 'code';
+
+ /**
+ * @inheritDoc
+ */
+ public function execute() {
+ $response = new Response();
+
+ try {
+ if ( $this->queuedError ) {
+ throw $this->queuedError;
+ }
+ $request = ServerRequest::fromGlobals()->withQueryParams(
+ $this->getValidatedParams()
+ );
+ // Note: Owner-only clients can only use client_credentials grant
+ // so would be rejected from this endpoint with invalid_client error
+ // automatically, no need for additional checks
+ if ( !$this->user instanceof User || $this->user->isAnon() ) {
+ return $this->getLoginRedirectResponse();
+ }
+
+ $authProvider = $this->getAuthorizationProvider();
+ $authProvider->setUser( $this->user );
+ /** @var AuthorizationRequest $authRequest */
+ $authRequest = $authProvider->init( $request );
+ $this->setValidScopes( $authRequest );
+ if ( !$authProvider->needsUserApproval() ) {
+ return $authProvider->authorize( $authRequest, $response );
+ }
+
+ if ( $this->getValidatedParams()['approval_cancel'] ) {
+ throw new ClientApprovalDenyException( $authRequest->getRedirectUri() );
+ }
+
+ if (
+ $this->getValidatedParams()['approval_pass'] &&
+ $this->checkApproval( $authRequest )
+ ) {
+ $authRequest->setAuthorizationApproved( true );
+ return $authProvider->authorize( $authRequest, $response );
+ }
+
+ return $this->getApprovalRedirectResponse( $authRequest );
+ } catch ( OAuthServerException $ex ) {
+ return $this->errorResponse( $ex, $response );
+ } catch ( Throwable $ex ) {
+ MWExceptionHandler::logException( $ex );
+ return $this->errorResponse(
+ OAuthServerException::serverError( $ex->getMessage() ),
+ $response
+ );
+ }
+ }
+
+ protected function setValidScopes( AuthorizationRequest &$authRequest ) {
+ /** @var ClientEntity $client */
+ $client = $authRequest->getClient();
+ '@phan-var ClientEntity $client';
+
+ $scopes = $this->getValidatedParams()['scope'];
+ if ( !$scopes ) {
+ // No scope parameter
+ $authRequest->setScopes(
+ $client->getScopes()
+ );
+ return;
+ }
+ // Trim off any not allowed scopes
+ $allowedScopes = $client->getGrants();
+
+ $authRequest->setScopes( array_filter(
+ $authRequest->getScopes(),
+ function ( ScopeEntityInterface $scope ) use ( $allowedScopes ) {
+ return in_array( $scope->getIdentifier(), $allowedScopes );
+ }
+ ) );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getParamSettings() {
+ return [
+ 'response_type' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => [
+ self::RESPONSE_TYPE_CODE
+ ],
+ ParamValidator::PARAM_REQUIRED => true,
+ ],
+ 'client_id' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => true,
+ ],
+ 'redirect_uri' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'scope' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'state' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'code_challenge' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'code_challenge_method' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => [
+ 'plain',
+ 'S256'
+ ],
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'approval_cancel' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ],
+ 'approval_pass' => [
+ self::PARAM_SOURCE => 'query',
+ ParamValidator::PARAM_TYPE => 'string',
+ ParamValidator::PARAM_REQUIRED => false,
+ ]
+ ];
+ }
+
+ /**
+ * @param AuthorizationRequest $authRequest
+ * @return RestResponse
+ * @throws MWException
+ */
+ private function getApprovalRedirectResponse( AuthorizationRequest $authRequest ) {
+ return $this->getResponseFactory()->createTemporaryRedirect(
+ SpecialPage::getTitleFor( 'OAuth', 'approve' )->getFullURL( [
+ 'returnto' => $this->getRequest()->getUri()->getPath(),
+ 'returntoquery' => $this->getQueryParamsCgi(),
+ 'client_id' => $authRequest->getClient()->getIdentifier(),
+ 'oauth_version' => ClientEntity::OAUTH_VERSION_2,
+ 'scope' => implode( ' ', array_map( function ( ScopeEntityInterface $scope ) {
+ return $scope->getIdentifier();
+ }, $authRequest->getScopes() ) )
+ ] )
+ );
+ }
+
+ private function getLoginRedirectResponse() {
+ return $this->getResponseFactory()->createTemporaryRedirect(
+ SpecialPage::getTitleFor( 'Userlogin' )->getFullURL( [
+ 'returnto' => SpecialPage::getTitleFor( 'OAuth', 'rest_redirect' ),
+ 'returntoquery' => $this->getQueryParamsCgi( [
+ 'rest_url' => $this->getRequest()->getUri()->getPath()
+ ] ),
+ ] )
+ );
+ }
+
+ /**
+ * @return string
+ */
+ protected function getGrantKey() {
+ return 'response_type';
+ }
+
+ /**
+ * @param string $grantKey
+ * @return string|false
+ */
+ protected function getGrantClass( $grantKey ) {
+ switch ( $grantKey ) {
+ case static::RESPONSE_TYPE_CODE:
+ return AuthorizationCodeAuthorization::class;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Check if user has approved the client, and scopes it requested
+ *
+ * @param AuthorizationRequest $authRequest
+ * @return bool
+ */
+ private function checkApproval( AuthorizationRequest $authRequest ) {
+ /** @var ClientEntity $client */
+ $client = $authRequest->getClient();
+ '@phan-var ClientEntity $client';
+
+ /** @var UserEntity $userEntity */
+ $userEntity = $authRequest->getUser();
+ '@phan-var UserEntity $userEntity';
+
+ try {
+ $approval = $client->getCurrentAuthorization(
+ $userEntity->getMwUser(),
+ wfWikiID()
+ );
+ } catch ( Exception $ex ) {
+ return false;
+ }
+
+ if ( !$approval ) {
+ return false;
+ }
+
+ // Scopes in OAuth 1.0 are called grants
+ $scopes = $approval->getGrants();
+ $requestedScopes = $this->getFlatScopes( $authRequest->getScopes() );
+ $missing = array_diff( $requestedScopes, $scopes );
+ if ( !empty( $missing ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param ScopeEntityInterface[] $scopeEntities
+ * @return string[]
+ */
+ private function getFlatScopes( $scopeEntities ) {
+ return array_map( function ( ScopeEntityInterface $scope ) {
+ return $scope->getIdentifier();
+ }, $scopeEntities );
+ }
+}
diff --git a/OAuth/src/Rest/Handler/Resource.php b/OAuth/src/Rest/Handler/Resource.php
new file mode 100644
index 00000000..2b283b4d
--- /dev/null
+++ b/OAuth/src/Rest/Handler/Resource.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Rest\Handler;
+
+use FormatJson;
+use GuzzleHttp\Psr7\ServerRequest;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\ResourceServer;
+use MediaWiki\Extensions\OAuth\Response;
+use MediaWiki\Extensions\OAuth\UserStatementProvider;
+use MediaWiki\Rest\Handler;
+use MediaWiki\Rest\HttpException;
+use MWException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Wikimedia\ParamValidator\ParamValidator;
+
+/**
+ * Handles the oauth2/resource/profile and oauth2/resource/scope endpoints, which return
+ * information about the user and the grants of the application, respectively.
+ */
+class Resource extends Handler {
+ const TYPE_PROFILE = 'profile';
+
+ /** @var ResourceServer */
+ protected $resourceServer;
+
+ /**
+ * @return static
+ */
+ public static function factory() {
+ return new static(
+ ResourceServer::factory()
+ );
+ }
+
+ /**
+ * @param ResourceServer $resourceServer
+ */
+ protected function __construct( $resourceServer ) {
+ $this->resourceServer = $resourceServer;
+ }
+
+ /**
+ * All access controls are handled over OAuth2
+ *
+ * @return bool
+ */
+ public function needsReadAccess() {
+ return false;
+ }
+
+ /**
+ * @return bool
+ */
+ public function needsWriteAccess() {
+ return false;
+ }
+
+ /**
+ * @return ResponseInterface
+ */
+ public function execute() {
+ $response = new Response();
+ $request = ServerRequest::fromGlobals()->withHeader(
+ 'authorization',
+ $this->getRequest()->getHeader( 'authorization' )
+ );
+
+ $callback = [ $this, 'doExecuteProtected' ];
+ return $this->resourceServer->verify( $request, $response, $callback );
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ * @throws HttpException
+ * @return ResponseInterface
+ * @throws MWOAuthException
+ */
+ public function doExecuteProtected( $request, $response ) {
+ $type = $this->getRequest()->getPathParam( 'type' );
+
+ switch ( $type ) {
+ case 'profile':
+ return $this->getProfile( $response );
+ case 'scopes':
+ return $this->getScopes( $response );
+ }
+
+ throw new HttpException( 'Invalid resource type', 400 );
+ }
+
+ /**
+ * Return appropriate profile info based on approved scopes
+ *
+ * @param ResponseInterface $response
+ * @return ResponseInterface
+ * @throws HttpException
+ * @throws MWOAuthException
+ */
+ private function getProfile( $response ) {
+ // Intersection between approved and requested scopes
+ $scopes = array_keys( $this->resourceServer->getScopes() );
+ $userStatementProvider = UserStatementProvider::factory(
+ $this->resourceServer->getUser(),
+ $this->resourceServer->getClient(),
+ $scopes
+ );
+
+ try {
+ $profile = $userStatementProvider->getUserProfile();
+ } catch ( MWException $ex ) {
+ throw new HttpException( $ex->getMessage(), $ex->getCode() );
+ }
+
+ return $this->respond( $response, $profile );
+ }
+
+ /**
+ * Get all available scopes client application can use
+ *
+ * @param ResponseInterface $response
+ * @return ResponseInterface
+ * @throws MWOAuthException
+ */
+ private function getScopes( $response ) {
+ $grants = $this->resourceServer->getClient()->getGrants();
+ return $this->respond( $response, [
+ 'scopes' => $grants
+ ] );
+ }
+
+ /**
+ * @param ResponseInterface $response
+ * @param array $data
+ * @return ResponseInterface
+ */
+ private function respond( $response, $data = [] ) {
+ $response->getBody()->write( FormatJson::encode( $data ) );
+ return $response;
+ }
+
+ public function getParamSettings() {
+ return [
+ 'type' => [
+ self::PARAM_SOURCE => 'path',
+ ParamValidator::PARAM_TYPE => [ 'profile', 'scopes' ],
+ ParamValidator::PARAM_REQUIRED => true,
+ ],
+ ];
+ }
+}
diff --git a/OAuth/src/SessionProvider.php b/OAuth/src/SessionProvider.php
new file mode 100644
index 00000000..8d7e5085
--- /dev/null
+++ b/OAuth/src/SessionProvider.php
@@ -0,0 +1,440 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+use ApiMessage;
+use GuzzleHttp\Psr7\ServerRequest;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthRequest;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\Extensions\OAuth\Repository\AccessTokenRepository;
+use MediaWiki\Session\SessionBackend;
+use MediaWiki\Session\SessionInfo;
+use MediaWiki\Session\SessionManager;
+use MediaWiki\Session\UserInfo;
+use User;
+use WebRequest;
+use Wikimedia\Rdbms\DBError;
+
+/**
+ * Session provider for OAuth
+ *
+ * This is a fairly standard ImmutableSessionProviderWithCookie implementation:
+ * the user identity is determined by the OAuth headers included in the
+ * request. But since we want to make sure to fail the request when OAuth
+ * headers are present but invalid, this takes the somewhat unusual step of
+ * returning a bogus SessionInfo and then hooking ApiBeforeMain to throw a
+ * fatal exception after MediaWiki is ready to handle it.
+ *
+ * It also takes advantage of the getAllowedUserRights() method for authz
+ * purposes (limiting the rights to those included in the grant), and
+ * registers some hooks to tag actions made via the provider.
+ */
+class SessionProvider extends \MediaWiki\Session\ImmutableSessionProviderWithCookie {
+
+ public function __construct( array $params = [] ) {
+ global $wgHooks;
+
+ parent::__construct( $params );
+
+ $wgHooks['ApiCheckCanExecute'][] = $this;
+ $wgHooks['RecentChange_save'][] = $this;
+ $wgHooks['MarkPatrolled'][] = $this;
+ }
+
+ /**
+ * Throw an exception, later
+ *
+ * @param string $key Key for the error message
+ * @param mixed ...$params Parameters as strings.
+ * @return SessionInfo
+ */
+ private function makeException( $key, ...$params ) {
+ global $wgHooks;
+
+ // First, schedule the throwing of the exception for later when the API
+ // is ready to catch it
+ $msg = wfMessage( $key, $params );
+ $exception = \ApiUsageException::newWithMessage( null, $msg );
+ $wgHooks['ApiBeforeMain'][] = function () use ( $exception ) {
+ throw $exception;
+ };
+
+ // Then return an appropriate SessionInfo
+ $id = $this->hashToSessionId( 'bogus' );
+ return new SessionInfo( SessionInfo::MAX_PRIORITY, [
+ 'provider' => $this,
+ 'id' => $id,
+ 'userInfo' => UserInfo::newAnonymous(),
+ 'persisted' => false,
+ ] );
+ }
+
+ public function provideSessionInfo( WebRequest $request ) {
+ // For some reason MWOAuth is restricted to be API-only.
+ if ( !defined( 'MW_API' ) && !defined( 'MW_REST_API' ) ) {
+ return null;
+ }
+
+ $oauthVersion = $this->getOAuthVersionFromRequest( $request );
+ if ( $oauthVersion === null ) {
+ // Not an OAuth request
+ return null;
+ }
+
+ $logData = [
+ 'clientip' => $request->getIP(),
+ 'user' => false,
+ 'consumer' => '',
+ 'result' => 'fail',
+ ];
+
+ $dbr = Utils::getCentralDB( DB_REPLICA );
+ $access = null;
+ try {
+ if ( $oauthVersion === Consumer::OAUTH_VERSION_2 ) {
+ $resourceServer = ResourceServer::factory();
+ $accessTokenKey = $this->verifyOAuth2Request( $resourceServer, $request );
+ $accessTokenRepo = new AccessTokenRepository();
+ $accessId = $accessTokenRepo->getApprovalId( $accessTokenKey );
+ if ( $accessId === 0 ) {
+ if (
+ $resourceServer->getUser()->getId() === 0 &&
+ $resourceServer->getClient()->getOwnerOnly() === false
+ ) {
+ // This tell us, with good degree of certainty, that the AT
+ // was issued to a machine and represents no particular user
+ $access = ConsumerAcceptance::newFromArray( [
+ 'id' => null,
+ 'wiki' => $resourceServer->getClient()->getWiki(),
+ 'userId' => 0,
+ 'consumerId' => $resourceServer->getClient()->getId(),
+ 'accessToken' => '',
+ 'accessSecret' => '',
+ 'grants' => $resourceServer->getClient()->getGrants(),
+ 'accepted' => wfTimestampNow(),
+ 'oauth_version' => Consumer::OAUTH_VERSION_2
+ ] );
+ }
+ } else {
+ $access = ConsumerAcceptance::newFromId(
+ Utils::getCentralDB( DB_REPLICA ), $accessId
+ );
+ }
+ if ( !$access ) {
+ throw new MWOAuthException( 'mwoauth-oauth2-error-create-at-no-user-approval' );
+ }
+
+ // Set the scopes that are verified for this request
+ $access->setField( 'grants', array_keys( $resourceServer->getScopes() ) );
+ } else {
+ $server = Utils::newMWOAuthServer();
+ $oauthRequest = MWOAuthRequest::fromRequest( $request );
+ $logData['consumer'] = $oauthRequest->getConsumerKey();
+ list( , $accessToken ) = $server->verify_request( $oauthRequest );
+ $accessTokenKey = $accessToken->key;
+ $access = ConsumerAcceptance::newFromToken( $dbr, $accessTokenKey );
+ }
+ } catch ( \Exception $ex ) {
+ $this->logger->info( 'Bad OAuth request from {ip}', $logData + [ 'exception' => $ex ] );
+ return $this->makeException( 'mwoauth-invalid-authorization', $ex->getMessage() );
+ }
+
+ $logData['user'] = Utils::getCentralUserNameFromId( $access->getUserId(), 'raw' );
+
+ $wiki = wfWikiID();
+ // Access token is for this wiki
+ if ( $access->getWiki() !== '*' && $access->getWiki() !== $wiki ) {
+ $this->logger->debug( 'OAuth request for wrong wiki from user {user}', $logData );
+ return $this->makeException( 'mwoauth-invalid-authorization-wrong-wiki', $wiki );
+ }
+
+ // There exists a local user
+ $localUser = Utils::getLocalUserFromCentralId( $access->getUserId() );
+ if ( !$localUser ) {
+ $localUser = User::newFromId( 0 );
+ }
+ // If there is an actual approval, but user bound to it does not exist
+ if ( $access->getId() > 0 && $localUser->getId() === 0 ) {
+ $this->logger->debug( 'OAuth request for invalid or non-local user {user}', $logData );
+ return $this->makeException( 'mwoauth-invalid-authorization-invalid-user',
+ \Message::rawParam( \Linker::makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Help:OAuth/Errors#E008',
+ 'E008',
+ true
+ ) )
+ );
+ }
+ if ( $localUser->isLocked() ||
+ ( $this->config->get( 'BlockDisablesLogin' ) && $localUser->isBlocked() )
+ ) {
+ $this->logger->debug( 'OAuth request for blocked user {user}', $logData );
+ return $this->makeException( 'mwoauth-invalid-authorization-blocked-user' );
+ }
+
+ // The consumer is approved or owned by $localUser, and is for this wiki.
+ $consumer = Consumer::newFromId( $dbr, $access->getConsumerId() );
+ if ( !$consumer->isUsableBy( $localUser ) ) {
+ $this->logger->debug(
+ 'OAuth request for consumer {consumer} not approved by user {user}', $logData
+ );
+ return $this->makeException( 'mwoauth-invalid-authorization-not-approved',
+ $consumer->getName() );
+ } elseif ( $consumer->getWiki() !== '*' && $consumer->getWiki() !== $wiki ) {
+ $this->logger->debug( 'OAuth request for consumer {consumer} to incorrect wiki', $logData );
+ return $this->makeException( 'mwoauth-invalid-authorization-wrong-wiki', $wiki );
+ }
+
+ // Ok, use this user!
+ if ( $this->sessionCookieName === null ) {
+ // We're not configured to use cookies, so concatenate some of the
+ // internal consumer-acceptance state to generate an ID.
+ $id = $this->hashToSessionId( implode( "\n", [
+ $access->getId(),
+ $access->getWiki(),
+ $access->getUserId(),
+ $access->getConsumerId(),
+ $access->getAccepted(),
+ $wiki,
+ ] ) );
+ $persisted = false;
+ $forceUse = true;
+ } else {
+ $id = $this->getSessionIdFromCookie( $request );
+ $persisted = $id !== null;
+ $forceUse = false;
+ }
+
+ $logData['result'] = 'success';
+ $this->logger->debug( 'OAuth request for consumer {consumer} by user {user}', $logData );
+
+ return new SessionInfo( SessionInfo::MAX_PRIORITY, [
+ 'provider' => $this,
+ 'id' => $id,
+ 'userInfo' => UserInfo::newFromUser( $localUser, true ),
+ 'persisted' => $persisted,
+ 'forceUse' => $forceUse,
+ 'metadata' => [
+ 'oauthVersion' => $oauthVersion,
+ 'consumerId' => $consumer->getOwnerOnly() ? null : $consumer->getId(),
+ 'key' => $accessTokenKey,
+ 'rights' => \MWGrants::getGrantRights( $access->getGrants() ),
+ ],
+ ] );
+ }
+
+ /**
+ * Determine OAuth version of the request
+ *
+ * @param WebRequest $request
+ * @return int|null if request is not using OAuth header
+ */
+ private function getOAuthVersionFromRequest( WebRequest $request ) {
+ if ( Utils::hasOAuthHeaders( $request ) ) {
+ return Consumer::OAUTH_VERSION_1;
+ }
+ if ( ResourceServer::isOAuth2Request( $request ) ) {
+ return Consumer::OAUTH_VERSION_2;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param ResourceServer &$resourceServer
+ * @param WebRequest $request
+ * @return string
+ * @throws MWOAuthException
+ */
+ private function verifyOAuth2Request( ResourceServer &$resourceServer, WebRequest $request ) {
+ $request = ServerRequest::fromGlobals()->withHeader(
+ 'authorization',
+ $request->getHeader( 'authorization' )
+ );
+
+ $response = new Response();
+ $valid = false;
+ $resourceServer->verify(
+ $request,
+ $response,
+ function ( $request, $response ) use ( &$valid ) {
+ $valid = true;
+ }
+ );
+
+ if ( $valid ) {
+ return $resourceServer->getAccessTokenId();
+ }
+
+ throw new MWOAuthException( 'mwoauth-oauth2-invalid-access-token' );
+ }
+
+ public function preventSessionsForUser( $username ) {
+ $id = Utils::getCentralIdFromUserName( $username );
+ $dbw = Utils::getCentralDB( DB_MASTER );
+
+ $dbw->startAtomic( __METHOD__ );
+ try {
+ // Remove any approvals for the user's consumers before deleting them
+ $dbw->deleteJoin(
+ 'oauth_accepted_consumer',
+ 'oauth_registered_consumer',
+ 'oaac_consumer_id',
+ 'oarc_id',
+ [ 'oarc_user_id' => $id ],
+ __METHOD__
+ );
+ $dbw->delete(
+ 'oauth_registered_consumer',
+ [ 'oarc_user_id' => $id ],
+ __METHOD__
+ );
+
+ // Remove any approvals by this user, too
+ $dbw->delete(
+ 'oauth_accepted_consumer',
+ [ 'oaac_user_id' => $id ],
+ __METHOD__
+ );
+ } catch ( DBError $e ) {
+ $dbw->rollback( __METHOD__ );
+ throw $e;
+ }
+ $dbw->endAtomic( __METHOD__ );
+ }
+
+ public function getVaryHeaders() {
+ return [
+ 'Authorization' => null,
+ ];
+ }
+
+ /**
+ * Fetch the access data, if any, for this user-session
+ * @param \User|null $user
+ * @return array|null
+ */
+ private function getSessionData( \User $user = null ) {
+ if ( $user ) {
+ $session = $user->getRequest()->getSession();
+ if ( $session->getProvider() === $this &&
+ $user->equals( $session->getUser() )
+ ) {
+ return $session->getProviderMetadata();
+ }
+ } else {
+ $session = SessionManager::getGlobalSession();
+ if ( $session->getProvider() === $this ) {
+ return $session->getProviderMetadata();
+ }
+ }
+
+ return null;
+ }
+
+ public function getAllowedUserRights( SessionBackend $backend ) {
+ if ( $backend->getProvider() !== $this ) {
+ throw new \InvalidArgumentException( 'Backend\'s provider isn\'t $this' );
+ }
+ $data = $backend->getProviderMetadata();
+ if ( $data ) {
+ return $data['rights'];
+ }
+
+ // Should never happen
+ $this->logger->debug( __METHOD__ . ': No provider metadata, returning no rights allowed' );
+ return [];
+ }
+
+ /**
+ * Disable certain API modules when used with OAuth
+ *
+ * @param \ApiBase $module
+ * @param \User $user
+ * @param string|array &$message
+ * @return bool
+ */
+ public function onApiCheckCanExecute( \ApiBase $module, \User $user, &$message ) {
+ global $wgMWOauthDisabledApiModules;
+ if ( !$this->getSessionData( $user ) ) {
+ return true;
+ }
+
+ foreach ( $wgMWOauthDisabledApiModules as $badModule ) {
+ if ( $module instanceof $badModule ) {
+ $message = ApiMessage::create(
+ [ 'mwoauth-api-module-disabled', $module->getModuleName() ],
+ 'mwoauth-api-module-disabled'
+ );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Record the fact that OAuth was used for anything added to RecentChanges.
+ *
+ * @param \RecentChange $rc
+ * @return bool true
+ */
+ public function onRecentChange_save( $rc ) {
+ $consumerId = $this->getPublicConsumerId( $rc->getPerformer() ?: null );
+ if ( $consumerId !== null ) {
+ $rc->addTags( Utils::getTagName( $consumerId ) );
+ }
+ return true;
+ }
+
+ /**
+ * Get the consumer ID of the non-owner-only OAuth consumer associated with this user, or null.
+ * @param User|null $user
+ * @return int|null
+ */
+ protected function getPublicConsumerId( User $user = null ) {
+ $data = $this->getSessionData( $user );
+ if ( $data && isset( $data['consumerId'] ) ) {
+ return $data['consumerId'];
+ }
+ return null;
+ }
+
+ /**
+ * Record the fact that OAuth was used for marking an existing RecentChange as patrolled.
+ * (RecentChange::doMarkPatrolled() does not use RecentChange::save()
+ * and therefore bypasses the above hook handler.)
+ *
+ * @param int $rcid
+ * @param User $user
+ * @param bool $wcOnlySysopsCanPatrol
+ * @param bool $auto
+ * @param string[] &$tags
+ *
+ * @return bool true
+ */
+ public function onMarkPatrolled(
+ $rcid,
+ User $user,
+ $wcOnlySysopsCanPatrol,
+ $auto,
+ array &$tags
+ ) {
+ $consumerId = $this->getPublicConsumerId( $user );
+ if ( $consumerId !== null ) {
+ $tags[] = Utils::getTagName( $consumerId );
+ }
+ return true;
+ }
+
+ /**
+ * OAuth tokens already protect against CSRF. CSRF tokens are not required.
+ *
+ * @return bool true
+ */
+ public function safeAgainstCsrf() {
+ return true;
+ }
+}
diff --git a/OAuth/src/Setup.php b/OAuth/src/Setup.php
new file mode 100644
index 00000000..8c69fd29
--- /dev/null
+++ b/OAuth/src/Setup.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+
+/**
+ * Class containing hooked functions for an OAuth environment
+ */
+class Setup {
+ const TTL_REFRESH_WINDOW = 600; // refresh if expiring in 10 minutes
+
+ /**
+ * Prevent CentralAuth from issuing centralauthtokens if we have
+ * OAuth headers in this request.
+ * @return bool
+ */
+ public static function onCentralAuthAbortCentralAuthToken() {
+ $request = \RequestContext::getMain()->getRequest();
+ return !self::isOAuthRequest( $request );
+ }
+
+ /**
+ * Prevent redirects to canonical titles, since that's not what the OAuth
+ * request signed.
+ * @param \WebRequest $request
+ * @param \Title $title
+ * @param \OutputPage $output
+ * @return bool
+ */
+ public static function onTestCanonicalRedirect( $request, $title, $output ) {
+ return !self::isOAuthRequest( $request );
+ }
+
+ protected static function isOAuthRequest( $request ) {
+ if ( Utils::hasOAuthHeaders( $request ) ) {
+ return true;
+ }
+ return ResourceServer::isOAuth2Request( $request );
+ }
+}
diff --git a/OAuth/src/UserStatementProvider.php b/OAuth/src/UserStatementProvider.php
new file mode 100644
index 00000000..ad967bd2
--- /dev/null
+++ b/OAuth/src/UserStatementProvider.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth;
+
+use Config;
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Backend\Utils;
+use MediaWiki\MediaWikiServices;
+use MWException;
+use MWGrants;
+use User;
+
+class UserStatementProvider {
+ /** @var Config */
+ protected $config;
+ /** @var User */
+ protected $user;
+ /** @var Consumer */
+ protected $consumer;
+ /** @var array */
+ protected $grants;
+
+ /**
+ * @param User $user
+ * @param Consumer $consumer
+ * @param array $grants
+ * @return static
+ */
+ public static function factory( User $user, Consumer $consumer, $grants = [] ) {
+ $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
+ return new static( $mainConfig, $user, $consumer, $grants );
+ }
+
+ /**
+ * UserStatementProvider constructor.
+ * @param Config $config
+ * @param User $user
+ * @param Consumer $consumer
+ * @param array $grants
+ */
+ protected function __construct( $config, $user, $consumer, $grants ) {
+ $this->config = $config;
+ $this->user = $user;
+ $this->consumer = $consumer;
+ $this->grants = $grants;
+ }
+
+ /**
+ * Retrieve user statement suitable for JWT encoding
+ *
+ * @return array
+ * @throws MWException
+ */
+ public function getUserStatement() {
+ $statement = [];
+
+ // Include some of the OpenID Connect attributes
+ // http://openid.net/specs/openid-connect-core-1_0.html (draft 14)
+ // Issuer Identifier for the Issuer of the response.
+ $statement['iss'] = $this->config->get( 'CanonicalServer' );
+ // Subject identifier. A locally unique and never reassigned identifier.
+ $statement['sub'] = Utils::getCentralIdFromLocalUser( $this->user );
+ // Audience(s) that this ID Token is intended for.
+ $statement['aud'] = $this->consumer->getConsumerKey();
+ // Expiration time on or after which the ID Token MUST NOT be accepted for processing.
+ $statement['exp'] = wfTimestamp() + 100;
+ // Time at which the JWT was issued.
+ $statement['iat'] = (int)wfTimestamp();
+ // TODO: Add auth_time, if we start tracking last login timestamp
+
+ $statement += $this->getUserProfile();
+
+ return $statement;
+ }
+
+ /**
+ * Retrieve user profile information
+ *
+ * @return array
+ */
+ public function getUserProfile() {
+ $profile = [];
+ // Include some MediaWiki info about the user
+ if ( !$this->user->isHidden() ) {
+ $profile['username'] = $this->user->getName();
+ $profile['editcount'] = intval( $this->user->getEditCount() );
+ $profile['confirmed_email'] = $this->user->isEmailConfirmed();
+ $profile['blocked'] = $this->user->getBlock() !== null;
+ $profile['registered'] = $this->user->getRegistration();
+ $profile['groups'] = $this->user->getEffectiveGroups();
+ $profile['rights'] = array_values( array_unique(
+ MediaWikiServices::getInstance()->getPermissionManager()->getUserPermissions( $this->user )
+ ) );
+ $profile['grants'] = $this->grants;
+
+ if ( in_array( 'mwoauth-authonlyprivate', $this->grants ) ||
+ in_array( 'viewmyprivateinfo', MWGrants::getGrantRights( $profile['grants'] ) )
+ ) {
+ // Paranoia - avoid showing the real name if the wiki is not configured to use
+ // it but it somehow exists (from past configuration, or some identity management
+ // extension). This is important as the viewmyprivateinfo grant is presented
+ // to the user differently when useRealNames() is false.
+ // Don't omit the field completely to avoid a breaking change.
+ $profile['realname'] = !in_array(
+ 'realname', $this->config->get( 'HiddenPrefs' ), true
+ ) ? $this->user->getRealName() : '';
+ $profile['email'] = $this->user->getEmail();
+ }
+ } else {
+ $profile['blocked'] = true;
+ }
+
+ return $profile;
+ }
+}
diff --git a/OAuth/tests/phpunit/AuthorizationProviderTest.php b/OAuth/tests/phpunit/AuthorizationProviderTest.php
new file mode 100644
index 00000000..f572c748
--- /dev/null
+++ b/OAuth/tests/phpunit/AuthorizationProviderTest.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests;
+
+use DateTime;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\AuthorizationProvider;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAccessTokens;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAuthorization;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\ClientCredentials;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\RefreshToken;
+use MediaWiki\Extensions\OAuth\AuthorizationProvider\IAuthorizationProvider;
+use MediaWiki\Extensions\OAuth\AuthorizationServerFactory;
+use MediaWiki\MediaWikiServices;
+use MediaWikiTestCase;
+use Psr\Log\NullLogger;
+use ReflectionClass;
+use User;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAuthorization
+ * @covers \MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\AuthorizationCodeAccessTokens
+ * @covers \MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\ClientCredentials
+ * @covers \MediaWiki\Extensions\OAuth\AuthorizationProvider\Grant\RefreshToken
+ */
+class AuthorizationProviderTest extends MediaWikiTestCase {
+
+ protected function setUp() : void {
+ parent::setUp();
+
+ $this->setMwGlobals( [
+ 'wgOAuthSecretKey' => base64_encode( random_bytes( 32 ) )
+ ] );
+ }
+
+ protected function getServer() {
+ $serverFactory = AuthorizationServerFactory::factory();
+
+ return $serverFactory->getAuthorizationServer();
+ }
+
+ protected function getOAuthConfig() {
+ $services = MediaWikiServices::getInstance();
+ return $services->getConfigFactory()->makeConfig( 'mwoauth' );
+ }
+
+ /**
+ * @dataProvider provideGrantsArguments
+ */
+ public function testSettingUser( $class ) {
+ /** @var IAuthorizationProvider $authorizationProvider */
+ $authorizationProvider = new $class(
+ $this->getOAuthConfig(),
+ $this->getServer(),
+ new NullLogger()
+ );
+
+ $authReflection = new ReflectionClass( $class );
+ $userProperty = $authReflection->getProperty( 'user' );
+ $userProperty->setAccessible( true );
+
+ $user = $this->getTestUser()->getUser();
+ $authorizationProvider->setUser( $user );
+ $actualValue = $userProperty->getValue( $authorizationProvider );
+ $this->assertInstanceOf(
+ User::class, $actualValue,
+ "Value of user set must be an instance of " . User::class .
+ ", " . get_class( $actualValue ) . " found"
+ );
+ $this->assertSame(
+ $user->getName(), $actualValue->getName(),
+ "User passed to $class must be the same as the one actually set"
+ );
+ }
+
+ /**
+ * @dataProvider provideGrantsArguments
+ */
+ public function testGrants( $class, $grantName, $needsApproval ) {
+ $server = $this->getServer();
+ /** @var IAuthorizationProvider $authorizationProvider */
+ $authorizationProvider = new $class(
+ $this->getOAuthConfig(),
+ $server,
+ new NullLogger()
+ );
+ if ( $needsApproval ) {
+ $this->assertTrue(
+ $authorizationProvider->needsUserApproval(), "$class must require user approval"
+ );
+ } else {
+ $this->assertFalse(
+ $authorizationProvider->needsUserApproval(), "$class must not require user approval"
+ );
+ }
+
+ // Test if the provider enabled corresponding grant on the server
+ $serverReflection = new ReflectionClass( get_class( $server ) );
+ $enabledGrantsProp = $serverReflection->getProperty( 'enabledGrantTypes' );
+ $enabledGrantsProp->setAccessible( true );
+ $enabledGrants = $enabledGrantsProp->getValue( $server );
+ // In our case, each class is handling a single grant, so only that grant must be enabled
+ $this->assertSame( 1, count( $enabledGrants ),
+ 'Authorization server must have exactly one grant enabled' );
+ $this->assertArrayHasKey(
+ $grantName, $enabledGrants, "Grant \"$grantName\" must be enabled for $class"
+ );
+ }
+
+ public function provideGrantsArguments() {
+ return [
+ [ AuthorizationCodeAuthorization::class, 'authorization_code', true ],
+ [ AuthorizationCodeAccessTokens::class, 'authorization_code', false ],
+ [ ClientCredentials::class, 'client_credentials', false ],
+ [ RefreshToken::class, 'refresh_token', false ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideExpirationInterval
+ * @param string $global Value for setting
+ * @param int $expect Expected DateTimeInterval->getTimestamp()
+ */
+ public function testGetGrantExpirationInterval( $global, $expect ) {
+ $this->setMwGlobals( [ 'wgOAuth2GrantExpirationInterval' => $global ] );
+
+ $server = $this->getServer();
+ /** @var IAuthorizationProvider $authorizationProvider */
+ $authorizationProvider = $this->getMockBuilder( AuthorizationProvider::class )
+ ->setConstructorArgs( [
+ $this->getOAuthConfig(),
+ $server,
+ new NullLogger()
+ ] )
+ ->getMockForAbstractClass();
+
+ $interval = TestingAccessWrapper::newFromObject( $authorizationProvider )
+ ->getGrantExpirationInterval();
+
+ // No way to get the interval directly, so add it to a 0 timestamp then extract the timestamp...
+ $actual = ( new DateTime( '@0' ) )->add( $interval )->getTimestamp();
+
+ $this->assertSame( $expect, $actual );
+ }
+
+ /**
+ * @dataProvider provideExpirationInterval
+ * @param string $global Value for setting
+ * @param int $expect Expected DateTimeInterval->getTimestamp()
+ */
+ public function testGetRefreshTokenTTL( $global, $expect ) {
+ $this->setMwGlobals( [ 'wgOAuth2RefreshTokenTTL' => $global ] );
+
+ $server = $this->getServer();
+ /** @var IAuthorizationProvider $authorizationProvider */
+ $authorizationProvider = $this->getMockBuilder( AuthorizationProvider::class )
+ ->setConstructorArgs( [
+ $this->getOAuthConfig(),
+ $server,
+ new NullLogger()
+ ] )
+ ->getMockForAbstractClass();
+
+ $interval = TestingAccessWrapper::newFromObject( $authorizationProvider )
+ ->getRefreshTokenTTL();
+
+ // No way to get the interval directly, so add it to a 0 timestamp then extract the timestamp...
+ $actual = ( new DateTime( '@0' ) )->add( $interval )->getTimestamp();
+
+ $this->assertSame( $expect, $actual );
+ }
+
+ public function provideExpirationInterval() {
+ return [
+ [ 'P30D', 2592000 ],
+ [ false, 9223371259704000000 ],
+ [ 'infinity', 9223371259704000000 ],
+ ];
+ }
+
+}
diff --git a/OAuth/tests/phpunit/Backend/MWOAuthHooksTest.php b/OAuth/tests/phpunit/Backend/MWOAuthHooksTest.php
new file mode 100644
index 00000000..116c4a46
--- /dev/null
+++ b/OAuth/tests/phpunit/Backend/MWOAuthHooksTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Backend;
+
+use MediaWiki\Extensions\OAuth\Backend\Hooks;
+use PHPUnit\Framework\TestCase;
+use Status;
+use User;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Backend\MWOAuthServer
+ */
+class MWOAuthHooksTest extends TestCase {
+
+ /**
+ * @dataProvider provideOnChangeTagCanCreate
+ */
+ public function testOnChangeTagCanCreate( $tagName, $statusOk ) {
+ $status = Status::newGood();
+ Hooks::onChangeTagCanCreate( $tagName, new User, $status );
+ $this->assertSame( $statusOk, $status->isOK() );
+ }
+
+ public function provideOnChangeTagCanCreate() {
+ return [
+ [ 'foo', true ],
+ [ 'OAuth CID', true ],
+ [ 'OAuth CID:', false ],
+ [ 'oauth cid:', false ],
+ [ 'OAuth CID: 123', false ],
+ ];
+ }
+
+}
diff --git a/OAuth/tests/phpunit/Backend/MWOAuthServerTest.php b/OAuth/tests/phpunit/Backend/MWOAuthServerTest.php
new file mode 100644
index 00000000..211d1800
--- /dev/null
+++ b/OAuth/tests/phpunit/Backend/MWOAuthServerTest.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @section LICENSE
+ * © 2017 Wikimedia Foundation and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Extensions\OAuth\Tests\Backend;
+
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthException;
+use MediaWiki\Extensions\OAuth\Backend\MWOAuthServer;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Backend\MWOAuthServer
+ */
+class MWOAuthServerTest extends \PHPUnit\Framework\TestCase {
+
+ /**
+ * @param bool $expect Expectation
+ * @param string $registeredUrl Registered callback URL
+ * @param string $got Request callback URL
+ * @param bool $isPrefix Is Callback prefix?
+ * @dataProvider provideCheckCallback
+ */
+ public function testCheckCallback( $expect, $registeredUrl, $got, $isPrefix = true ) {
+ $fixture = new MWOAuthServer( null );
+ $consumer = new StubConsumer( [
+ 'callbackIsPrefix' => $isPrefix,
+ 'callbackUrl' => $registeredUrl,
+ ] );
+
+ $method = new \ReflectionMethod( $fixture, 'checkCallback' );
+ $method->setAccessible( true );
+ $wasValid = null;
+ try {
+ $method->invoke( $fixture, $consumer, $got );
+ $wasValid = true;
+ } catch ( MWOAuthException $e ) {
+ $wasValid = false;
+ }
+ $this->assertSame( $expect, $wasValid );
+ }
+
+ public function provideCheckCallback() {
+ return [
+ // [ $expect, $registeredUrl, $got, $isPrefix=true ]
+ [ true, '', 'oob', false ],
+ [ false, 'https://host', 'https://host', false ],
+ [ true, 'https://host', 'oob' ],
+
+ [ true, 'https://host', 'https://host' ],
+ [ true, 'http://host', 'https://host' ],
+ [ true, 'https://host:1234', 'https://host:1234' ],
+ [ true, 'http://host:1234', 'https://host:1234' ],
+ [ true, 'https://host', 'https://host/path?query' ],
+ [ true, 'http://host', 'https://host/path?query' ],
+ [ true, 'https://host/path', 'https://host/path?query' ],
+ [ true, 'https://host/path?query', 'https://host/path?query' ],
+ [ true, 'https://host/path', 'https://host/path/dir2' ],
+ [ true, 'https://host/path?query', 'https://host/path?query&more' ],
+
+ [ false, 'https://host/', 'https://host' ],
+ [ false, 'https://host', 'https://host:1234' ],
+ [ false, 'https://host:4321', 'https://host:1234' ],
+ [ false, 'https://host:80', 'https://host:8099' ],
+ [ false, 'https://host/path', 'https://host:1234/path' ],
+ [ false, 'https://host/path?query', 'https://host/path' ],
+ [ false, 'https://host:8000', 'https://host:8000@evil.com' ],
+ [ false, 'https://host', 'https://hosting' ],
+ ];
+ }
+}
diff --git a/OAuth/tests/phpunit/Backend/StubConsumer.php b/OAuth/tests/phpunit/Backend/StubConsumer.php
new file mode 100644
index 00000000..3cc71c35
--- /dev/null
+++ b/OAuth/tests/phpunit/Backend/StubConsumer.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * @section LICENSE
+ * © 2017 Wikimedia Foundation and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Extensions\OAuth\Tests\Backend;
+
+class StubConsumer {
+ public $data;
+
+ public function __construct( $data ) {
+ $this->data = $data;
+ }
+
+ public function get( $key ) {
+ return $this->data[$key];
+ }
+
+ public function getId() {
+ return $this->get( 'id' );
+ }
+
+ public function getConsumerKey() {
+ return $this->get( 'consumerKey' );
+ }
+
+ public function getName() {
+ return $this->get( 'name' );
+ }
+
+ public function getUserId() {
+ return $this->get( 'userId' );
+ }
+
+ public function getVersion() {
+ return $this->get( 'version' );
+ }
+
+ public function getCallbackUrl() {
+ return $this->get( 'callbackUrl' );
+ }
+
+ public function getCallbackIsPrefix() {
+ return $this->get( 'callbackIsPrefix' );
+ }
+
+ public function getDescription() {
+ return $this->get( 'description' );
+ }
+
+ public function getEmail() {
+ return $this->get( 'email' );
+ }
+
+ public function getEmailAuthenticated() {
+ return $this->get( 'emailAuthenticated' );
+ }
+
+ public function getDeveloperAgreement() {
+ return $this->get( 'developerAgreement' );
+ }
+
+ public function getOwnerOnly() {
+ return $this->get( 'ownerOnly' );
+ }
+
+ public function getWiki() {
+ return $this->get( 'wiki' );
+ }
+
+ public function getGrants() {
+ return $this->get( 'grants' );
+ }
+
+ public function getRegistration() {
+ return $this->get( 'registration' );
+ }
+
+ public function getSecretKey() {
+ return $this->get( 'secretKey' );
+ }
+
+ public function getRsaKey() {
+ return $this->get( 'rsaKey' );
+ }
+
+ public function getRestrictions() {
+ return $this->get( 'restrictions' );
+ }
+
+ public function getStage() {
+ return $this->get( 'stage' );
+ }
+
+ public function getStageTimestamp() {
+ return $this->get( 'stageTimestamp' );
+ }
+
+ public function getDeleted() {
+ return $this->get( 'deleted' );
+ }
+
+}
diff --git a/OAuth/tests/phpunit/Entity/AccessTokenEntityTest.php b/OAuth/tests/phpunit/Entity/AccessTokenEntityTest.php
new file mode 100644
index 00000000..7c3da324
--- /dev/null
+++ b/OAuth/tests/phpunit/Entity/AccessTokenEntityTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Entity;
+
+use League\OAuth2\Server\Entities\ScopeEntityInterface;
+use MediaWiki\Extensions\OAuth\Entity\AccessTokenEntity;
+use MediaWiki\Extensions\OAuth\Entity\ScopeEntity;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Entity\AccessTokenEntity
+ */
+class AccessTokenEntityTest extends MediaWikiTestCase {
+
+ public function testProperties() {
+ $accessToken = new AccessTokenEntity(
+ Mock_ClientEntity::newMock( $this->getTestUser()->getUser(), [
+ 'consumerKey' => 'dummykey'
+ ] ),
+ [
+ new ScopeEntity( 'editpage' ),
+ new ScopeEntity( 'highvolume' )
+ ],
+ $this->getTestUser()->getUser()->getId()
+ );
+ $identifier = bin2hex( random_bytes( 40 ) );
+ $accessToken->setIdentifier( $identifier );
+
+ $this->assertSame(
+ $identifier, $accessToken->getIdentifier(),
+ 'Access token identifier should match the one set'
+ );
+ $this->assertSame(
+ $this->getTestUser()->getUser()->getId(),
+ $accessToken->getUserIdentifier(),
+ 'Access token should have the same user identifier that was passed to it'
+ );
+ $this->assertSame(
+ 'dummykey', $accessToken->getClient()->getIdentifier(),
+ 'Access token should have the same client identifier as the one that was passed'
+ );
+ $atScopes = array_map( function ( ScopeEntityInterface $scope ) {
+ return $scope->getIdentifier();
+ }, $accessToken->getScopes() );
+ $this->assertArrayEquals(
+ [ 'editpage', 'highvolume' ],
+ $atScopes,
+ 'Access tokens should have the same scopes as the ones that were passed'
+ );
+ }
+}
diff --git a/OAuth/tests/phpunit/Entity/ClientEntityTest.php b/OAuth/tests/phpunit/Entity/ClientEntityTest.php
new file mode 100644
index 00000000..130aede5
--- /dev/null
+++ b/OAuth/tests/phpunit/Entity/ClientEntityTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Entity;
+
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Entity\ClientEntity
+ */
+class ClientEntityTest extends MediaWikiTestCase {
+
+ public function testProperties() {
+ $domain = 'http://domain.com/oauth2';
+ $client = Mock_ClientEntity::newMock( $this->getTestUser()->getUser(), [
+ 'consumerKey' => '123456789',
+ 'callbackUrl' => $domain,
+ 'name' => 'Test client',
+ 'oauth2IsConfidential' => false,
+ 'oauth2GrantTypes' => [ 'client_credentials' ]
+ ] );
+
+ $this->assertSame(
+ $domain, $client->getRedirectUri(),
+ 'Redirect URI should match the one given on registration'
+ );
+ $this->assertFalse(
+ $client->isConfidential(),
+ 'Client should not be confidential'
+ );
+ $this->assertSame(
+ '123456789', $client->getConsumerKey(),
+ 'ConsumerKey should be the same as the one given on registration'
+ );
+
+ $client->setIdentifier( '987654321' );
+ $this->assertSame(
+ '987654321', $client->getConsumerKey(),
+ 'ConsumerKey should change when explicitly set'
+ );
+ $this->assertSame(
+ 'Test client', $client->getName(),
+ 'Client name should be same as the one given on registration'
+ );
+ $this->assertArrayEquals(
+ [ 'client_credentials' ], $client->getAllowedGrants(),
+ 'Allowed grants should be the same as ones given on registration'
+ );
+ }
+}
diff --git a/OAuth/tests/phpunit/Entity/Mock_ClientEntity.php b/OAuth/tests/phpunit/Entity/Mock_ClientEntity.php
new file mode 100644
index 00000000..a99ffdb8
--- /dev/null
+++ b/OAuth/tests/phpunit/Entity/Mock_ClientEntity.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Entity;
+
+use MediaWiki\Extensions\OAuth\Backend\Consumer;
+use MediaWiki\Extensions\OAuth\Entity\ClientEntity;
+use MWRestrictions;
+use User;
+
+class Mock_ClientEntity extends ClientEntity {
+ public static function newMock( User $user, $values = [] ) {
+ $now = wfTimestampNow();
+ return ClientEntity::newFromArray( array_merge( [
+ 'id' => null,
+ 'consumerKey' => '123456789',
+ 'userId' => $user->getId(),
+ 'name' => 'Test client',
+ 'description' => 'Test application',
+ 'wiki' => 'TestWiki',
+ 'version' => '1.0',
+ 'email' => $user->getEmail(),
+ 'emailAuthenticated' => $now,
+ 'callbackUrl' => 'https://example.com',
+ 'callbackIsPrefix' => true,
+ 'developerAgreement' => 1,
+ 'secretKey' => 'secretKey',
+ 'registration' => $now,
+ 'stage' => Consumer::STAGE_APPROVED,
+ 'stageTimestamp' => $now,
+ 'grants' => [ 'editpage', 'highvolume' ],
+ 'restrictions' => MWRestrictions::newDefault(),
+ 'deleted' => 0,
+ 'rsaKey' => '',
+ 'oauthVersion' => Consumer::OAUTH_VERSION_2,
+ 'ownerOnly' => false,
+ 'oauth2IsConfidential' => true,
+ 'oauth2GrantTypes' => [ 'authorization_code', 'refresh_token' ]
+ ], $values ) );
+ }
+}
diff --git a/OAuth/tests/phpunit/Entity/UserEntityTest.php b/OAuth/tests/phpunit/Entity/UserEntityTest.php
new file mode 100644
index 00000000..39f4a14a
--- /dev/null
+++ b/OAuth/tests/phpunit/Entity/UserEntityTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Entity;
+
+use MediaWiki\Extensions\OAuth\Entity\UserEntity;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Entity\UserEntity
+ */
+class UserEntityTest extends MediaWikiTestCase {
+
+ public function testProperties() {
+ $userEntity = UserEntity::newFromMWUser(
+ $this->getTestUser()->getUser()
+ );
+
+ $this->assertSame(
+ $this->getTestUser()->getUser()->getId(),
+ $userEntity->getIdentifier(),
+ 'User identifier should be the same as the id of the user it represents'
+ );
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/Mock_OAuthBaseStringRequest.php b/OAuth/tests/phpunit/Lib/Mock_OAuthBaseStringRequest.php
new file mode 100644
index 00000000..ebfadcd1
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/Mock_OAuthBaseStringRequest.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+/**
+ * A very simple class that you can pass a base-string, and then have it returned again.
+ * Used for testing the signature-methods
+ */
+class Mock_OAuthBaseStringRequest {
+ private $provided_base_string;
+ public $base_string; // legacy
+ public function __construct($bs) { $this->provided_base_string = $bs; }
+ public function get_signature_base_string() { return $this->provided_base_string; }
+}
diff --git a/OAuth/tests/phpunit/Lib/Mock_OAuthDataStore.php b/OAuth/tests/phpunit/Lib/Mock_OAuthDataStore.php
new file mode 100644
index 00000000..0cd16b83
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/Mock_OAuthDataStore.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthDataStore;
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+
+/**
+ * A mock store for testing
+ */
+class Mock_OAuthDataStore extends OAuthDataStore {
+ private $consumer;
+ private $request_token;
+ private $access_token;
+ private $nonce;
+
+ function __construct() {
+ $this->consumer = new OAuthConsumer("key", "secret", NULL);
+ $this->request_token = new OAuthToken("requestkey", "requestsecret", 1);
+ $this->access_token = new OAuthToken("accesskey", "accesssecret", 1);
+ $this->nonce = "nonce";
+ }
+
+ function lookup_consumer($consumer_key) {
+ if ($consumer_key == $this->consumer->key) return $this->consumer;
+ return NULL;
+ }
+
+ function lookup_token($consumer, $token_type, $token) {
+ $token_attrib = $token_type . "_token";
+ if ($consumer->key == $this->consumer->key
+ && $token == $this->$token_attrib->key) {
+ return $this->$token_attrib;
+ }
+ return NULL;
+ }
+
+ function lookup_nonce($consumer, $token, $nonce, $timestamp) {
+ if ($consumer->key == $this->consumer->key
+ && (($token && $token->key == $this->request_token->key)
+ || ($token && $token->key == $this->access_token->key))
+ && $nonce == $this->nonce) {
+ return $this->nonce;
+ }
+ return NULL;
+ }
+
+ function new_request_token($consumer, $callback = null) {
+ if ($consumer->key == $this->consumer->key) {
+ return $this->request_token;
+ }
+ return NULL;
+ }
+
+ function new_access_token($token, $consumer, $verifier = null) {
+ if ($consumer->key == $this->consumer->key
+ && $token->key == $this->request_token->key) {
+ return $this->access_token;
+ }
+ return NULL;
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/Mock_OAuthSignatureMethod_RSA_SHA1.php b/OAuth/tests/phpunit/Lib/Mock_OAuthSignatureMethod_RSA_SHA1.php
new file mode 100644
index 00000000..30d17aa8
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/Mock_OAuthSignatureMethod_RSA_SHA1.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_RSA_SHA1;
+
+/**
+ * A mock implementation of OAuthSignatureMethod_RSA_SHA1
+ * Always returns the signatures described in
+ * http://wiki.oauth.net/TestCases section 9.3 ("RSA-SHA1")
+ */
+class Mock_OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod_RSA_SHA1 {
+ public function fetch_private_cert(&$request) {
+ $cert = <<<EOD
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V
+A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d
+7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ
+hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H
+X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm
+uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw
+rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z
+zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn
+qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG
+WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno
+cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+
+3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8
+AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54
+Lw03eHTNQghS0A==
+-----END PRIVATE KEY-----
+EOD;
+ return $cert;
+ }
+
+ public function fetch_public_cert(&$request) {
+ $cert = <<<EOD
+-----BEGIN CERTIFICATE-----
+MIIBpjCCAQ+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAZMRcwFQYDVQQDDA5UZXN0
+IFByaW5jaXBhbDAeFw03MDAxMDEwODAwMDBaFw0zODEyMzEwODAwMDBaMBkxFzAV
+BgNVBAMMDlRlc3QgUHJpbmNpcGFsMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3gYytUvtC2JlY
+zypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp/IpH7kH41Etb
+mUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQABMA0GCSqGSIb3
+DQEBBQUAA4GBAGZLPEuJ5SiJ2ryq+CmEGOXfvlTtEL2nuGtr9PewxkgnOjZpUy+d
+4TvuXJbNQc8f4AMWL/tO9w0Fk80rWKp9ea8/df4qMq5qlFWlx6yOLQxumNOmECKb
+WpkUQDIDJEoFUzKMVuJf4KO/FJ345+BNLGgbJ6WujreoM1X/gYfdnJ/J
+-----END CERTIFICATE-----
+EOD;
+ return $cert;
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthConsumerTest.php b/OAuth/tests/phpunit/Lib/OAuthConsumerTest.php
new file mode 100644
index 00000000..2f3ce1b8
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthConsumerTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @group OAuth
+ */
+class OAuthConsumerTest extends \PHPUnit\Framework\TestCase {
+ public function testConvertToString() {
+ $consumer = new OAuthConsumer('key', 'secret');
+ $this->assertEquals('OAuthConsumer[key=key,secret=secret]', (string) $consumer);
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthRequestTest.php b/OAuth/tests/phpunit/Lib/OAuthRequestTest.php
new file mode 100644
index 00000000..ee5fcfbd
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthRequestTest.php
@@ -0,0 +1,376 @@
+<?php
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_HMAC_SHA1;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_PLAINTEXT;
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+
+/**
+ * Tests of OAuthRequest
+ *
+ * The tests works by using OAuthTestUtils::build_request
+ * to populare $_SERVER, $_GET & $_POST.
+ *
+ * Most of the base string and signature tests
+ * are either very simple or based upon
+ * http://wiki.oauth.net/TestCases
+ *
+ * @group OAuth
+ */
+class OAuthRequestTest extends \PHPUnit\Framework\TestCase {
+
+ protected static $globals = [];
+
+ public static function setUpBeforeClass() : void {
+ parent::setUpBeforeClass();
+ // can't use @backupGlobals because it tries to serialize the arrays
+ self::$globals['$_SERVER'] = $_SERVER;
+ self::$globals['$_POST'] = $_POST;
+ self::$globals['$_GET'] = $_GET;
+ }
+
+ public static function tearDownAfterClass() : void {
+ $_SERVER = self::$globals['$_SERVER'];
+ $_POST = self::$globals['$_POST'];
+ $_GET = self::$globals['$_GET'];
+ parent::tearDownAfterClass();
+ }
+
+ public function testCanGetSingleParameter() {
+ // Yes, a awesomely boring test.. But if this doesn't work, the other tests is unreliable
+ $request = new OAuthRequest('', '', array('test'=>'foo'));
+ $this->assertEquals( 'foo', $request->get_parameter('test'), 'Failed to read back parameter');
+
+ $request = new OAuthRequest('', '', array('test'=>array('foo', 'bar')));
+ $this->assertEquals( array('foo', 'bar'), $request->get_parameter('test'), 'Failed to read back parameter');
+
+ $request = new OAuthRequest('', '', array('test'=>'foo', 'bar'=>'baz'));
+ $this->assertEquals( 'foo', $request->get_parameter('test'), 'Failed to read back parameter');
+ $this->assertEquals( 'baz', $request->get_parameter('bar'), 'Failed to read back parameter');
+ }
+
+ public function testGetAllParameters() {
+ // Yes, a awesomely boring test.. But if this doesn't work, the other tests is unreliable
+ $request = new OAuthRequest('', '', array('test'=>'foo'));
+ $this->assertEquals( array('test'=>'foo'), $request->get_parameters(), 'Failed to read back parameters');
+
+ $request = new OAuthRequest('', '', array('test'=>'foo', 'bar'=>'baz'));
+ $this->assertEquals( array('test'=>'foo', 'bar'=>'baz'), $request->get_parameters(), 'Failed to read back parameters');
+
+ $request = new OAuthRequest('', '', array('test'=>array('foo', 'bar')));
+ $this->assertEquals( array('test'=>array('foo', 'bar')), $request->get_parameters(), 'Failed to read back parameters');
+ }
+
+ public function testSetParameters() {
+ $request = new OAuthRequest('', '');
+ $this->assertEquals( NULL, $request->get_parameter('test'), 'Failed to assert that non-existing parameter is NULL');
+
+ $request->set_parameter('test', 'foo');
+ $this->assertEquals( 'foo', $request->get_parameter('test'), 'Failed to set single-entry parameter');
+
+ $request->set_parameter('test', 'bar');
+ $this->assertEquals( array('foo', 'bar'), $request->get_parameter('test'), 'Failed to set single-entry parameter');
+
+ $request->set_parameter('test', 'bar', false);
+ $this->assertEquals( 'bar', $request->get_parameter('test'), 'Failed to set single-entry parameter');
+ }
+
+ public function testUnsetParameter() {
+ $request = new OAuthRequest('', '');
+ $this->assertEquals( NULL, $request->get_parameter('test'));
+
+ $request->set_parameter('test', 'foo');
+ $this->assertEquals( 'foo', $request->get_parameter('test'));
+
+ $request->unset_parameter('test');
+ $this->assertEquals( NULL, $request->get_parameter('test'), 'Failed to unset parameter');
+ }
+
+ public function testCreateRequestFromConsumerAndToken() {
+ $cons = new OAuthConsumer('key', 'kd94hf93k423kf44');
+ $token = new OAuthToken('token', 'pfkkdhi9sl3r4s00');
+
+ $request = OAuthRequest::from_consumer_and_token($cons, $token, 'POST', 'http://example.com');
+ $this->assertEquals('POST', $request->get_normalized_http_method());
+ $this->assertEquals('http://example.com', $request->get_normalized_http_url());
+ $this->assertEquals('1.0', $request->get_parameter('oauth_version'));
+ $this->assertEquals($cons->key, $request->get_parameter('oauth_consumer_key'));
+ $this->assertEquals($token->key, $request->get_parameter('oauth_token'));
+ $this->assertEquals(time(), $request->get_parameter('oauth_timestamp'));
+ $this->assertRegExp('/[0-9a-f]{32}/', $request->get_parameter('oauth_nonce'));
+ // We don't know what the nonce will be, except it'll be md5 and hence 32 hexa digits
+
+ $request = OAuthRequest::from_consumer_and_token($cons, $token, 'POST', 'http://example.com', array('oauth_nonce'=>'foo'));
+ $this->assertEquals('foo', $request->get_parameter('oauth_nonce'));
+
+ $request = OAuthRequest::from_consumer_and_token($cons, NULL, 'POST', 'http://example.com', array('oauth_nonce'=>'foo'));
+ $this->assertNull($request->get_parameter('oauth_token'));
+
+ // Test that parameters given in the $http_url instead of in the $parameters-parameter
+ // will still be picked up
+ $request = OAuthRequest::from_consumer_and_token($cons, $token, 'POST', 'http://example.com/?foo=bar');
+ $this->assertEquals('http://example.com/', $request->get_normalized_http_url());
+ $this->assertEquals('bar', $request->get_parameter('foo'));
+ }
+
+ public function testBuildRequestFromPost() {
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'foo=bar&baz=blargh');
+ $this->assertEquals(array('foo'=>'bar','baz'=>'blargh'), OAuthRequest::from_request()->get_parameters(), 'Failed to parse POST parameters');
+ }
+
+ public function testBuildRequestFromGet() {
+ OAuthTestUtils::build_request('GET', 'http://testbed/test?foo=bar&baz=blargh');
+ $this->assertEquals(array('foo'=>'bar','baz'=>'blargh'), OAuthRequest::from_request()->get_parameters(), 'Failed to parse GET parameters');
+ }
+
+ public function testBuildRequestFromHeader() {
+ $test_header = 'OAuth realm="",oauth_foo=bar,oauth_baz="bla,rgh"';
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', '', $test_header);
+ $this->assertEquals(array('oauth_foo'=>'bar','oauth_baz'=>'bla,rgh'), OAuthRequest::from_request()->get_parameters(), 'Failed to split auth-header correctly');
+ }
+
+ public function testHasProperParameterPriority() {
+ $test_header = 'OAuth realm="",oauth_foo=header';
+ OAuthTestUtils::build_request('POST', 'http://testbed/test?oauth_foo=get', 'oauth_foo=post', $test_header);
+ $this->assertEquals('header', OAuthRequest::from_request()->get_parameter('oauth_foo'), 'Loaded parameters in with the wrong priorities');
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test?oauth_foo=get', 'oauth_foo=post');
+ $this->assertEquals('post', OAuthRequest::from_request()->get_parameter('oauth_foo'), 'Loaded parameters in with the wrong priorities');
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test?oauth_foo=get');
+ $this->assertEquals('get', OAuthRequest::from_request()->get_parameter('oauth_foo'), 'Loaded parameters in with the wrong priorities');
+ }
+
+ public function testNormalizeHttpMethod() {
+ OAuthTestUtils::build_request('POST', 'http://testbed/test');
+ $this->assertEquals('POST', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: POST');
+
+ OAuthTestUtils::build_request('post', 'http://testbed/test');
+ $this->assertEquals('POST', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: post');
+
+ OAuthTestUtils::build_request('GET', 'http://testbed/test');
+ $this->assertEquals('GET', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: GET');
+
+ OAuthTestUtils::build_request('PUT', 'http://testbed/test');
+ $this->assertEquals('PUT', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: PUT');
+ }
+
+ public function testNormalizeParameters() {
+ // This is mostly repeats of OAuthUtilTest::testParseParameters & OAuthUtilTest::TestBuildHttpQuery
+
+ // Tests taken from
+ // http://wiki.oauth.net/TestCases ("Normalize Request Parameters")
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'name');
+ $this->assertEquals( 'name=', OAuthRequest::from_request()->get_signable_parameters());
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=b');
+ $this->assertEquals( 'a=b', OAuthRequest::from_request()->get_signable_parameters());
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=b&c=d');
+ $this->assertEquals( 'a=b&c=d', OAuthRequest::from_request()->get_signable_parameters());
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=x%21y&a=x+y');
+ $this->assertEquals( 'a=x%20y&a=x%21y', OAuthRequest::from_request()->get_signable_parameters());
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'x%21y=a&x=a');
+ $this->assertEquals( 'x=a&x%21y=a', OAuthRequest::from_request()->get_signable_parameters());
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=1&c=hi there&f=25&f=50&f=a&z=p&z=t');
+ $this->assertEquals( 'a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t', OAuthRequest::from_request()->get_signable_parameters());
+ }
+
+ public function testNormalizeHttpUrl() {
+ OAuthTestUtils::build_request('POST', 'http://example.com');
+ $this->assertEquals('http://example.com', OAuthRequest::from_request()->get_normalized_http_url());
+
+ OAuthTestUtils::build_request('POST', 'https://example.com');
+ $this->assertEquals('https://example.com', OAuthRequest::from_request()->get_normalized_http_url());
+
+ // Tests that http on !80 and https on !443 keeps the port
+ OAuthTestUtils::build_request('POST', 'http://example.com:8080');
+ $this->assertEquals('http://example.com:8080', OAuthRequest::from_request()->get_normalized_http_url());
+
+ OAuthTestUtils::build_request('POST', 'https://example.com:80');
+ $this->assertEquals('https://example.com:80', OAuthRequest::from_request()->get_normalized_http_url());
+
+ OAuthTestUtils::build_request('POST', 'http://example.com:443');
+ $this->assertEquals('http://example.com:443', OAuthRequest::from_request()->get_normalized_http_url());
+
+ OAuthTestUtils::build_request('POST', 'http://Example.COM');
+ $this->assertEquals('http://example.com', OAuthRequest::from_request()->get_normalized_http_url());
+
+ // Emulate silly behavior by some clients, where there Host header includes the port
+ OAuthTestUtils::build_request('POST', 'http://example.com');
+ $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT'];
+ $this->assertEquals('http://example.com', OAuthRequest::from_request()->get_normalized_http_url());
+ }
+
+ public function testBuildPostData() {
+ OAuthTestUtils::build_request('POST', 'http://example.com');
+ $this->assertEquals('', OAuthRequest::from_request()->to_postdata());
+
+ OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar');
+ $this->assertEquals('foo=bar', OAuthRequest::from_request()->to_postdata());
+
+ OAuthTestUtils::build_request('GET', 'http://example.com?foo=bar');
+ $this->assertEquals('foo=bar', OAuthRequest::from_request()->to_postdata());
+ }
+
+ public function testBuildUrl() {
+ OAuthTestUtils::build_request('POST', 'http://example.com');
+ $this->assertEquals('http://example.com', OAuthRequest::from_request()->to_url());
+
+ OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar');
+ $this->assertEquals('http://example.com?foo=bar', OAuthRequest::from_request()->to_url());
+
+ OAuthTestUtils::build_request('GET', 'http://example.com?foo=bar');
+ $this->assertEquals('http://example.com?foo=bar', OAuthRequest::from_request()->to_url());
+ }
+
+ public function testConvertToString() {
+ OAuthTestUtils::build_request('POST', 'http://example.com');
+ $this->assertEquals('http://example.com', (string) OAuthRequest::from_request());
+
+ OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar');
+ $this->assertEquals('http://example.com?foo=bar', (string) OAuthRequest::from_request());
+
+ OAuthTestUtils::build_request('GET', 'http://example.com?foo=bar');
+ $this->assertEquals('http://example.com?foo=bar', (string) OAuthRequest::from_request());
+ }
+
+ public function testBuildHeader() {
+ OAuthTestUtils::build_request('POST', 'http://example.com');
+ $this->assertEquals('Authorization: OAuth', OAuthRequest::from_request()->to_header());
+ $this->assertEquals('Authorization: OAuth realm="test"', OAuthRequest::from_request()->to_header('test'));
+
+ OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar');
+ $this->assertEquals('Authorization: OAuth', OAuthRequest::from_request()->to_header());
+ $this->assertEquals('Authorization: OAuth realm="test"', OAuthRequest::from_request()->to_header('test'));
+
+ OAuthTestUtils::build_request('POST', 'http://example.com', 'oauth_test=foo');
+ $this->assertEquals('Authorization: OAuth oauth_test="foo"', OAuthRequest::from_request()->to_header());
+ $this->assertEquals('Authorization: OAuth realm="test",oauth_test="foo"', OAuthRequest::from_request()->to_header('test'));
+
+ // Is headers supposted to be Urlencoded. More to the point:
+ // Should it be baz = bla,rgh or baz = bla%2Crgh ??
+ // - morten.fangel
+ OAuthTestUtils::build_request('POST', 'http://example.com', '', 'OAuth realm="",oauth_foo=bar,oauth_baz="bla,rgh"');
+ $this->assertEquals('Authorization: OAuth oauth_foo="bar",oauth_baz="bla%2Crgh"', OAuthRequest::from_request()->to_header());
+ $this->assertEquals('Authorization: OAuth realm="test",oauth_foo="bar",oauth_baz="bla%2Crgh"', OAuthRequest::from_request()->to_header('test'));
+ }
+
+ public function testWontBuildHeaderWithArrayInput() {
+ $this->expectException(OAuthException::class);
+ OAuthTestUtils::build_request('POST', 'http://example.com', 'oauth_foo=bar&oauth_foo=baz');
+ OAuthRequest::from_request()->to_header();
+ }
+
+ public function testBuildBaseString() {
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'n=v');
+ $this->assertEquals('POST&http%3A%2F%2Ftestbed%2Ftest&n%3Dv', OAuthRequest::from_request()->get_signature_base_string());
+
+ OAuthTestUtils::build_request('POST', 'http://testbed/test', 'n=v&n=v2');
+ $this->assertEquals('POST&http%3A%2F%2Ftestbed%2Ftest&n%3Dv%26n%3Dv2', OAuthRequest::from_request()->get_signature_base_string());
+
+ OAuthTestUtils::build_request('GET', 'http://example.com?n=v');
+ $this->assertEquals('GET&http%3A%2F%2Fexample.com&n%3Dv', OAuthRequest::from_request()->get_signature_base_string());
+
+ $params = 'oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_timestamp=1191242090';
+ $params .= '&oauth_nonce=hsu94j3884jdopsl&oauth_signature_method=PLAINTEXT&oauth_signature=ignored';
+ OAuthTestUtils::build_request('POST', 'https://photos.example.net/request_token', $params);
+ $this->assertEquals('POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_'
+ .'consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884j'
+ .'dopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestam'
+ .'p%3D1191242090%26oauth_version%3D1.0',
+ OAuthRequest::from_request()->get_signature_base_string());
+
+ $params = 'file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03';
+ $params .= '&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh';
+ $params .= '&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1';
+ OAuthTestUtils::build_request('GET', 'http://photos.example.net/photos?'.$params);
+ $this->assertEquals('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation'
+ .'.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%'
+ .'3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26o'
+ .'auth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jd'
+ .'k%26oauth_version%3D1.0%26size%3Doriginal',
+ OAuthRequest::from_request()->get_signature_base_string());
+ }
+
+ public function testBuildSignature() {
+ $params = 'file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03';
+ $params .= '&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh';
+ $params .= '&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1';
+ OAuthTestUtils::build_request('GET', 'http://photos.example.net/photos?'.$params);
+ $r = OAuthRequest::from_request();
+
+ $cons = new OAuthConsumer('key', 'kd94hf93k423kf44');
+ $token = new OAuthToken('token', 'pfkkdhi9sl3r4s00');
+
+ $hmac = new OAuthSignatureMethod_HMAC_SHA1();
+ $plaintext = new OAuthSignatureMethod_PLAINTEXT();
+
+ $this->assertEquals('tR3+Ty81lMeYAr/Fid0kMTYa/WM=', $r->build_signature($hmac, $cons, $token));
+ $this->assertEquals('kd94hf93k423kf44&pfkkdhi9sl3r4s00', $r->build_signature($plaintext, $cons, $token));
+ }
+
+ public function testSign() {
+ $params = 'file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03';
+ $params .= '&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh';
+ $params .= '&oauth_signature=__ignored__&oauth_signature_method=HMAC-SHA1';
+ OAuthTestUtils::build_request('GET', 'http://photos.example.net/photos?'.$params);
+ $r = OAuthRequest::from_request();
+
+ $cons = new OAuthConsumer('key', 'kd94hf93k423kf44');
+ $token = new OAuthToken('token', 'pfkkdhi9sl3r4s00');
+
+ $hmac = new OAuthSignatureMethod_HMAC_SHA1();
+ $plaintext = new OAuthSignatureMethod_PLAINTEXT();
+
+ // We need to test both what the parameter is, and how the serialized request is..
+
+ $r->sign_request($hmac, $cons, $token);
+ $this->assertEquals('HMAC-SHA1', $r->get_parameter('oauth_signature_method'));
+ $this->assertEquals('tR3+Ty81lMeYAr/Fid0kMTYa/WM=', $r->get_parameter('oauth_signature'));
+ $expectedPostdata = 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&'
+ . 'oauth_signature=tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D&oauth_signature_method=HMAC-SHA1&'
+ . 'oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original';
+ $this->assertEquals( $expectedPostdata, $r->to_postdata());
+
+ $r->sign_request($plaintext, $cons, $token);
+ $this->assertEquals('PLAINTEXT', $r->get_parameter('oauth_signature_method'));
+ $this->assertEquals('kd94hf93k423kf44&pfkkdhi9sl3r4s00', $r->get_parameter('oauth_signature'));
+ $expectedPostdata = 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&'
+ . 'oauth_signature=kd94hf93k423kf44%26pfkkdhi9sl3r4s00&oauth_signature_method=PLAINTEXT&'
+ . 'oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original';
+ $this->assertEquals( $expectedPostdata, $r->to_postdata());
+
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthServerTest.php b/OAuth/tests/phpunit/Lib/OAuthServerTest.php
new file mode 100644
index 00000000..db508934
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthServerTest.php
@@ -0,0 +1,259 @@
+<?php
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthException;
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+use MediaWiki\Extensions\OAuth\Lib\OAuthServer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_HMAC_SHA1;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_PLAINTEXT;
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+
+/**
+ * Tests of OAuthUtil
+ * @group OAuth
+ */
+class OAuthServerTest extends \PHPUnit\Framework\TestCase {
+
+ private $consumer;
+ private $request_token;
+ private $access_token;
+ private $hmac_sha1;
+ private $plaintext;
+ private $server;
+
+ protected function setUp() : void {
+ $this->consumer = new OAuthConsumer('key', 'secret');
+ $this->request_token = new OAuthToken('requestkey', 'requestsecret');
+ $this->access_token = new OAuthToken('accesskey', 'accesssecret');
+
+ $this->hmac_sha1 = new OAuthSignatureMethod_HMAC_SHA1();
+ $this->plaintext = new OAuthSignatureMethod_PLAINTEXT();
+
+ $this->server = new OAuthServer( new Mock_OAuthDataStore() );
+ $this->server->add_signature_method( $this->hmac_sha1 );
+ $this->server->add_signature_method( $this->plaintext );
+ }
+
+ public function testAcceptValidRequest() {
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+ [$consumer, $token] = $this->server->verify_request( $request );
+ $this->assertEquals( $this->consumer, $consumer );
+ $this->assertEquals( $this->access_token, $token );
+
+ $request->sign_request( $this->hmac_sha1, $this->consumer, $this->access_token );
+ [$consumer, $token] = $this->server->verify_request( $request );
+ $this->assertEquals( $this->consumer, $consumer );
+ $this->assertEquals( $this->access_token, $token );
+ }
+
+ public function testAcceptRequestWithoutVersion() {
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->unset_parameter('oauth_version');
+ $request->sign_request( $this->hmac_sha1, $this->consumer, $this->access_token );
+
+ [ $consumer, $token ] = $this->server->verify_request( $request );
+ $this->assertEquals( $this->consumer, $consumer );
+ $this->assertEquals( $this->access_token, $token );
+ }
+
+ public function testRejectRequestSignedWithRequestToken() {
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->request_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->request_token );
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request( $request );
+ }
+
+ public function requiredParameterProvider() {
+ // The list of required parameters is taken from
+ // Chapter 7 ("Accessing Protected Resources")
+ return array(
+ array( 'oauth_consumer_key' ),
+ array( 'oauth_token' ),
+ array( 'oauth_signature_method' ),
+ array( 'oauth_signature' ),
+ array( 'oauth_timestamp' ),
+ array( 'oauth_nonce' ),
+ );
+ }
+
+ /**
+ * @dataProvider requiredParameterProvider
+ */
+ public function testRejectRequestWithMissingParameters( $required ) {
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+ $request->unset_parameter( $required );
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request($request);
+ }
+
+ public function testRejectPastTimestamp() {
+ // We change the timestamp to be 10 hours ago, it should throw an exception
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->set_parameter( 'oauth_timestamp', $request->get_parameter('oauth_timestamp') - 10*60*60, false);
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request($request);
+ }
+
+ public function testRejectFutureTimestamp() {
+ // We change the timestamp to be 10 hours in the future, it should throw an exception
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->set_parameter( 'oauth_timestamp', $request->get_parameter('oauth_timestamp') + 10*60*60, false);
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request($request);
+ }
+
+ public function testRejectUsedNonce() {
+ // We give a known nonce and should see an exception
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ // The Mock datastore is set to say that the `nonce` nonce is known
+ $request->set_parameter( 'oauth_nonce', 'nonce', false);
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request($request);
+ }
+
+ public function testRejectInvalidSignature() {
+ // We change the signature post-signing to be something invalid
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+ $request->set_parameter( 'oauth_signature', '__whatever__', false);
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request($request);
+ }
+
+ public function testRejectInvalidConsumer() {
+ // We use the consumer-key "unknown", which isn't known by the datastore.
+
+ $unknown_consumer = new OAuthConsumer('unknown', '__unused__');
+
+ $request = OAuthRequest::from_consumer_and_token( $unknown_consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $unknown_consumer, $this->access_token );
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request( $request );
+ }
+
+ public function testRejectInvalidToken() {
+ // We use the access-token "unknown" which isn't known by the datastore
+
+ $unknown_token = new OAuthToken('unknown', '__unused__');
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $unknown_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $unknown_token );
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request( $request );
+ }
+
+ public function testRejectUnknownSignatureMethod() {
+ // We use a server that only supports HMAC-SHA1, but requests with PLAINTEXT signature
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+
+ $server = new OAuthServer( new Mock_OAuthDataStore() );
+ $server->add_signature_method( $this->hmac_sha1 );
+
+ $this->expectException(OAuthException::class);
+ $server->verify_request( $request );
+ }
+
+ public function testRejectUnknownVersion() {
+ // We use the version "1.0a" which isn't "1.0", so reject the request
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+ $request->set_parameter('oauth_version', '1.0a', false);
+
+ $this->expectException(OAuthException::class);
+ $this->server->verify_request( $request );
+ }
+
+ public function testCreateRequestToken() {
+ // We request a new Request Token
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, NULL, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, NULL );
+
+ $token = $this->server->fetch_request_token($request);
+ $this->assertEquals($this->request_token, $token);
+ }
+
+ public function testRejectSignedRequestTokenRequest() {
+ // We request a new Request Token, but the request is signed with a token which should fail
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->request_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->request_token );
+
+ $this->expectException(OAuthException::class);
+ $token = $this->server->fetch_request_token($request);
+ }
+
+ public function testCreateAccessToken() {
+ // We request a new Access Token
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->request_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->request_token );
+
+ $token = $this->server->fetch_access_token($request);
+ $this->assertEquals($this->access_token, $token);
+ }
+
+ public function testRejectUnsignedAccessTokenRequest() {
+ // We request a new Access Token, but we didn't sign the request with a Access Token
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, NULL, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, NULL );
+
+ $this->expectException(OAuthException::class);
+ $token = $this->server->fetch_access_token($request);
+ }
+
+ public function testRejectAccessTokenSignedAccessTokenRequest() {
+ // We request a new Access Token, but the request is signed with an access token, so fail!
+
+ $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com');
+ $request->sign_request( $this->plaintext, $this->consumer, $this->access_token );
+
+ $this->expectException(OAuthException::class);
+ $token = $this->server->fetch_access_token($request);
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthSignatureMethodHmacSha1Test.php b/OAuth/tests/phpunit/Lib/OAuthSignatureMethodHmacSha1Test.php
new file mode 100644
index 00000000..af01d1d4
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthSignatureMethodHmacSha1Test.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+use MediaWiki\Extensions\OAuth\Lib\OAuthSignatureMethod_HMAC_SHA1;
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @group OAuth
+ */
+class OAuthSignatureMethodHmacSha1Test extends \PHPUnit\Framework\TestCase {
+ private $method;
+
+ protected function setUp() : void {
+ $this->method = new OAuthSignatureMethod_HMAC_SHA1();
+ }
+
+ public function testIdentifyAsHmacSha1() {
+ $this->assertEquals('HMAC-SHA1', $this->method->get_name());
+ }
+
+ public function testBuildSignature() {
+ // Tests taken from http://wiki.oauth.net/TestCases section 9.2 ("HMAC-SHA1")
+ $request = new Mock_OAuthBaseStringRequest('bs');
+ $consumer = new OAuthConsumer('__unused__', 'cs');
+ $token = NULL;
+ $this->assertEquals('egQqG5AJep5sJ7anhXju1unge2I=', $this->method->build_signature( $request, $consumer, $token) );
+
+ $request = new Mock_OAuthBaseStringRequest('bs');
+ $consumer = new OAuthConsumer('__unused__', 'cs');
+ $token = new OAuthToken('__unused__', 'ts');
+ $this->assertEquals('VZVjXceV7JgPq/dOTnNmEfO0Fv8=', $this->method->build_signature( $request, $consumer, $token) );
+
+ $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26'
+ . 'oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26'
+ . 'oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal');
+ $consumer = new OAuthConsumer('__unused__', 'kd94hf93k423kf44');
+ $token = new OAuthToken('__unused__', 'pfkkdhi9sl3r4s00');
+ $this->assertEquals('tR3+Ty81lMeYAr/Fid0kMTYa/WM=', $this->method->build_signature( $request, $consumer, $token) );
+ }
+
+ public function testVerifySignature() {
+ // Tests taken from http://wiki.oauth.net/TestCases section 9.2 ("HMAC-SHA1")
+ $request = new Mock_OAuthBaseStringRequest('bs');
+ $consumer = new OAuthConsumer('__unused__', 'cs');
+ $token = NULL;
+ $signature = 'egQqG5AJep5sJ7anhXju1unge2I=';
+ $this->assertTrue( $this->method->check_signature( $request, $consumer, $token, $signature) );
+
+ $request = new Mock_OAuthBaseStringRequest('bs');
+ $consumer = new OAuthConsumer('__unused__', 'cs');
+ $token = new OAuthToken('__unused__', 'ts');
+ $signature = 'VZVjXceV7JgPq/dOTnNmEfO0Fv8=';
+ $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) );
+
+ $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26'
+ . 'oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26'
+ . 'oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal');
+ $consumer = new OAuthConsumer('__unused__', 'kd94hf93k423kf44');
+ $token = new OAuthToken('__unused__', 'pfkkdhi9sl3r4s00');
+ $signature = 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=';
+ $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) );
+
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthSignatureMethodRsaSha1Test.php b/OAuth/tests/phpunit/Lib/OAuthSignatureMethodRsaSha1Test.php
new file mode 100644
index 00000000..80beb699
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthSignatureMethodRsaSha1Test.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthConsumer;
+
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @group OAuth
+ */
+class OAuthSignatureMethodRsaSha1Test extends \PHPUnit\Framework\TestCase {
+ private $method;
+
+ protected function setUp() : void {
+ $this->method = new Mock_OAuthSignatureMethod_RSA_SHA1();
+ }
+
+ public function testIdentifyAsRsaSha1() {
+ $this->assertEquals('RSA-SHA1', $this->method->get_name());
+ }
+
+ public function testBuildSignature() {
+ if( ! function_exists('openssl_get_privatekey') ) {
+ $this->markTestSkipped('OpenSSL not available, can\'t test RSA-SHA1 functionality');
+ }
+
+ // Tests taken from http://wiki.oauth.net/TestCases section 9.3 ("RSA-SHA1")
+ $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal');
+ $consumer = new OAuthConsumer('dpf43f3p2l4k3l03', '__unused__');
+ $token = NULL;
+ $signature = 'jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=';
+ $this->assertEquals($signature, $this->method->build_signature( $request, $consumer, $token) );
+ }
+
+ public function testVerifySignature() {
+ if( ! function_exists('openssl_get_privatekey') ) {
+ $this->markTestSkipped('OpenSSL not available, can\'t test RSA-SHA1 functionality');
+ }
+
+ // Tests taken from http://wiki.oauth.net/TestCases section 9.3 ("RSA-SHA1")
+ $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal');
+ $consumer = new OAuthConsumer('dpf43f3p2l4k3l03', '__unused__');
+ $token = NULL;
+ $signature = 'jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=';
+ $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) );
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthTestUtils.php b/OAuth/tests/phpunit/Lib/OAuthTestUtils.php
new file mode 100644
index 00000000..5f362543
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthTestUtils.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthRequest;
+
+/**
+ * A simple utils class for methods needed
+ * during some of the tests
+ */
+class OAuthTestUtils {
+ private static function reset_request_vars() {
+ $_SERVER = array();
+ $_POST = array();
+ $_GET = array();
+ }
+
+ /**
+ * Populates $_{SERVER,GET,POST} and whatever environment-variables needed to test everything..
+ *
+ * @param string $method GET or POST
+ * @param string $uri What URI is the request to (eg http://example.com/foo?bar=baz)
+ * @param string $post_data What should the post-data be
+ * @param string $auth_header What to set the Authorization header to
+ */
+ public static function build_request( $method, $uri, $post_data = '', $auth_header = '' ) {
+ self::reset_request_vars();
+
+ $method = strtoupper($method);
+
+ $parts = parse_url($uri);
+
+ $scheme = $parts['scheme'];
+ $port = isset( $parts['port'] ) && $parts['port'] ? $parts['port'] : ( $scheme === 'https' ? '443' : '80' );
+ $host = $parts['host'];
+ $path = isset( $parts['path'] ) ? $parts['path'] : NULL;
+ $query = isset( $parts['query'] ) ? $parts['query'] : NULL;
+
+ if( $scheme == 'https') {
+ $_SERVER['HTTPS'] = 'on';
+ }
+
+ $_SERVER['REQUEST_METHOD'] = $method;
+ $_SERVER['HTTP_HOST'] = $host;
+ $_SERVER['SERVER_NAME'] = $host;
+ $_SERVER['SERVER_PORT'] = $port;
+ $_SERVER['SCRIPT_NAME'] = $path;
+ $_SERVER['REQUEST_URI'] = $path . '?' . $query;
+ $_SERVER['QUERY_STRING'] = $query.'';
+ parse_str($query, $_GET);
+
+ if( $method == 'POST' ) {
+ $_SERVER['HTTP_CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+ parse_str($post_data, $_POST);
+ OAuthRequest::$POST_INPUT = 'data:application/x-www-form-urlencoded,'.$post_data;
+ }
+
+ if( $auth_header != '' ) {
+ $_SERVER['HTTP_AUTHORIZATION'] = $auth_header;
+ }
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthTokenTest.php b/OAuth/tests/phpunit/Lib/OAuthTokenTest.php
new file mode 100644
index 00000000..46000f09
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthTokenTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthToken;
+
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @group OAuth
+ */
+class OAuthTokenTest extends \PHPUnit\Framework\TestCase {
+ public function testSerialize() {
+ $token = new OAuthToken('token', 'secret');
+ $this->assertEquals('oauth_token=token&oauth_token_secret=secret', $token->to_string());
+
+ $token = new OAuthToken('token&', 'secret%');
+ $this->assertEquals('oauth_token=token%26&oauth_token_secret=secret%25', $token->to_string());
+ }
+ public function testConvertToString() {
+ $token = new OAuthToken('token', 'secret');
+ $this->assertEquals('oauth_token=token&oauth_token_secret=secret', (string) $token);
+
+ $token = new OAuthToken('token&', 'secret%');
+ $this->assertEquals('oauth_token=token%26&oauth_token_secret=secret%25', (string) $token);
+ }
+}
diff --git a/OAuth/tests/phpunit/Lib/OAuthUtilTest.php b/OAuth/tests/phpunit/Lib/OAuthUtilTest.php
new file mode 100644
index 00000000..2a422269
--- /dev/null
+++ b/OAuth/tests/phpunit/Lib/OAuthUtilTest.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Lib;
+
+use MediaWiki\Extensions\OAuth\Lib\OAuthUtil;
+
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files ( the "Software" ), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+*/
+
+/**
+ * Tests of OAuthUtil
+ * @group OAuth
+ */
+class OAuthUtilTest extends \PHPUnit\Framework\TestCase {
+ public function testUrlencode() {
+ // Tests taken from
+ // http://wiki.oauth.net/TestCases ("Parameter Encoding")
+ $this->assertEquals('abcABC123', OAuthUtil::urlencode_rfc3986('abcABC123'));
+ $this->assertEquals('-._~', OAuthUtil::urlencode_rfc3986('-._~'));
+ $this->assertEquals('%25', OAuthUtil::urlencode_rfc3986('%'));
+ $this->assertEquals('%2B', OAuthUtil::urlencode_rfc3986('+'));
+ $this->assertEquals('%0A', OAuthUtil::urlencode_rfc3986("\n"));
+ $this->assertEquals('%20', OAuthUtil::urlencode_rfc3986(' '));
+ $this->assertEquals('%7F', OAuthUtil::urlencode_rfc3986("\x7F"));
+ //$this->assertEquals('%C2%80', OAuthUtil::urlencode_rfc3986("\x00\x80"));
+ //$this->assertEquals('%E3%80%81', OAuthUtil::urlencode_rfc3986("\x30\x01"));
+
+ // Last two checks disabled because of lack of UTF-8 support, or lack
+ // of knowledge from me (morten.fangel) on how to use it properly..
+
+ // A few tests to ensure code-coverage
+ $this->assertEquals( '', OAuthUtil::urlencode_rfc3986(NULL));
+ $this->assertEquals( '', OAuthUtil::urlencode_rfc3986(new \stdClass()));
+ }
+
+ public function testUrldecode() {
+ // Tests taken from
+ // http://wiki.oauth.net/TestCases ("Parameter Encoding")
+ $this->assertEquals('abcABC123', OAuthUtil::urldecode_rfc3986('abcABC123'));
+ $this->assertEquals('-._~', OAuthUtil::urldecode_rfc3986('-._~'));
+ $this->assertEquals('%', OAuthUtil::urldecode_rfc3986('%25'));
+ $this->assertEquals('+', OAuthUtil::urldecode_rfc3986('%2B'));
+ $this->assertEquals("\n", OAuthUtil::urldecode_rfc3986('%0A'));
+ $this->assertEquals(' ', OAuthUtil::urldecode_rfc3986('%20'));
+ $this->assertEquals("\x7F", OAuthUtil::urldecode_rfc3986('%7F'));
+ //$this->assertEquals("\x00\x80", OAuthUtil::urldecode_rfc3986('%C2%80'));
+ //$this->assertEquals("\x30\x01", OAuthUtil::urldecode_rfc3986('%E3%80%81'));
+
+ // Last two checks disabled because of lack of UTF-8 support, or lack
+ // of knowledge from me (morten.fangel) on how to use it properly..
+ }
+
+ public function testParseParameter() {
+ // Tests taken from
+ // http://wiki.oauth.net/TestCases ("Normalize Request Parameters")
+
+ $this->assertEquals(
+ array('name'=>''),
+ OAuthUtil::parse_parameters('name')
+ );
+ $this->assertEquals(
+ array('a'=>'b'),
+ OAuthUtil::parse_parameters('a=b')
+ );
+ $this->assertEquals(
+ array('a'=>'b','c'=>'d'),
+ OAuthUtil::parse_parameters('a=b&c=d')
+ );
+ $this->assertEquals(
+ array('a'=>array('x!y','x y')),
+ OAuthUtil::parse_parameters('a=x!y&a=x+y')
+ );
+ $this->assertEquals(
+ array('x!y'=>'a', 'x' =>'a'),
+ OAuthUtil::parse_parameters('x!y=a&x=a')
+ );
+ }
+
+ public function testBuildHttpQuery() {
+ // Tests taken from
+ // http://wiki.oauth.net/TestCases ("Normalize Request Parameters")
+ $this->assertEquals(
+ 'name=',
+ OAuthUtil::build_http_query(array('name'=>''))
+ );
+ $this->assertEquals(
+ 'a=b',
+ OAuthUtil::build_http_query(array('a'=>'b'))
+ );
+ $this->assertEquals(
+ 'a=b&c=d',
+ OAuthUtil::build_http_query(array('a'=>'b','c'=>'d'))
+ );
+ $this->assertEquals(
+ 'a=x%20y&a=x%21y',
+ OAuthUtil::build_http_query(array('a'=>array('x!y','x y')))
+ );
+ $this->assertEquals(
+ 'x=a&x%21y=a',
+ OAuthUtil::build_http_query(array('x!y'=>'a', 'x' =>'a'))
+ );
+
+ // Test taken from the Spec 9.1.1
+ $this->assertEquals(
+ 'a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t',
+ OAuthUtil::build_http_query(array('a'=>'1', 'c' =>'hi there', 'f'=>array(25, 50, 'a'), 'z'=>array('p','t')))
+ );
+
+ // From issue 164, by hidetaka
+ // Based on discussion at
+ // http://groups.google.com/group/oauth/browse_thread/thread/7c698004be0d536/dced7b6c82b917b2?lnk=gst&q=sort#
+ $this->assertEquals(
+ 'x=200&x=25&y=B&y=a',
+ OAuthUtil::build_http_query(array('x'=>array(25, 200), 'y'=>array('a', 'B')))
+ );
+ }
+
+ public function testSplitHeader() {
+ $this->assertEquals(
+ array('oauth_foo'=>'bar','oauth_baz'=>'bla,rgh'),
+ OAuthUtil::split_header('OAuth realm="",oauth_foo=bar,oauth_baz="bla,rgh"')
+ );
+ $this->assertEquals(
+ array(),
+ OAuthUtil::split_header('OAuth realm="",foo=bar,baz="bla,rgh"')
+ );
+ $this->assertEquals(
+ array('foo'=>'bar', 'baz'=>'bla,rgh'),
+ OAuthUtil::split_header('OAuth realm="",foo=bar,baz="bla,rgh"', false)
+ );
+ $this->assertEquals(
+ array('oauth_foo' => 'hi there'),
+ OAuthUtil::split_header('OAuth realm="",oauth_foo=hi+there,foo=bar,baz="bla,rgh"')
+ );
+
+ }
+
+ public function testGetHeaders() {
+ if (function_exists('apache_request_headers')) {
+ $this->markTestSkipped('We assume the apache module is well tested. Since this module is present, no need testing our suplement');
+ }
+
+ $_SERVER['HTTP_HOST'] = 'foo';
+ $_SERVER['HTTP_X_WHATEVER'] = 'bar';
+ $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar'), OAuthUtil::get_headers() );
+
+ // Test picking up the Content-Type of POST requests running as an Apache module but not having the ARH method
+ $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+ $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar', 'Content-Type'=>'application/x-www-form-urlencoded'), OAuthUtil::get_headers() );
+
+ // Test picking up the Content-Type of POST requests when using CGI
+ unset($_SERVER['CONTENT_TYPE']);
+ $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar'), OAuthUtil::get_headers() );
+ $_ENV['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+ $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar', 'Content-Type'=>'application/x-www-form-urlencoded'), OAuthUtil::get_headers() );
+ }
+}
diff --git a/OAuth/tests/phpunit/Repository/AccessTokenRepositoryTest.php b/OAuth/tests/phpunit/Repository/AccessTokenRepositoryTest.php
new file mode 100644
index 00000000..084c30b3
--- /dev/null
+++ b/OAuth/tests/phpunit/Repository/AccessTokenRepositoryTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Repository;
+
+use MediaWiki\Extensions\OAuth\Entity\AccessTokenEntity;
+use MediaWiki\Extensions\OAuth\Repository\AccessTokenRepository;
+use MediaWiki\Extensions\OAuth\Tests\Entity\Mock_ClientEntity;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Repository\AccessTokenRepository
+ * @group Database
+ */
+class AccessTokenRepositoryTest extends MediaWikiTestCase {
+ protected $accessToken;
+ protected $accessTokenRepo;
+
+ protected $tablesUsed = [ 'oauth2_access_tokens' ];
+
+ protected function setUp() : void {
+ parent::setUp();
+
+ $this->accessToken = new AccessTokenEntity(
+ Mock_ClientEntity::newMock( $this->getTestUser()->getUser() ), []
+ );
+ $identifier = bin2hex( random_bytes( 40 ) );
+ $this->accessToken->setIdentifier( $identifier );
+ $this->accessToken->setExpiryDateTime(
+ ( new \DateTimeImmutable() )->add( new \DateInterval( 'PT1H' ) )
+ );
+
+ $this->accessTokenRepo = new AccessTokenRepository();
+ }
+
+ public function testPersistingToken() {
+ $this->accessTokenRepo->persistNewAccessToken( $this->accessToken );
+
+ $this->assertFalse(
+ $this->accessTokenRepo->isAccessTokenRevoked( $this->accessToken->getIdentifier() ),
+ 'Access token should not be revoked'
+ );
+ }
+
+ public function testRevokingToken() {
+ $this->accessTokenRepo->revokeAccessToken( $this->accessToken->getIdentifier() );
+
+ $this->assertTrue(
+ $this->accessTokenRepo->isAccessTokenRevoked( $this->accessToken->getIdentifier() ),
+ 'Access token should be revoked'
+ );
+ }
+}
diff --git a/OAuth/tests/phpunit/Repository/AuthCodeRepositoryTest.php b/OAuth/tests/phpunit/Repository/AuthCodeRepositoryTest.php
new file mode 100644
index 00000000..2d2dc431
--- /dev/null
+++ b/OAuth/tests/phpunit/Repository/AuthCodeRepositoryTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Repository;
+
+use MediaWiki\Extensions\OAuth\Repository\AuthCodeRepository;
+use MediaWiki\Extensions\OAuth\Tests\Entity\Mock_ClientEntity;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Repository\AuthCodeRepository
+ */
+class AuthCodeRepositoryTest extends MediaWikiTestCase {
+ protected $authCodeToken;
+ protected $authCodeTokenRepo;
+
+ protected function setUp() : void {
+ parent::setUp();
+
+ $this->authCodeTokenRepo = AuthCodeRepository::factory();
+ $this->authCodeToken = $this->authCodeTokenRepo->getNewAuthCode();
+ $this->authCodeToken->setIdentifier( bin2hex( random_bytes( 20 ) ) );
+ $this->authCodeToken->setClient(
+ Mock_ClientEntity::newMock( $this->getTestUser()->getUser() )
+ );
+ $this->authCodeToken->setExpiryDateTime(
+ ( new \DateTimeImmutable() )->add( new \DateInterval( 'PT1H' ) )
+ );
+ }
+
+ public function testPersistingToken() {
+ $this->authCodeTokenRepo->persistNewAuthCode( $this->authCodeToken );
+
+ $this->assertFalse(
+ $this->authCodeTokenRepo->isAuthCodeRevoked( $this->authCodeToken->getIdentifier() ),
+ 'AuthCode token must be persisted'
+ );
+ }
+
+ public function testRevokingToken() {
+ $this->authCodeTokenRepo->revokeAuthCode( $this->authCodeToken->getIdentifier() );
+
+ $this->assertTrue(
+ $this->authCodeTokenRepo->isAuthCodeRevoked( $this->authCodeToken->getIdentifier() ),
+ 'AuthCode token should be revoked'
+ );
+ }
+}
diff --git a/OAuth/tests/phpunit/Repository/ScopeRepositoryTest.php b/OAuth/tests/phpunit/Repository/ScopeRepositoryTest.php
new file mode 100644
index 00000000..41196aef
--- /dev/null
+++ b/OAuth/tests/phpunit/Repository/ScopeRepositoryTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Repository;
+
+use MediaWiki\Extensions\OAuth\Entity\ScopeEntity;
+use MediaWiki\Extensions\OAuth\Repository\ScopeRepository;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Repository\ScopeRepository
+ */
+class ScopeRepositoryTest extends MediaWikiTestCase {
+ public function testScopes() {
+ $repo = new ScopeRepository();
+
+ $this->assertInstanceOf(
+ ScopeEntity::class, $repo->getScopeEntityByIdentifier( 'editpage' ),
+ 'Scope \"editpage\" should be a valid scope'
+ );
+ $this->assertInstanceOf(
+ ScopeEntity::class, $repo->getScopeEntityByIdentifier( 'mwoauth-authonlyprivate' ),
+ 'Scope \"mwoauth-authonlyprivate\" should be a valid scope'
+ );
+
+ $this->assertNotInstanceOf(
+ ScopeEntity::class, $repo->getScopeEntityByIdentifier( 'dummynonexistent' ),
+ 'Scope \"dummynonexistent\" should not be a valid scope'
+ );
+ }
+}
diff --git a/OAuth/tests/phpunit/Rest/AccessTokenEndpointTest.php b/OAuth/tests/phpunit/Rest/AccessTokenEndpointTest.php
new file mode 100644
index 00000000..c3b7f8af
--- /dev/null
+++ b/OAuth/tests/phpunit/Rest/AccessTokenEndpointTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Rest;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Rest\Handler\AccessToken
+ */
+class AccessTokenEndpointTest extends EndpointTest {
+ public static function provideTestViaRouter() {
+ return [
+ 'normal' => [
+ [
+ 'method' => 'POST',
+ 'uri' => self::makeUri( '/oauth2/access_token' ),
+ 'headers' => [
+ 'Content-Type' => 'application/json'
+ ],
+ 'postParams' => [
+ 'grant_type' => 'authorization_code',
+ 'client_id' => 'dummy'
+ ]
+ ],
+ [
+ 'statusCode' => 401,
+ 'reasonPhrase' => 'Unauthorized',
+ 'protocolVersion' => '1.1'
+ ]
+ ],
+ 'method not allowed' => [
+ [
+ 'method' => 'GET',
+ 'uri' => self::makeUri( '/oauth2/access_token' ),
+ ],
+ [
+ 'statusCode' => 405,
+ 'reasonPhrase' => 'Method Not Allowed',
+ 'protocolVersion' => '1.1',
+ 'body' => '{"httpCode":405,"httpReason":"Method Not Allowed"}',
+ ]
+ ],
+ 'invalid grant type' => [
+ [
+ 'method' => 'POST',
+ 'uri' => self::makeUri( '/oauth2/access_token' ),
+ 'postParams' => [
+ 'grant_type' => 'dummy',
+ 'client_id' => 'dummy'
+ ]
+ ],
+ [
+ 'statusCode' => 400,
+ 'reasonPhrase' => 'Bad Request',
+ 'protocolVersion' => '1.1'
+ ]
+ ],
+ 'grant type missing' => [
+ [
+ 'method' => 'POST',
+ 'uri' => self::makeUri( '/oauth2/access_token' ),
+ 'postParams' => [
+ 'client_id' => 'dummy'
+ ]
+ ],
+ [
+ 'statusCode' => 400,
+ 'reasonPhrase' => 'Bad Request',
+ 'protocolVersion' => '1.1'
+ ]
+ ],
+ ];
+ }
+}
diff --git a/OAuth/tests/phpunit/Rest/AuthorizationEndpointTest.php b/OAuth/tests/phpunit/Rest/AuthorizationEndpointTest.php
new file mode 100644
index 00000000..599c17c6
--- /dev/null
+++ b/OAuth/tests/phpunit/Rest/AuthorizationEndpointTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Rest;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\Rest\Handler\Authorize
+ */
+class AuthorizationEndpointTest extends EndpointTest {
+ /**
+ * @return array
+ */
+ public static function provideTestViaRouter() {
+ return [
+ 'redirect to login' => [
+ [
+ 'method' => 'GET',
+ 'uri' => self::makeUri( '/oauth2/authorize' ),
+ 'queryParams' => [
+ 'client_id' => 'dummy',
+ 'response_type' => 'code'
+ ]
+ ],
+ [
+ 'statusCode' => 307,
+ 'reasonPhrase' => 'Temporary Redirect',
+ 'protocolVersion' => '1.1'
+ ]
+ ],
+ 'method not allowed' => [
+ [
+ 'method' => 'POST',
+ 'uri' => self::makeUri( '/oauth2/authorize' ),
+ ],
+ [
+ 'statusCode' => 405,
+ 'reasonPhrase' => 'Method Not Allowed',
+ 'protocolVersion' => '1.1',
+ 'body' => '{"httpCode":405,"httpReason":"Method Not Allowed"}',
+ ]
+ ],
+ ];
+ }
+}
diff --git a/OAuth/tests/phpunit/Rest/EndpointTest.php b/OAuth/tests/phpunit/Rest/EndpointTest.php
new file mode 100644
index 00000000..94638a58
--- /dev/null
+++ b/OAuth/tests/phpunit/Rest/EndpointTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace MediaWiki\Extensions\OAuth\Tests\Rest;
+
+use EmptyBagOStuff;
+use GuzzleHttp\Psr7\Uri;
+use MediaWiki\Permissions\PermissionManager;
+use MediaWiki\Rest\BasicAccess\StaticBasicAuthorizer;
+use MediaWiki\Rest\RequestData;
+use MediaWiki\Rest\ResponseFactory;
+use MediaWiki\Rest\Router;
+use MediaWiki\Rest\Validator\Validator;
+use Psr\Container\ContainerInterface;
+use RequestContext;
+use Title;
+use User;
+use Wikimedia\ObjectFactory;
+
+abstract class EndpointTest extends \MediaWikiTestCase {
+
+ protected function setUp() : void {
+ parent::setUp();
+
+ $this->setMwGlobals( [
+ 'wgOAuthSecretKey' => base64_encode( random_bytes( 32 ) )
+ ] );
+
+ RequestContext::getMain()->setTitle( Title::newMainPage() );
+ }
+
+ abstract public static function provideTestViaRouter();
+
+ protected static function makeUri( $path ) {
+ return new Uri( "http://www.example.com/rest$path" );
+ }
+
+ /** @dataProvider provideTestViaRouter */
+ public function testViaRouter( $requestInfo, $responseInfo ) {
+ $objectFactory = new ObjectFactory(
+ $this->getMockForAbstractClass( ContainerInterface::class )
+ );
+ $permissionManager = $this->createMock( PermissionManager::class );
+ $request = new RequestData( $requestInfo );
+ $router = new Router(
+ [ __DIR__ . '/testRoutes.json' ],
+ [],
+ 'http://wiki.example.com',
+ '/rest',
+ new EmptyBagOStuff(),
+ new ResponseFactory( [] ),
+ new StaticBasicAuthorizer(),
+ $objectFactory,
+ new Validator( $objectFactory, $permissionManager, $request, new User ),
+ $this->createHookContainer()
+ );
+ $response = $router->execute( $request );
+
+ if ( isset( $responseInfo['statusCode'] ) ) {
+ $this->assertSame( $responseInfo['statusCode'], $response->getStatusCode() );
+ }
+ if ( isset( $responseInfo['reasonPhrase'] ) ) {
+ $this->assertSame( $responseInfo['reasonPhrase'], $response->getReasonPhrase() );
+ }
+ if ( isset( $responseInfo['protocolVersion'] ) ) {
+ $this->assertSame( $responseInfo['protocolVersion'], $response->getProtocolVersion() );
+ }
+ if ( isset( $responseInfo['body'] ) ) {
+ $this->assertSame( $responseInfo['body'], $response->getBody()->getContents() );
+ }
+ $this->assertSame(
+ [],
+ array_diff( array_keys( $responseInfo ), [
+ 'statusCode',
+ 'reasonPhrase',
+ 'protocolVersion',
+ 'body'
+ ] ),
+ '$responseInfo may not contain unknown keys' );
+ }
+}
diff --git a/OAuth/tests/phpunit/Rest/testRoutes.json b/OAuth/tests/phpunit/Rest/testRoutes.json
new file mode 100644
index 00000000..031f0cc4
--- /dev/null
+++ b/OAuth/tests/phpunit/Rest/testRoutes.json
@@ -0,0 +1,15 @@
+[
+ {
+ "path": "/oauth2/authorize",
+ "factory": "MediaWiki\\Extensions\\OAuth\\Rest\\Handler\\Authorize::factory"
+ },
+ {
+ "path": "/oauth2/access_token",
+ "factory": "MediaWiki\\Extensions\\OAuth\\Rest\\Handler\\AccessToken::factory",
+ "method": "POST"
+ },
+ {
+ "path": "/oauth2/resource/{{type}}",
+ "factory": "MediaWiki\\Extensions\\OAuth\\Rest\\Handler\\Resource::factory"
+ }
+]
diff --git a/OAuth/tests/phpunit/SessionProviderTest.php b/OAuth/tests/phpunit/SessionProviderTest.php
new file mode 100644
index 00000000..60df85f3
--- /dev/null
+++ b/OAuth/tests/phpunit/SessionProviderTest.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * @section LICENSE
+ * © 2017 Wikimedia Foundation and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Extensions\OAuth\Tests;
+
+use MediaWiki\Extensions\OAuth\SessionProvider;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Extensions\OAuth\SessionProvider
+ * @group OAuth
+ * @license GPL-2.0-or-later
+ */
+class SessionProviderTest extends MediaWikiTestCase {
+
+ protected function setUp() : void {
+ parent::setUp();
+ // the SessionProvider constructor modifies $wgHooks, stash it
+ global $wgHooks;
+ $this->setMwGlobals( 'wgHooks', $wgHooks );
+ }
+
+ public function testSafeAgainstCsrf() {
+ $provider = $this->getMockBuilder( SessionProvider::class )
+ ->setMethodsExcept( [ 'safeAgainstCsrf' ] )
+ ->getMock();
+ $this->assertTrue( $provider->safeAgainstCsrf() );
+ }
+
+ /**
+ * @dataProvider provideOnMarkPatrolledArguments
+ */
+ public function testOnMarkPatrolled( $consumerId, $auto, $expectedExtraTag ) {
+ $provider = $this->getMockBuilder( SessionProvider::class )
+ ->setMethods( [ 'getPublicConsumerId' ] )
+ ->getMock();
+ $provider->expects( $this->once() )
+ ->method( 'getPublicConsumerId' )
+ ->willReturn( $consumerId );
+
+ $originalTags = [ 'Unrelated tag' ];
+ $tags = $originalTags;
+
+ $provider->onMarkPatrolled( 1, $this->getTestUser()->getUser(), false, $auto, $tags );
+
+ if ( $expectedExtraTag === null ) {
+ $this->assertSame( $originalTags, $tags );
+ } else {
+ $expectedTags = $originalTags;
+ $expectedTags[] = $expectedExtraTag;
+ $this->assertSame( $expectedTags, $tags );
+ }
+ }
+
+ public function provideOnMarkPatrolledArguments() {
+ yield 'no consumer, manually patrolled' => [
+ null,
+ false,
+ null,
+ ];
+
+ yield 'no consumer, automatically patrolled' => [
+ null,
+ true,
+ null,
+ ];
+
+ yield 'consumer 123, manually patrolled' => [
+ 123,
+ false,
+ 'OAuth CID: 123',
+ ];
+
+ yield 'consumer 1234, automatically patrolled' => [
+ 1234,
+ true,
+ 'OAuth CID: 1234',
+ ];
+ }
+
+}