r101042 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r101041‎ | r101042 | r101043 >
Date:20:44, 27 October 2011
Author:awjrichards
Status:deferred
Tags:
Comment:
Copy from branches/1.18wmf1 @ r97502
Modified paths:
  • /branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking (added) (history)

Diff [purge]

Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.alias.php
@@ -0,0 +1,169 @@
 2+<?php
 3+/**
 4+ * Aliases for ContributionTracking extension special pages
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$specialPageAliases = array();
 11+
 12+/** English (English) */
 13+$specialPageAliases['en'] = array(
 14+ 'ContributionTracking' => array( 'ContributionTracking' ),
 15+);
 16+
 17+/** Arabic (العربية) */
 18+$specialPageAliases['ar'] = array(
 19+ 'ContributionTracking' => array( 'تتبع_المساهمة' ),
 20+);
 21+
 22+/** Egyptian Spoken Arabic (مصرى) */
 23+$specialPageAliases['arz'] = array(
 24+ 'ContributionTracking' => array( 'متابعة_المساهمه' ),
 25+);
 26+
 27+/** Bosnian (Bosanski) */
 28+$specialPageAliases['bs'] = array(
 29+ 'ContributionTracking' => array( 'DoprinosiPracenje' ),
 30+);
 31+
 32+/** Lower Sorbian (Dolnoserbski) */
 33+$specialPageAliases['dsb'] = array(
 34+ 'ContributionTracking' => array( 'Slědowanje_pśinoskow' ),
 35+);
 36+
 37+/** Esperanto (Esperanto) */
 38+$specialPageAliases['eo'] = array(
 39+ 'ContributionTracking' => array( 'Kontribukontado' ),
 40+);
 41+
 42+/** Persian (فارسی) */
 43+$specialPageAliases['fa'] = array(
 44+ 'ContributionTracking' => array( 'ردگیری_مشارکت' ),
 45+);
 46+
 47+/** Finnish (Suomi) */
 48+$specialPageAliases['fi'] = array(
 49+ 'ContributionTracking' => array( 'Muokkausten_seuranta' ),
 50+);
 51+
 52+/** Galician (Galego) */
 53+$specialPageAliases['gl'] = array(
 54+ 'ContributionTracking' => array( 'Seguimento_das_contribucións' ),
 55+);
 56+
 57+/** Haitian (Kreyòl ayisyen) */
 58+$specialPageAliases['ht'] = array(
 59+ 'ContributionTracking' => array( 'SwiviKontribisyon' ),
 60+);
 61+
 62+/** Hungarian (Magyar) */
 63+$specialPageAliases['hu'] = array(
 64+ 'ContributionTracking' => array( 'Adományok_követése' ),
 65+);
 66+
 67+/** Interlingua (Interlingua) */
 68+$specialPageAliases['ia'] = array(
 69+ 'ContributionTracking' => array( 'Traciamento_de_contributiones' ),
 70+);
 71+
 72+/** Indonesian (Bahasa Indonesia) */
 73+$specialPageAliases['id'] = array(
 74+ 'ContributionTracking' => array( 'Pelacakan_kontribusi', 'PelacakanKontribusi' ),
 75+);
 76+
 77+/** Italian (Italiano) */
 78+$specialPageAliases['it'] = array(
 79+ 'ContributionTracking' => array( 'TracciaEdit' ),
 80+);
 81+
 82+/** Japanese (日本語) */
 83+$specialPageAliases['ja'] = array(
 84+ 'ContributionTracking' => array( '寄付追跡' ),
 85+);
 86+
 87+/** Korean (한국어) */
 88+$specialPageAliases['ko'] = array(
 89+ 'ContributionTracking' => array( '기여추적' ),
 90+);
 91+
 92+/** Colognian (Ripoarisch) */
 93+$specialPageAliases['ksh'] = array(
 94+ 'ContributionTracking' => array( 'MetmaacherVerfoljung' ),
 95+);
 96+
 97+/** Ladino (Ladino) */
 98+$specialPageAliases['lad'] = array(
 99+ 'ContributionTracking' => array( 'PerseguirAjustamientos' ),
 100+);
 101+
 102+/** Macedonian (Македонски) */
 103+$specialPageAliases['mk'] = array(
 104+ 'ContributionTracking' => array( 'СледењеНаПридонесите' ),
 105+);
 106+
 107+/** Malayalam (മലയാളം) */
 108+$specialPageAliases['ml'] = array(
 109+ 'ContributionTracking' => array( 'സേവനംശ്രദ്ധിക്കൽ' ),
 110+);
 111+
 112+/** Marathi (मराठी) */
 113+$specialPageAliases['mr'] = array(
 114+ 'ContributionTracking' => array( 'योगदानमागमूस' ),
 115+);
 116+
 117+/** Nedersaksisch (Nedersaksisch) */
 118+$specialPageAliases['nds-nl'] = array(
 119+ 'ContributionTracking' => array( 'Donasies_volgen' ),
 120+);
 121+
 122+/** Dutch (Nederlands) */
 123+$specialPageAliases['nl'] = array(
 124+ 'ContributionTracking' => array( 'DonatiesVolgen' ),
 125+);
 126+
 127+/** Norwegian (bokmål)‬ (‪Norsk (bokmål)‬) */
 128+$specialPageAliases['no'] = array(
 129+ 'ContributionTracking' => array( 'Bidragssporing' ),
 130+);
 131+
 132+/** Polish (Polski) */
 133+$specialPageAliases['pl'] = array(
 134+ 'ContributionTracking' => array( 'Przekierowanie_do_systemu_płatnościowego' ),
 135+);
 136+
 137+/** Sanskrit (संस्कृत) */
 138+$specialPageAliases['sa'] = array(
 139+ 'ContributionTracking' => array( 'योगदानउन्नयति' ),
 140+);
 141+
 142+/** Slovak (Slovenčina) */
 143+$specialPageAliases['sk'] = array(
 144+ 'ContributionTracking' => array( 'SledovaniePríspevkov' ),
 145+);
 146+
 147+/** Tagalog (Tagalog) */
 148+$specialPageAliases['tl'] = array(
 149+ 'ContributionTracking' => array( 'Pagbakas ng ambag' ),
 150+);
 151+
 152+/** Turkish (Türkçe) */
 153+$specialPageAliases['tr'] = array(
 154+ 'ContributionTracking' => array( 'KatkıTakibi' ),
 155+);
 156+
 157+/** Vèneto (Vèneto) */
 158+$specialPageAliases['vec'] = array(
 159+ 'ContributionTracking' => array( 'TraciamentoContributi' ),
 160+);
 161+
 162+/** Simplified Chinese (‪中文(简体)‬) */
 163+$specialPageAliases['zh-hans'] = array(
 164+ 'ContributionTracking' => array( '跟踪贡献' ),
 165+);
 166+
 167+/**
 168+ * For backwards compatibility with MediaWiki 1.15 and earlier.
 169+ */
 170+$aliases =& $specialPageAliases;
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.alias.php
___________________________________________________________________
Added: svn:keywords
1171 + Id
Added: svn:eol-style
2172 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.sql
@@ -0,0 +1,18 @@
 2+CREATE TABLE IF NOT EXISTS /*_*/contribution_tracking (
 3+ id int(10) unsigned NOT NULL PRIMARY KEY auto_increment,
 4+ contribution_id int(10) unsigned default NULL,
 5+ note text,
 6+ referrer varchar(4096) default NULL,
 7+ anonymous tinyint(1) unsigned NOT NULL,
 8+ utm_source varchar(128) default NULL,
 9+ utm_medium varchar(128) default NULL,
 10+ utm_campaign varchar(128) default NULL,
 11+ optout tinyint(1) unsigned NOT NULL,
 12+ language varchar(8) default NULL,
 13+ ts char(14) default NULL,
 14+ owa_session varbinary(255) default NULL,
 15+ owa_ref int(11) default NULL
 16+) /*wgDBTableOptions*/;
 17+
 18+CREATE UNIQUE INDEX /*i*/contribution_id ON /*_*/contribution_tracking (contribution_id);
 19+CREATE INDEX /*i*/ts ON /*_*/contribution_tracking (ts);
\ No newline at end of file
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.sql
___________________________________________________________________
Added: svn:eol-style
120 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/patch-owa.sql
@@ -0,0 +1,5 @@
 2+--
 3+-- create contribution_tracking.owa_session and owa_ref
 4+--
 5+ALTER TABLE /*_*/contribution_tracking ADD owa_session varbinary(255) default NULL;
 6+ALTER TABLE /*_*/contribution_tracking ADD owa_ref int(11) default NULL;
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/patch-owa.sql
___________________________________________________________________
Added: svn:eol-style
17 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ApiContributionTracking.php
@@ -0,0 +1,197 @@
 2+<?php
 3+
 4+/**
 5+ * This API will allow for the elimination of the interstitial page defined in
 6+ * ContributionTracking_body.php. Instead of posting contribution data to that
 7+ * page, a request to ApiContributionTracking will save contribution tracking
 8+ * data locally and prepare a set of data to be immediately reposted to the
 9+ * gateway by the original calling page. The ajax side of this is handled by
 10+ * jquery.contributionTracking.js.
 11+ * For a working example of the whole process, see
 12+ * ContributionTracking_Tester.php (must be sysop for permission).
 13+ * @author Katie Horn <khorn@wikimedia.org>
 14+ */
 15+class ApiContributionTracking extends ApiBase {
 16+
 17+ public function execute( $params = null ) {
 18+ if ( $params === null ) {
 19+ $params = $this->extractRequestParams();
 20+ }
 21+ $params = $this->getStagedParams( $params );
 22+ $contribution_tracking_id = ContributionTrackingProcessor::saveNewContribution( $params );
 23+ $this->doReturn( $contribution_tracking_id, $params );
 24+ }
 25+
 26+ /**
 27+ * Stages incoming request parameters for the ContributionTrackingProcessor
 28+ * @param array $params Incoming request parameters
 29+ * @return array Paramaters ready to be sent off to the processor.
 30+ */
 31+ function getStagedParams( $params = null ) {
 32+
 33+ foreach ( $params as $key => $value ) {
 34+ if ( $value == '' ) {
 35+ if ( $key === 'comment-option' || $key === 'email-opt' ) {
 36+ $params[$key] = false;
 37+ } else {
 38+ unset( $params[$key] ); //gotcha. And might I add: BOO-URNS.
 39+ }
 40+ }
 41+ }
 42+ return $params;
 43+ }
 44+
 45+ /**
 46+ * Assembles the data for the API to return.
 47+ * @param integer $id The Contribution Tracking ID.
 48+ * @param array $params Original (staged) request paramaters.
 49+ */
 50+ function doReturn( $id, $params ) {
 51+// foreach ($params as $key=>$value){
 52+// if ($value != ''){
 53+// $this->getResult()->addValue(array('returns', 'parrot'), $key, $value);
 54+// }
 55+// }
 56+ $params['contribution_tracking_id'] = $id;
 57+
 58+ $repost = ContributionTrackingProcessor::getRepostFields( $params );
 59+
 60+ $this->getResult()->addValue( array( 'returns', 'action' ), 'url', $repost['action'] );
 61+ foreach ( $repost['fields'] as $key => $value ) {
 62+ $this->getResult()->addValue( array( 'returns', 'fields' ), $key, $value );
 63+ }
 64+ }
 65+
 66+ /**
 67+ *
 68+ * @return array An array of parameters allowed by ApiContributionTracking
 69+ */
 70+ public function getAllowedParams() {
 71+ return array(
 72+ 'amount' => array(
 73+ ApiBase::PARAM_TYPE => 'string',
 74+ ApiBase::PARAM_REQUIRED => true,
 75+ ),
 76+ 'referrer' => array(
 77+ ApiBase::PARAM_TYPE => 'string',
 78+ ApiBase::PARAM_REQUIRED => true,
 79+ ),
 80+ 'gateway' => array(
 81+ ApiBase::PARAM_TYPE => 'string',
 82+ ApiBase::PARAM_REQUIRED => true,
 83+ ),
 84+ 'comment' => array(
 85+ ApiBase::PARAM_TYPE => 'string',
 86+ ),
 87+ 'comment-option' => array(
 88+ ApiBase::PARAM_TYPE => 'boolean',
 89+ ),
 90+ 'utm_source' => array(
 91+ ApiBase::PARAM_TYPE => 'string',
 92+ ),
 93+ 'utm_medium' => array(
 94+ ApiBase::PARAM_TYPE => 'string',
 95+ ),
 96+ 'utm_campaign' => array(
 97+ ApiBase::PARAM_TYPE => 'string',
 98+ ),
 99+ 'email-opt' => array(
 100+ ApiBase::PARAM_TYPE => 'boolean',
 101+ ),
 102+ 'language' => array(
 103+ ApiBase::PARAM_TYPE => 'string',
 104+ ),
 105+ 'owa_session' => array(
 106+ ApiBase::PARAM_TYPE => 'string',
 107+ ),
 108+ 'owa_ref' => array(
 109+ ApiBase::PARAM_TYPE => 'string',
 110+ ),
 111+ 'contribution_tracking_id' => array(
 112+ ApiBase::PARAM_TYPE => 'string',
 113+ ),
 114+ 'returnto' => array(
 115+ ApiBase::PARAM_TYPE => 'string',
 116+ ),
 117+ 'tshirt' => array(
 118+ ApiBase::PARAM_TYPE => 'boolean',
 119+ ),
 120+ 'size' => array(
 121+ ApiBase::PARAM_TYPE => 'string',
 122+ ),
 123+ 'premium_language' => array(
 124+ ApiBase::PARAM_TYPE => 'string',
 125+ ),
 126+ 'currency_code' => array(
 127+ ApiBase::PARAM_TYPE => 'string',
 128+ ),
 129+ 'fname' => array(
 130+ ApiBase::PARAM_TYPE => 'string',
 131+ ),
 132+ 'lname' => array(
 133+ ApiBase::PARAM_TYPE => 'string',
 134+ ),
 135+ 'email' => array(
 136+ ApiBase::PARAM_TYPE => 'string',
 137+ ),
 138+ 'recurring_paypal' => array(
 139+ ApiBase::PARAM_TYPE => 'boolean',
 140+ ),
 141+ 'amountGiven' => array(
 142+ ApiBase::PARAM_TYPE => 'string',
 143+ ),
 144+ );
 145+ }
 146+
 147+ public function getParamDescription() {
 148+ return array(
 149+ 'amount' => 'Transaction amount (required)',
 150+ 'referrer' => 'String identifying the referring entity (required)',
 151+ 'gateway' => array(
 152+ 'String identifying the specific entity used to process this payment. ',
 153+ 'Probably "paypal". (required)' ),
 154+ 'comment' => 'String with a comment. Actually saved as "note" in the database',
 155+ 'comment-option' => 'Boolean assumed to be from a checkbox. This is actually the inverse of the anonymous flag.',
 156+ 'utm_source' => 'String identifying "utm_source"',
 157+ 'utm_medium' => 'String identifying "utm_medium"',
 158+ 'utm_campaign' => 'String identifying "utm_campaign"',
 159+ 'email-opt' => 'Boolean assumed to be from a checkbox. This is actually the inverse of the E-mail opt-out checkbox.',
 160+ 'language' => array(
 161+ 'User language code. Messages will be translated appropriately (where possible).',
 162+ 'This will also determine what "Thank You" page the user sees upon completion of a donation at the gateway.' ),
 163+ 'owa_session' => 'String identifying the "owa_session"',
 164+ 'owa_ref' => 'String with the referring URL.',
 165+ 'contribution_tracking_id' => 'Our ID for the current contribution. Not supplied for new contributions.', //in fact, why is this here?
 166+ 'returnto' => 'String identifying an alternate "Thank You" page to show the user on completion of their transaction.',
 167+ 'tshirt' => 'Boolean indicating whether or not there is a t-shirt involved.',
 168+ 'size' => 'String indicating the desired size of the above t-shirt (if involved)',
 169+ 'premium_language' => 'Language code for the shirt. This will have no effect on message translation outside of the physical scope of the shirt.',
 170+ 'currency_code' => 'Currency code for the current transaction.',
 171+ 'fname' => "String: Donor's first name",
 172+ 'lname' => "String: Donor's last name",
 173+ 'email' => "String: Donor's email",
 174+ 'recurring_paypal' => 'Boolean identifying a recurring donation. Do not supply at all for a one-time donation.',
 175+ 'amountGiven' => 'Normalized amount.'
 176+ );
 177+ }
 178+
 179+ public function getDescription() {
 180+ return array(
 181+ 'Track donor contributions via API',
 182+ 'This API exists so we are able to eliminate the interstitial page',
 183+ 'that would otherwise be used to track contributions before sending',
 184+ 'the donor off to paypal (or wherever).',
 185+ );
 186+ }
 187+
 188+ public function getExamples() {
 189+ return array(
 190+ 'api.php?action=contributiontracking&comment=examplecomment&referrer=examplereferrer&gateway=paypal&amount=5.50',
 191+ );
 192+ }
 193+
 194+ public function getVersion() {
 195+ return __CLASS__ . ': $Id$';
 196+ }
 197+
 198+}
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ApiContributionTracking.php
___________________________________________________________________
Added: svn:keywords
1199 + Id
Added: svn:eol-style
2200 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking_OWA_ref.sql
@@ -0,0 +1,14 @@
 2+--
 3+-- Schema for OWA_ref
 4+--
 5+-- Used to normalize target pages for OWA "conversions"
 6+--
 7+
 8+CREATE TABLE IF NOT EXISTS /*_*/contribution_tracking_owa_ref (
 9+ -- URL of event
 10+ url VARBINARY(255) unique,
 11+
 12+ -- event ID
 13+ id INTEGER AUTO_INCREMENT PRIMARY KEY
 14+) /*$wgDBTableOptions*/;
 15+
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking_OWA_ref.sql
___________________________________________________________________
Added: svn:eol-style
116 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/tests/ContributionTrackingAPITest.php
@@ -0,0 +1,285 @@
 2+<?php
 3+
 4+/**
 5+ * Yes, I realize this whole test class is full of things that are more
 6+ * regression run by phpunit, than actual unit tests. For the sake of coverage,
 7+ * it's going to stay that way until we can completely refactor
 8+ * ContributionTracking_body.php (beyond splitting its newly-shared
 9+ * functionality out into something the new API can also reach).
 10+ * //TODO: Refactor ContributionTracking_body.php, and clean up this whole mess.
 11+ * //TODO: Add tests to make sure that garbage requests fail gracefully.
 12+ *
 13+ * //FIXME: Yes, this test class and ContributionTrackingTest are nearly exactly
 14+ * the same. They should probably be combined into a thing that tests both entry
 15+ * methods simultaneously with the same requests.
 16+ * @group Fundraising
 17+ * @group Splunge
 18+ * @author Katie Horn <khorn@wikimedia.org>
 19+ */
 20+class ContributionTrackingAPITest extends MediaWikiTestCase {
 21+
 22+ /**
 23+ * Takes $request parameters and checks them against $expected parameters in
 24+ * the data about to be returned by ApiContributionTracking.
 25+ * All assert failures will start with the $message_prefix so we know which
 26+ * test actually failed.
 27+ * @param array $request The request parameters
 28+ * @param array $expected Expected contents of the hidden form about to be
 29+ * reposted to the gateway.
 30+ * @param string $message_prefix A readable string that identifies the test
 31+ * on failed assert.
 32+ */
 33+ function assertExecute_responseAsExpected( $request, $expected, $message_prefix ) {
 34+ $result = $this->getAPIResultData( $request );
 35+ $result = $result['returns'];
 36+
 37+ $reposters = array( );
 38+
 39+ foreach ( $expected['fields'] as $name => $value ) {
 40+ if ( $name === 'custom' ) {
 41+ $this->assertTrue( is_numeric( $result['fields'][$name] ), $message_prefix . ": 'custom' should be a number." );
 42+ } elseif ( $name === 'item_name' && array_key_exists( 'language', $request ) && $request['language'] !== 'en' ) {
 43+ //TODO: Actually deal with the encoding mismatch here. Urgh.
 44+ $this->assertTrue( ($result['fields'][$name] != 'One-time Donation' ), $message_prefix . ": Alternate language is returning English strings by the API." );
 45+ } else {
 46+ $this->assertEquals( $value, $result['fields'][$name], $message_prefix . ": Field $name was not reposted as expected by the API" );
 47+ }
 48+ }
 49+
 50+ //and don't forget to check it's the proper action!
 51+ $this->assertEquals( $result['action']['url'], $expected['action'], $message_prefix . ": Contribution Tracking API form action was incorrect." );
 52+ }
 53+
 54+ /**
 55+ * Gets ApiContributionTracking's response in array format, for the given
 56+ * $request params.
 57+ * @global FauxRequest $wgRequest used to shoehorn in our own request vars.
 58+ * @param <type> $request Request vars we are sending to
 59+ * ApiContributionTracking.
 60+ * @return array Values to be returned by ApiContributionTracking
 61+ */
 62+ function getAPIResultData( $request ) {
 63+ global $wgRequest;
 64+ $request['format'] = 'xml';
 65+ $request['action'] = 'contributiontracking';
 66+ $wgRequest = new FauxRequest( $request );
 67+
 68+ $ctapi = new ApiMain( $wgRequest, true );
 69+ $ctapi->execute();
 70+ $api_response = $ctapi->getResult()->getData();
 71+ return $api_response;
 72+ }
 73+
 74+ /**
 75+ * Sets up a bare-bones request to send to ApiContributionTracking, and the
 76+ * values we expect to see in the response. Then calls
 77+ * assertExecute_responseAsExpected for the actual
 78+ * processing and assertions.
 79+ */
 80+ function testExecuteforRepostFields_minimal() {
 81+ $minimal = array(
 82+ 'referrer' => 'phpunit_api',
 83+ 'gateway' => 'paypal',
 84+ 'amount' => '8.80'
 85+ );
 86+
 87+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 88+ $expected = array(
 89+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 90+ 'fields' => array(
 91+ 'business' => 'donations@wikimedia.org',
 92+ 'item_number' => 'DONATE',
 93+ 'no_note' => 0,
 94+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 95+ 'currency_code' => 'USD',
 96+ 'cmd' => '_xclick',
 97+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 98+ 'item_name' => 'One-time donation',
 99+ 'amount' => '8.80',
 100+ 'custom' => '', //this is overridden later. Should be the id of the inserted transaction.
 101+ )
 102+ );
 103+
 104+ $this->assertExecute_responseAsExpected( $minimal, $expected, "Minimal Repost Test" );
 105+ }
 106+
 107+ /**
 108+ * Sets up a recurring payment type request to send to
 109+ * ApiContributionTracking, and the values we expect to see in the response
 110+ * after processing. Then calls assertExecute_responseAsExpected for the
 111+ * actual processing and assertions.
 112+ */
 113+ function testExecuteforRepostFields_recurring() {
 114+ //test paypal recurring
 115+ $recurring = array(
 116+ 'referrer' => 'phpunit_api',
 117+ 'gateway' => 'paypal',
 118+ 'amount' => '8.80',
 119+ 'recurring_paypal' => true
 120+ );
 121+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 122+ $expected = array(
 123+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 124+ 'fields' => array(
 125+ 'business' => 'donations@wikimedia.org',
 126+ 'item_number' => 'DONATE',
 127+ 'no_note' => 0,
 128+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 129+ 'currency_code' => 'USD',
 130+ 'cmd' => '_xclick',
 131+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 132+ 'item_name' => 'One-time donation',
 133+ 'a3' => '8.80',
 134+ 'custom' => '', //this is overridden later. Should be the id of the inserted transaction.
 135+ 't3' => 'M',
 136+ 'p3' => '1',
 137+ 'srt' => '12',
 138+ 'src' => '1',
 139+ 'sra' => '1',
 140+ 'cmd' => '_xclick-subscriptions',
 141+ 'item_name' => 'Recurring monthly donation',
 142+ )
 143+ );
 144+
 145+
 146+ $this->assertExecute_responseAsExpected( $recurring, $expected, "Paypal Recurring Test" );
 147+ }
 148+
 149+ /**
 150+ * Sets up a non-english request (in a language that has a translation) to
 151+ * send to ApiContributionTracking, and the values we expect to see in the
 152+ * response after processing. Then calls
 153+ * assertExecute_responseAsExpected for the actual processing and
 154+ * assertions.
 155+ * FIXME: something about the encoding makes this not work as expected.
 156+ */
 157+ function testExecuteforRepostFields_language() {
 158+
 159+ //test alternate language
 160+ $language = array(
 161+ 'referrer' => 'phpunit_api',
 162+ 'gateway' => 'paypal',
 163+ 'amount' => '8.80',
 164+ 'language' => 'ja'
 165+ );
 166+
 167+
 168+ $returnTitle = Title::newFromText( 'Donate-thanks/ja' );
 169+ $expected = array(
 170+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 171+ 'fields' => array(
 172+ 'business' => 'donations@wikimedia.org',
 173+ 'item_number' => 'DONATE',
 174+ 'no_note' => 0,
 175+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ), //Important to the language test.
 176+ 'currency_code' => 'USD',
 177+ 'cmd' => '_xclick',
 178+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 179+ 'item_name' => '1回だけ寄付', //This should be translated.
 180+ 'amount' => '8.80',
 181+ 'custom' => '',
 182+ )
 183+ );
 184+
 185+ $this->assertExecute_responseAsExpected( $language, $expected, "Translation Test" );
 186+ }
 187+
 188+ /**
 189+ * Sets up a "premium" request to send to ApiContributionTracking, and the
 190+ * values we expect to see in the response after processing. Then calls
 191+ * assertExecute_responseAsExpected for the actual processing and assertions.
 192+ */
 193+ function testExecuteforRepostFields_tshirts() {
 194+
 195+ //test T-shirtness
 196+ $tshirts = array(
 197+ 'referrer' => 'phpunit_api',
 198+ 'gateway' => 'paypal',
 199+ 'amount' => '8.80',
 200+ 'language' => 'en',
 201+ 'tshirt' => 'true',
 202+ 'size' => 'medium',
 203+ 'premium_language' => 'ja'
 204+ );
 205+
 206+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 207+ $expected = array(
 208+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 209+ 'fields' => array(
 210+ 'business' => 'donations@wikimedia.org',
 211+ 'item_number' => 'DONATE',
 212+ 'no_note' => 0,
 213+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 214+ 'currency_code' => 'USD',
 215+ 'cmd' => '_xclick',
 216+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 217+ 'item_name' => 'One-time donation',
 218+ 'amount' => '8.80',
 219+ 'custom' => '',
 220+ 'on0' => 'Shirt size',
 221+ 'os0' => 'medium',
 222+ 'on1' => 'Shirt language',
 223+ 'os1' => 'ja',
 224+ 'no_shipping' => 2
 225+ )
 226+ );
 227+
 228+ $this->assertExecute_responseAsExpected( $tshirts, $expected, "T-shirt Test" );
 229+ }
 230+
 231+ /**
 232+ * Tests to make sure the contribution was saved in the database properly.
 233+ * Assertions:
 234+ * The saved contribution ID is set to be reposted to paypal
 235+ * Each parameter saved to the contribution_tracking table is identical
 236+ * to the value we were trying to save, in the row matching the ID passed to
 237+ * paypal
 238+ * The owa_ref URL value is stored in the owa_ref table, and referenced
 239+ * by the correct id in the owa_ref column
 240+ *
 241+ */
 242+ function testExecuteForContributionSave() {
 243+ //TODO: Test inserting pure garbage.
 244+ $complete = array(
 245+ 'comment' => 'Interstitial Save',
 246+ 'referrer' => 'phpunit_api',
 247+ 'comment-option' => 'yep',
 248+ 'utm_source' => 'here',
 249+ 'utm_medium' => 'large',
 250+ 'utm_campaign' => 'testy01',
 251+ 'language' => 'en',
 252+ 'owa_session' => 'foo2',
 253+ 'owa_ref' => 'execute_save',
 254+ 'gateway' => 'paypal',
 255+ 'amount' => '6.60'
 256+ );
 257+ $table1_check = $complete;
 258+ $table1_check['anonymous'] = 0;
 259+ $table1_check['optout'] = 1;
 260+ $table1_check['note'] = $complete['comment'];
 261+ unset( $table1_check['owa_ref'] );
 262+ unset( $table1_check['comment'] );
 263+ unset( $table1_check['comment-option'] );
 264+ unset( $table1_check['gateway'] );
 265+ unset( $table1_check['amount'] );
 266+
 267+ $result = $this->getAPIResultData( $complete );
 268+ $result = $result['returns']['fields'];
 269+
 270+ //We're using paypal, one-time, so the ID will come back in the hidden "custom" field
 271+ $this->assertTrue( is_numeric( $result['custom'] ), "The saved transaction ID was not found" );
 272+
 273+ $db = ContributionTrackingProcessor::contributionTrackingConnection();
 274+ $row = $db->selectRow( 'contribution_tracking', '*', array( 'id' => $result['custom'] ) );
 275+
 276+ foreach ( $table1_check as $key => $value ) {
 277+ $this->assertEquals( $value, $row->$key, "$key does not match in the database." );
 278+ }
 279+
 280+ $row = $db->selectRow( 'contribution_tracking_owa_ref', '*', array( 'id' => $row->owa_ref ) );
 281+ $this->assertEquals( $complete['owa_ref'], $row->url, "OWA Reference lookup does not match" );
 282+ }
 283+
 284+}
 285+
 286+?>
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/tests/ContributionTrackingAPITest.php
___________________________________________________________________
Added: svn:eol-style
1287 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/tests/ContributionTrackingProcessorTest.php
@@ -0,0 +1,429 @@
 2+<?php
 3+
 4+/**
 5+ * Tests for the ContributionTrackingProcessor class. This class is used by both
 6+ * the interstitial page and the API to process donation requests, determine
 7+ * where the donor should be sent next, and send them there with all the
 8+ * required information in a format accepted by the gateway.
 9+ * @group Fundraising
 10+ * @group Splunge
 11+ * @author Katie Horn <khorn@wikimedia.org>
 12+ */
 13+class ContributionTrackingProcessorTest extends MediaWikiTestCase {
 14+
 15+ /**
 16+ * tests the rekey function in the ContributionTrackingProcessor.
 17+ */
 18+ function testRekey() {
 19+ $start = array(
 20+ 'bears' => 'green',
 21+ 'emus' => 'purple'
 22+ );
 23+ $expected = array(
 24+ 'llamas' => 'green',
 25+ 'emus' => 'purple'
 26+ );
 27+
 28+ ContributionTrackingProcessor::rekey( $start, 'bears', 'llamas' );
 29+
 30+ $this->assertEquals( $start, $expected, "Rekey is not working as expected." );
 31+ }
 32+
 33+ /**
 34+ * Tests the stage_checkbox function
 35+ * $start coming out as $expected will tell us that it works as expected
 36+ * with both existing and non-existant keys
 37+ */
 38+ function testStageCheckbox() {
 39+ $start = array(
 40+ 'bears' => 'green',
 41+ 'emus' => 'purple'
 42+ );
 43+ $expected = array(
 44+ 'bears' => 1,
 45+ 'emus' => 'purple'
 46+ );
 47+
 48+ ContributionTrackingProcessor::stage_checkbox( $start, 'bears' );
 49+ ContributionTrackingProcessor::stage_checkbox( $start, 'llamas' );
 50+
 51+ $this->assertEquals( $start, $expected, "stage_checkbox is not working as expected." );
 52+ }
 53+
 54+ /**
 55+ * tests the stage_contribution function, and as a by-product,
 56+ * the getContributionDefaults function as well.
 57+ * Asserts that:
 58+ * A staged contribution with no relevant fields will come back equal
 59+ * to exactly the defaults
 60+ * A staged contribution with some relevant fields will come back as
 61+ * the defaults, with keys overwritten by the supplied fields where they
 62+ * exist
 63+ * A staged contribution with some relevant fields and some irrelevant
 64+ * fields will come back as the defaults, with relevant keys overwritten by
 65+ * the supplied fields where they exist. The irrelevant fields should not
 66+ * come back at all.
 67+ * A staged contribution with boolean (checkbox) fields will come back
 68+ * with those values either set to "1" or "0", depending solely on whether
 69+ * they exist in the supplied parameters or not.
 70+ */
 71+ function testStageContribution() {
 72+ $start = array(
 73+ 'bears' => 'green',
 74+ 'emus' => 'purple'
 75+ );
 76+ $expected = ContributionTrackingProcessor::getContributionDefaults();
 77+ $result = ContributionTrackingProcessor::stage_contribution( $start );
 78+ $this->assertEquals( $expected, $result, "Staged Contribution with no defined fields should be exactly all the default values." );
 79+
 80+ $additional = array(
 81+ 'note' => 'B Flat',
 82+ 'referrer' => 'phpunit_processor',
 83+ 'anonymous' => 'Raspberries'
 84+ );
 85+
 86+ $expected = array(
 87+ 'note' => 'B Flat',
 88+ 'referrer' => 'phpunit_processor',
 89+ 'anonymous' => 1,
 90+ 'utm_source' => null,
 91+ 'utm_medium' => null,
 92+ 'utm_campaign' => null,
 93+ 'optout' => 0,
 94+ 'language' => null,
 95+ 'owa_session' => null,
 96+ 'owa_ref' => null,
 97+ 'ts' => null,
 98+ );
 99+ $result = ContributionTrackingProcessor::stage_contribution( $additional );
 100+ $this->assertEquals( $expected, $result, "Contribution not staging properly." );
 101+
 102+ $start = array_merge( $start, $additional );
 103+ $result = ContributionTrackingProcessor::stage_contribution( $start );
 104+ $this->assertEquals( $expected, $result, "Contribution not staging properly." );
 105+
 106+
 107+ $complete = array(
 108+ 'note' => 'Batman',
 109+ 'referrer' => 'phpunit_processor',
 110+ 'anonymous' => 'of course',
 111+ 'utm_source' => 'batcave',
 112+ 'utm_medium' => 'Alfred',
 113+ 'utm_campaign' => 'Joker',
 114+ 'language' => 'squeak!',
 115+ 'owa_session' => 'arghargh',
 116+ 'owa_ref' => 'test',
 117+ 'ts' => '11235813'
 118+ );
 119+
 120+ $expected = $complete;
 121+ $expected['anonymous'] = 1;
 122+ $expected['optout'] = 0;
 123+
 124+ $result = ContributionTrackingProcessor::stage_contribution( $complete );
 125+ $this->assertEquals( $expected, $result, "Contribution not staging properly." );
 126+ }
 127+
 128+ /**
 129+ * Tests saveNewContribution()
 130+ * Assertions:
 131+ * saveNewContributions returns a number.
 132+ * Each parameter saved to the contribution_tracking table is identical
 133+ * to the value we were trying to save, in the row matching the ID returned
 134+ * from saveNewContribution.
 135+ * The owa_ref URL value is stored in the owa_ref table, and referenced
 136+ * by the correct id in the owa_ref column
 137+ *
 138+ */
 139+ function testSaveNewContribution() {
 140+ //TODO: Test inserting pure garbage.
 141+ $complete = array(
 142+ 'note' => 'Batman is pretty awesome.',
 143+ 'referrer' => 'phpunit_processor',
 144+ 'anonymous' => 'of course',
 145+ 'utm_source' => 'batcave',
 146+ 'utm_medium' => 'Alfred',
 147+ 'utm_campaign' => 'Joker',
 148+ 'language' => 'squeak!',
 149+ 'owa_session' => 'arghargh',
 150+ 'owa_ref' => 'test'
 151+ );
 152+ $table1_check = $complete;
 153+ $table1_check['anonymous'] = 1;
 154+ $table1_check['optout'] = 0;
 155+ unset( $table1_check['owa_ref'] );
 156+
 157+ $id = ContributionTrackingProcessor::saveNewContribution( $complete );
 158+ $this->assertTrue( is_numeric( $id ), "Returned value is not an ID." );
 159+
 160+ $db = ContributionTrackingProcessor::contributionTrackingConnection();
 161+ $row = $db->selectRow( 'contribution_tracking', '*', array( 'id' => $id ) );
 162+
 163+ foreach ( $table1_check as $key => $value ) {
 164+ $this->assertEquals( $value, $row->$key, "$key does not match in the database." );
 165+ }
 166+
 167+ $row = $db->selectRow( 'contribution_tracking_owa_ref', '*', array( 'id' => $row->owa_ref ) );
 168+ $this->assertEquals( $complete['owa_ref'], $row->url, "OWA Reference lookup does not match" );
 169+ }
 170+
 171+ /**
 172+ * tests the getRepostFields function.
 173+ * Assertions:
 174+ * getRepostFields returns an array.
 175+ * getRepostFields returns expected fields for a one-time paypal
 176+ * donation.
 177+ * getRepostFields returns expected fields for a one-time paypal
 178+ * donation.
 179+ * getRepostFields returns expected fields for a one-time paypal
 180+ * donation.
 181+ * getRepostFields returns translated fields (when they will be
 182+ * displayed by the gateway) and return-to's for the specified language.
 183+ *
 184+ */
 185+ function testGetRepostFields() {
 186+ //TODO: More here.
 187+ $minimal = array(
 188+ 'referrer' => 'phpunit_processor',
 189+ 'gateway' => 'paypal',
 190+ 'amount' => '8.80'
 191+ );
 192+
 193+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 194+ $expected = array(
 195+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 196+ 'fields' => array(
 197+ 'business' => 'donations@wikimedia.org',
 198+ 'item_number' => 'DONATE',
 199+ 'no_note' => 0,
 200+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 201+ 'currency_code' => 'USD',
 202+ 'cmd' => '_xclick',
 203+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 204+ 'item_name' => 'One-time donation',
 205+ 'amount' => '8.80',
 206+ 'custom' => '',
 207+ )
 208+ );
 209+
 210+ $ret = ContributionTrackingProcessor::getRepostFields( $minimal );
 211+ $this->assertTrue( is_array( $ret ), "Returned value is not an array" );
 212+
 213+ $this->assertEquals( $ret, $expected, "Fields for reposting (Paypal, one-time) do not match expected fields" );
 214+
 215+ //test paypal recurring
 216+ $minimal['recurring_paypal'] = true;
 217+ $expected['fields']['t3'] = 'M';
 218+ $expected['fields']['p3'] = '1';
 219+ $expected['fields']['srt'] = '12';
 220+ $expected['fields']['src'] = '1';
 221+ $expected['fields']['sra'] = '1';
 222+ $expected['fields']['cmd'] = '_xclick-subscriptions';
 223+ $expected['fields']['item_name'] = 'Recurring monthly donation';
 224+ $expected['fields']['a3'] = '8.80';
 225+ unset( $expected['fields']['amount'] );
 226+
 227+
 228+ $ret = ContributionTrackingProcessor::getRepostFields( $minimal );
 229+ $this->assertEquals( $ret, $expected, "Fields for reposting (Paypal, recurring) do not match expected fields" );
 230+
 231+
 232+ //test moneybookers... just in case anybody cares anymore.
 233+ unset( $minimal['recurring_paypal'] );
 234+ $minimal['gateway'] = 'moneybookers';
 235+
 236+ $expected = array(
 237+ 'action' => 'https://www.moneybookers.com/app/payment.pl',
 238+ 'fields' => Array
 239+ (
 240+ 'merchant_fields' => 'os0',
 241+ 'pay_to_email' => 'donation@wikipedia.org',
 242+ 'status_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/moneybookers',
 243+ 'language' => 'en',
 244+ 'detail1_description' => 'One-time donation',
 245+ 'detail1_text' => 'DONATE',
 246+ 'currency' => 'USD',
 247+ 'amount' => '8.80',
 248+ 'custom' => '',
 249+ )
 250+ );
 251+ $ret = ContributionTrackingProcessor::getRepostFields( $minimal );
 252+ $this->assertEquals( $ret, $expected, "Fields for reposting (moneybookers, one-time) do not match expected fields" );
 253+
 254+ //test alternate language
 255+ $minimal['gateway'] = 'paypal';
 256+ $minimal['language'] = 'ja'; //japanese.
 257+
 258+ $returnTitle = Title::newFromText( 'Donate-thanks/ja' );
 259+ $expected = array(
 260+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 261+ 'fields' => array(
 262+ 'business' => 'donations@wikimedia.org',
 263+ 'item_number' => 'DONATE',
 264+ 'no_note' => 0,
 265+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ), //Important to the language test.
 266+ 'currency_code' => 'USD',
 267+ 'cmd' => '_xclick',
 268+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 269+ 'item_name' => '1回だけ寄付', //This should be translated.
 270+ 'amount' => '8.80',
 271+ 'custom' => '',
 272+ )
 273+ );
 274+
 275+ $ret = ContributionTrackingProcessor::getRepostFields( $minimal );
 276+ $this->assertEquals( $ret, $expected, "Fields for reposting (paypal, one-time, language=ja) do not match expected fields" );
 277+
 278+ //test T-shirtness
 279+ $minimal['gateway'] = 'paypal';
 280+ $minimal['language'] = 'en';
 281+ $minimal['tshirt'] = true;
 282+ $minimal['size'] = 'medium';
 283+ $minimal['premium_language'] = 'ja';
 284+
 285+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 286+ $expected = array(
 287+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 288+ 'fields' => array(
 289+ 'business' => 'donations@wikimedia.org',
 290+ 'item_number' => 'DONATE',
 291+ 'no_note' => 0,
 292+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 293+ 'currency_code' => 'USD',
 294+ 'cmd' => '_xclick',
 295+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 296+ 'item_name' => 'One-time donation',
 297+ 'amount' => '8.80',
 298+ 'custom' => '',
 299+ 'on0' => 'Shirt size',
 300+ 'os0' => 'medium',
 301+ 'on1' => 'Shirt language',
 302+ 'os1' => 'ja',
 303+ 'no_shipping' => 2
 304+ )
 305+ );
 306+
 307+ $ret = ContributionTrackingProcessor::getRepostFields( $minimal );
 308+ $this->assertEquals( $ret, $expected, "Fields for reposting (paypal, one-time, T-shirt) do not match expected fields" );
 309+ }
 310+
 311+ /**
 312+ * tests the stage_repost function
 313+ * Assertions:
 314+ * Garbage in, defaults out.
 315+ * The recurring_paypal key is treated like a boolean
 316+ */
 317+ function testStageRepost() {
 318+ $start = array(
 319+ 'bears' => 'green',
 320+ 'emus' => 'purple'
 321+ );
 322+ ContributionTrackingProcessor::getLanguage( array( 'language' => 'en' ) );
 323+ $expected = ContributionTrackingProcessor::getRepostDefaults();
 324+ $expected['item_name'] = 'One-time donation';
 325+ $expected['notify_url'] = 'https://civicrm.wikimedia.org/fundcore_gateway/paypal';
 326+
 327+ $result = ContributionTrackingProcessor::stage_repost( $start );
 328+ $this->assertEquals( $expected, $result, "Staged Repost with no defined fields should be exactly all the default values." );
 329+
 330+ $additional = array(
 331+ 'gateway' => 'testgateway',
 332+ 'recurring_paypal' => 'raspberries',
 333+ 'amount' => '6.60'
 334+ );
 335+
 336+ $expected = array(
 337+ 'gateway' => 'testgateway',
 338+ 'tshirt' => false,
 339+ 'size' => false,
 340+ 'premium_language' => false,
 341+ 'currency_code' => 'USD',
 342+ 'return' => 'Donate-thanks/en',
 343+ 'fname' => '',
 344+ 'lname' => '',
 345+ 'email' => '',
 346+ 'recurring_paypal' => '1',
 347+ 'amount' => '6.60',
 348+ 'amount_given' => '',
 349+ 'contribution_tracking_id' => '',
 350+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 351+ 'item_name' => 'Recurring monthly donation'
 352+ );
 353+ $result = ContributionTrackingProcessor::stage_repost( $additional );
 354+ $this->assertEquals( $expected, $result, "Repost not staging properly." );
 355+
 356+
 357+ unset( $additional['recurring_paypal'] );
 358+ $expected['recurring_paypal'] = 0;
 359+ $expected['item_name'] = 'One-time donation';
 360+ $result = ContributionTrackingProcessor::stage_repost( $additional );
 361+ $this->assertEquals( $expected, $result, "Repost not staging properly." );
 362+ }
 363+
 364+ /**
 365+ * tests the get_owa_ref_id function
 366+ * Assertions:
 367+ * The unique add comes back with a numeric id.
 368+ * The second call also comes back with a numeric id.
 369+ * The insert and the lookup come back with the same numeric id.
 370+ */
 371+ function testGetOWARefID() {
 372+ $testRef = "test_ref_" . time();
 373+ $id_1 = ContributionTrackingProcessor::get_owa_ref_id( $testRef ); //add
 374+ $id_2 = ContributionTrackingProcessor::get_owa_ref_id( $testRef ); //get
 375+ $this->assertTrue( is_numeric( $id_1 ), "First id is not numeric: Problem adding OWA Ref URL" );
 376+ $this->assertTrue( is_numeric( $id_2 ), "Second id is not numeric: Problem retrieving OWA Ref ID" );
 377+ $this->assertEquals( $id_1, $id_2, "IDs do not match." );
 378+ }
 379+
 380+ /**
 381+ * tests the getLanguage function.
 382+ * NOTE: Static vars are involved here.
 383+ * Assertions:
 384+ * getLanguage with no parameters returns english (if none of the
 385+ * previous tests set the var differently. Static vars have tricky initial
 386+ * conditions...)
 387+ * Passing getLanguage a different language than the one previously in
 388+ * use will cause the var to reset to the explicit language. Messages should
 389+ * be sent in the new language.
 390+ */
 391+ function testGetLanguage() {
 392+ $messageKey = 'contributiontracking';
 393+ $messageBG = 'Проследяване на дарението';
 394+ $messageEN = 'Contribution tracking';
 395+
 396+ $code = ContributionTrackingProcessor::getLanguage();
 397+ $this->assertEquals( $code, 'en', "Default language is not US (or your test has a hangover)" );
 398+
 399+ $params['language'] = 'bg';
 400+ $code = ContributionTrackingProcessor::getLanguage( $params );
 401+ $this->assertEquals( $params['language'], $code, "Returned language is not the one we just sent." );
 402+ $message = ContributionTrackingProcessor::msg( $messageKey );
 403+ $this->assertEquals( $message, $messageBG, "Returned language is not the one we just sent." );
 404+
 405+ $params['language'] = 'en';
 406+ $code = ContributionTrackingProcessor::getLanguage( $params );
 407+ $this->assertEquals( $params['language'], $code, "Returned language is not the one we just sent." );
 408+ $message = ContributionTrackingProcessor::msg( $messageKey );
 409+ $this->assertEquals( $message, $messageEN, "Returned language is not the one we just sent." );
 410+ }
 411+
 412+ /**
 413+ * Helper function that recursively sorts arrays by key. Nice for debugging
 414+ * failed assertEquals, where you're comparing large arrays.
 415+ * @param array $array The array you want to recursively ksort.
 416+ * @return array The ksorted array.
 417+ */
 418+ function deepKSort( $array ) {
 419+ foreach ( $array as $key => $value ) {
 420+ if ( is_array( $value ) ) {
 421+ $array[$key] = $this->deepKSort( $value );
 422+ }
 423+ }
 424+ ksort( $array );
 425+ return $array;
 426+ }
 427+
 428+}
 429+
 430+?>
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/tests/ContributionTrackingProcessorTest.php
___________________________________________________________________
Added: svn:eol-style
1431 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/tests/ContributionTrackingTest.php
@@ -0,0 +1,330 @@
 2+<?php
 3+
 4+/**
 5+ * Yes, I realize this whole test class is full of things that are more
 6+ * regression run by phpunit, than actual unit tests. For the sake of coverage,
 7+ * it's going to stay that way until we can completely refactor
 8+ * ContributionTracking_body.php (beyond splitting its newly-shared
 9+ * functionality out into something the new API can also reach).
 10+ * //TODO: Refactor ContributionTracking_body.php, and clean up this whole mess.
 11+ * //TODO: Add tests to make sure that garbage requests fail gracefully.
 12+ *
 13+ * //FIXME: Yes, this test class and ContributionTrackingAPITest are nearly
 14+ * exactly the same. They should probably be combined into a thing that tests
 15+ * both entry methods simultaneously with the same requests.
 16+ * @group Fundraising
 17+ * @group Splunge
 18+ * @author Katie Horn <khorn@wikimedia.org>
 19+ */
 20+class ContributionTrackingTest extends MediaWikiTestCase {
 21+
 22+ /**
 23+ * Takes $request parameters and checks them against $expected parameters in
 24+ * the hidden form that comes back from the ContributionTracking page.
 25+ * All assert failures will start with the $message_prefix so we know which
 26+ * test actually failed.
 27+ * @param array $request The request parameters
 28+ * @param array $expected Expected contents of the hidden form about to be
 29+ * reposted to the gateway.
 30+ * @param string $message_prefix A readable string that identifies the test
 31+ * on failed assert.
 32+ */
 33+ function assertExecute_repostFormAsExpected( $request, $expected, $message_prefix ) {
 34+ $page_xml = $this->getPageHTML( $request );
 35+
 36+ $reposters = array( );
 37+ foreach ( $page_xml->getElementsByTagName( 'input' ) as $node ) {
 38+ $attributes = $this->getNodeAttributes( $node );
 39+ if ( $attributes['type'] == 'hidden' ) {
 40+ $reposters[$attributes['name']] = $attributes['value'];
 41+ }
 42+ }
 43+
 44+ foreach ( $expected['fields'] as $name => $value ) {
 45+ if ( $name === 'custom' ) {
 46+ $this->assertTrue( is_numeric( $reposters[$name] ), $message_prefix . ": 'custom' should be a number." );
 47+ } elseif ( $name === 'item_name' && array_key_exists( 'language', $request ) && $request['language'] !== 'en' ) {
 48+ //TODO: Actually deal with the encoding mismatch here. Urgh.
 49+ $this->assertTrue( ($reposters[$name] != 'One-time Donation' ), $message_prefix . ": Alternate language is coming up English." );
 50+ } else {
 51+ $this->assertEquals( $value, $reposters[$name], $message_prefix . ": Field $name was not reposted as expected by the interstitial page" );
 52+ }
 53+ }
 54+
 55+ //and don't forget to check it's the proper action!
 56+ foreach ( $page_xml->getElementsByTagName( 'form' ) as $node ) {
 57+ $attributes = $this->getNodeAttributes( $node );
 58+ if ( $attributes['name'] == 'contributiontracking' ) {
 59+ $this->assertEquals( $attributes['action'], $expected['action'], $message_prefix . ": Form action was incorrect!" );
 60+ }
 61+ }
 62+ }
 63+
 64+ /**
 65+ * Gets the ContributionTracking page's HTML and loads it into a DomDocument
 66+ * @global FauxRequest $wgRequest used to shoehorn in our own request vars.
 67+ * @global <type> $wgOut Needed so I can grab the resultant HTML.
 68+ * @global <type> $wgTitle Needed to solve a totally weird bug. (See below)
 69+ * @param <type> $request Request vars we are sending to the
 70+ * ContributionTracking page
 71+ * @return DomDocument Loaded up with the generated page's html.
 72+ */
 73+ function getPageHTML( $request ) {
 74+ global $wgRequest, $wgOut, $wgTitle;
 75+
 76+ //The next line addresses a totally weird bug I found. Uncomment the next line and run the test to see it.
 77+ $wgTitle = Title::newFromText( 'whatever' );
 78+
 79+ $ctpage = new ContributionTracking();
 80+ $wgRequest = new FauxRequest( $request );
 81+ if ( array_key_exists( 'language', $request ) ) {
 82+ $language = $request['language'];
 83+ } else {
 84+ $language = 'en';
 85+ }
 86+ $ctpage->execute( $language );
 87+ $page_xml = new DomDocument( '1.0' );
 88+
 89+ $page_xml->loadHTML( trim( $wgOut->getHTML() ) );
 90+
 91+ return $page_xml;
 92+
 93+ //echo "Hidden form: " . print_r($reposters, true);
 94+ //echo "wgOut: ##" . print_r($wgOut->getHTML(), true) . "##\n";
 95+ }
 96+
 97+ /**
 98+ * Sets up a bare-bones request to send to the interstitial page, and the
 99+ * values we expect to see in the page's hidden repost form after
 100+ * processing. Then calls assertExecute_repostFormAsExpected for the actual
 101+ * processing and assertions.
 102+ */
 103+ function testExecuteforRepostFields_minimal() {
 104+ $minimal = array(
 105+ 'referrer' => 'phpunit_interstitial',
 106+ 'gateway' => 'paypal',
 107+ 'amount' => '8.80'
 108+ );
 109+
 110+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 111+ $expected = array(
 112+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 113+ 'fields' => array(
 114+ 'business' => 'donations@wikimedia.org',
 115+ 'item_number' => 'DONATE',
 116+ 'no_note' => 0,
 117+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 118+ 'currency_code' => 'USD',
 119+ 'cmd' => '_xclick',
 120+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 121+ 'item_name' => 'One-time donation',
 122+ 'amount' => '8.80',
 123+ 'custom' => '', //this is overridden later. Should be the id of the inserted transaction.
 124+ )
 125+ );
 126+
 127+ $this->assertExecute_repostFormAsExpected( $minimal, $expected, "Minimal Repost Test" );
 128+ }
 129+
 130+ /**
 131+ * Sets up a recurring payment type request to send to the interstitial
 132+ * page, and the values we expect to see in the page's hidden repost form
 133+ * after processing. Then calls assertExecute_repostFormAsExpected for the
 134+ * actual processing and assertions.
 135+ */
 136+ function testExecuteforRepostFields_recurring() {
 137+ //test paypal recurring
 138+ $recurring = array(
 139+ 'referrer' => 'phpunit_interstitial',
 140+ 'gateway' => 'paypal',
 141+ 'amount' => '8.80',
 142+ 'recurring_paypal' => true
 143+ );
 144+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 145+ $expected = array(
 146+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 147+ 'fields' => array(
 148+ 'business' => 'donations@wikimedia.org',
 149+ 'item_number' => 'DONATE',
 150+ 'no_note' => 0,
 151+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 152+ 'currency_code' => 'USD',
 153+ 'cmd' => '_xclick',
 154+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 155+ 'item_name' => 'One-time donation',
 156+ 'a3' => '8.80',
 157+ 'custom' => '', //this is overridden later. Should be the id of the inserted transaction.
 158+ 't3' => 'M',
 159+ 'p3' => '1',
 160+ 'srt' => '12',
 161+ 'src' => '1',
 162+ 'sra' => '1',
 163+ 'cmd' => '_xclick-subscriptions',
 164+ 'item_name' => 'Recurring monthly donation',
 165+ )
 166+ );
 167+
 168+
 169+ $this->assertExecute_repostFormAsExpected( $recurring, $expected, "Paypal Recurring Test" );
 170+ }
 171+
 172+ /**
 173+ * Sets up a non-english request (in a language that has a translation) to
 174+ * send to the interstitial page, and the values we expect to see in the
 175+ * page's hidden repost form after processing. Then calls
 176+ * assertExecute_repostFormAsExpected for the actual processing and
 177+ * assertions.
 178+ * FIXME: something about the encoding makes this not work as expected.
 179+ */
 180+ function testExecuteforRepostFields_language() {
 181+
 182+ //test alternate language
 183+ $language = array(
 184+ 'referrer' => 'phpunit_interstitial',
 185+ 'gateway' => 'paypal',
 186+ 'amount' => '8.80',
 187+ 'language' => 'ja'
 188+ );
 189+
 190+
 191+ $returnTitle = Title::newFromText( 'Donate-thanks/ja' );
 192+ $expected = array(
 193+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 194+ 'fields' => array(
 195+ 'business' => 'donations@wikimedia.org',
 196+ 'item_number' => 'DONATE',
 197+ 'no_note' => 0,
 198+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ), //Important to the language test.
 199+ 'currency_code' => 'USD',
 200+ 'cmd' => '_xclick',
 201+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 202+ 'item_name' => '1回だけ寄付', //This should be translated.
 203+ 'amount' => '8.80',
 204+ 'custom' => '',
 205+ )
 206+ );
 207+
 208+ $this->assertExecute_repostFormAsExpected( $language, $expected, "Translation Test" );
 209+ }
 210+
 211+ /**
 212+ * Sets up a "premium" request to send to the interstitial page, and the
 213+ * values we expect to see in the page's hidden repost form after
 214+ * processing. Then calls assertExecute_repostFormAsExpected for the actual
 215+ * processing and assertions.
 216+ */
 217+ function testExecuteforRepostFields_tshirts() {
 218+
 219+ //test T-shirtness
 220+ $tshirts = array(
 221+ 'referrer' => 'phpunit_interstitial',
 222+ 'gateway' => 'paypal',
 223+ 'amount' => '8.80',
 224+ 'language' => 'en',
 225+ 'tshirt' => 'true',
 226+ 'size' => 'medium',
 227+ 'premium_language' => 'ja'
 228+ );
 229+
 230+ $returnTitle = Title::newFromText( 'Donate-thanks/en' );
 231+ $expected = array(
 232+ 'action' => 'https://www.paypal.com/cgi-bin/webscr',
 233+ 'fields' => array(
 234+ 'business' => 'donations@wikimedia.org',
 235+ 'item_number' => 'DONATE',
 236+ 'no_note' => 0,
 237+ 'return' => wfExpandUrl( $returnTitle->getFullUrl(), PROTO_HTTP ),
 238+ 'currency_code' => 'USD',
 239+ 'cmd' => '_xclick',
 240+ 'notify_url' => 'https://civicrm.wikimedia.org/fundcore_gateway/paypal',
 241+ 'item_name' => 'One-time donation',
 242+ 'amount' => '8.80',
 243+ 'custom' => '',
 244+ 'on0' => 'Shirt size',
 245+ 'os0' => 'medium',
 246+ 'on1' => 'Shirt language',
 247+ 'os1' => 'ja',
 248+ 'no_shipping' => 2
 249+ )
 250+ );
 251+
 252+ $this->assertExecute_repostFormAsExpected( $tshirts, $expected, "T-shirt Test" );
 253+ }
 254+
 255+ /**
 256+ * Tests to make sure the contribution was saved in the database properly.
 257+ * Assertions:
 258+ * The saved contribution ID is reposted to paypal
 259+ * Each parameter saved to the contribution_tracking table is identical
 260+ * to the value we were trying to save, in the row matching the ID passed to
 261+ * paypal
 262+ * The owa_ref URL value is stored in the owa_ref table, and referenced
 263+ * by the correct id in the owa_ref column
 264+ *
 265+ */
 266+ function testExecuteForContributionSave() {
 267+ //TODO: Test inserting pure garbage.
 268+ $complete = array(
 269+ 'comment' => 'Interstitial Save',
 270+ 'referrer' => 'phpunit_interstitial',
 271+ 'comment-option' => 'yep',
 272+ 'utm_source' => 'here',
 273+ 'utm_medium' => 'large',
 274+ 'utm_campaign' => 'testy01',
 275+ 'language' => 'en',
 276+ 'owa_session' => 'foo2',
 277+ 'owa_ref' => 'execute_save',
 278+ 'gateway' => 'paypal',
 279+ 'amount' => '6.60'
 280+ );
 281+ $table1_check = $complete;
 282+ $table1_check['anonymous'] = 0;
 283+ $table1_check['optout'] = 1;
 284+ $table1_check['note'] = $complete['comment'];
 285+ unset( $table1_check['owa_ref'] );
 286+ unset( $table1_check['comment'] );
 287+ unset( $table1_check['comment-option'] );
 288+ unset( $table1_check['gateway'] );
 289+ unset( $table1_check['amount'] );
 290+
 291+ $page_xml = $this->getPageHTML( $complete );
 292+
 293+ //We're using paypal, one-time, so the ID will come back in the hidden "custom" field
 294+
 295+ $reposters = array( );
 296+ foreach ( $page_xml->getElementsByTagName( 'input' ) as $node ) {
 297+ $attributes = $this->getNodeAttributes( $node );
 298+ if ( $attributes['type'] == 'hidden' ) {
 299+ $reposters[$attributes['name']] = $attributes['value'];
 300+ }
 301+ }
 302+
 303+ $this->assertTrue( is_numeric( $reposters['custom'] ), "The saved transaction ID was not found" );
 304+
 305+ $db = ContributionTrackingProcessor::contributionTrackingConnection();
 306+ $row = $db->selectRow( 'contribution_tracking', '*', array( 'id' => $reposters['custom'] ) );
 307+
 308+ foreach ( $table1_check as $key => $value ) {
 309+ $this->assertEquals( $value, $row->$key, "$key does not match in the database." );
 310+ }
 311+
 312+ $row = $db->selectRow( 'contribution_tracking_owa_ref', '*', array( 'id' => $row->owa_ref ) );
 313+ $this->assertEquals( $complete['owa_ref'], $row->url, "OWA Reference lookup does not match" );
 314+ }
 315+
 316+ /**
 317+ *
 318+ * @param DOMNode $node A DOMNode that ostensibly has attributes we need to retrieve.
 319+ * @return array All of $node's attributes in a key/value array.
 320+ */
 321+ function getNodeAttributes( $node ) {
 322+ $attributes = array( );
 323+ foreach ( $node->attributes as $name => $attrNode ) {
 324+ $attributes[$name] = $attrNode->value;
 325+ }
 326+ return $attributes;
 327+ }
 328+
 329+}
 330+
 331+?>
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/tests/ContributionTrackingTest.php
___________________________________________________________________
Added: svn:eol-style
1332 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.pg.sql
@@ -0,0 +1,25 @@
 2+
 3+-- Schema for the ContributionTracking extension
 4+-- Postgres version
 5+
 6+BEGIN;
 7+
 8+CREATE TABLE contribution_tracking (
 9+ id SERIAL PRIMARY KEY,
 10+ contribution_id INTEGER,
 11+ note TEXT,
 12+ referrer TEXT,
 13+ anonymous SMALLINT,
 14+ utm_source TEXT,
 15+ utm_medium TEXT,
 16+ utm_campaign TEXT,
 17+ optout SMALLINT,
 18+ language TEXT,
 19+ ts TIMESTAMPTZ
 20+);
 21+
 22+CREATE UNIQUE INDEX contribution_tracking_index1 ON contribution_tracking(contribution_id);
 23+CREATE UNIQUE INDEX contribution_tracking_index2 ON contribution_tracking(ts);
 24+
 25+COMMIT;
 26+
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.pg.sql
___________________________________________________________________
Added: svn:eol-style
127 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking_body.php
@@ -0,0 +1,113 @@
 2+<?php
 3+
 4+class ContributionTracking extends UnlistedSpecialPage {
 5+
 6+ function __construct() {
 7+ parent::__construct( 'ContributionTracking' );
 8+ }
 9+
 10+ function execute( $language ) {
 11+ global $wgRequest, $wgOut, $wgContributionTrackingReturnToURLDefault;
 12+
 13+ if ( !preg_match( '/^[a-z-]+$/', $language ) ) {
 14+ $language = 'en';
 15+ }
 16+ $this->lang = Language::factory( $language );
 17+
 18+ $this->setHeaders();
 19+
 20+ $gateway = $wgRequest->getText( 'gateway' );
 21+ if ( !in_array( $gateway, array( 'paypal', 'moneybookers' ) ) ) {
 22+ $wgOut->showErrorPage( 'contrib-tracking-error', 'contrib-tracking-error-text' );
 23+ return;
 24+ }
 25+
 26+ // Store the contribution data
 27+ if ( $wgRequest->getVal( 'contribution_tracking_id' ) ) {
 28+ $contribution_tracking_id = $wgRequest->getVal( 'contribution_tracking_id', 0 );
 29+ } else {
 30+ $tracked_contribution = array(
 31+ 'note' => $wgRequest->getVal( 'comment' ),
 32+ 'referrer' => $wgRequest->getVal( 'referrer' ),
 33+ 'anonymous' => $wgRequest->getCheck( 'comment-option', false ) ? false : true, //yup: 'anonymous' = !comment-option
 34+ 'utm_source' => $wgRequest->getVal( 'utm_source' ),
 35+ 'utm_medium' => $wgRequest->getVal( 'utm_medium' ),
 36+ 'utm_campaign' => $wgRequest->getVal( 'utm_campaign' ),
 37+ 'optout' => $wgRequest->getCheck( 'email-opt', false ) ? false : true, //Also: 'optout' = !email-opt.
 38+ 'language' => $wgRequest->getVal( 'language' ),
 39+ 'owa_session' => $wgRequest->getVal( 'owa_session' ),
 40+ 'owa_ref' => $wgRequest->getVal( 'owa_ref', null ),
 41+ //'ts' => $ts,
 42+ );
 43+ $contribution_tracking_id = ContributionTrackingProcessor::saveNewContribution( $tracked_contribution );
 44+ }
 45+
 46+ $params = array(
 47+ 'gateway' => $gateway,
 48+ 'tshirt' => $wgRequest->getVal( 'tshirt' ),
 49+ 'return' => $wgRequest->getText( 'returnto', "Donate-thanks/$language" ),
 50+ 'currency_code' => $wgRequest->getText( 'currency_code', 'USD' ),
 51+ 'fname' => $wgRequest->getText( 'fname', null ),
 52+ 'lname' => $wgRequest->getText( 'lname', null ),
 53+ 'email' => $wgRequest->getText( 'email', null ),
 54+ 'address1' => $wgRequest->getText( 'address1', null ),
 55+ 'city' => $wgRequest->getText( 'city', null ),
 56+ 'state' => $wgRequest->getText( 'state', null ),
 57+ 'zip' => $wgRequest->getText( 'zip', null ),
 58+ 'country' => $wgRequest->getText( 'country', null ),
 59+ 'address_override' => $wgRequest->getText( 'address_override', '0' ),
 60+ 'recurring_paypal' => $wgRequest->getText( 'recurring_paypal' ),
 61+ 'amount' => $wgRequest->getVal( 'amount' ),
 62+ 'amount_given' => $wgRequest->getVal( 'amountGiven' ),
 63+ 'contribution_tracking_id' => $contribution_tracking_id,
 64+ 'language' => $language,
 65+ );
 66+
 67+ if ( $params['tshirt'] ) {
 68+ $params['size'] = $wgRequest->getText( 'size' );
 69+ $params['premium_language'] = $wgRequest->getText( 'premium_language' );
 70+ }
 71+
 72+ foreach ( $params as $key => $value ) {
 73+ if ( $value === "" || $value === null ) {
 74+ unset( $params[$key] );
 75+ }
 76+ }
 77+
 78+ $repost = ContributionTrackingProcessor::getRepostFields( $params );
 79+
 80+ $wgOut->addWikiText( "{{2009/Donate-banner/$language}}" );
 81+ $wgOut->addHTML( $this->msgWiki( 'contrib-tracking-submitting' ) );
 82+
 83+ // Output the repost form
 84+ $output = '<form method="post" name="contributiontracking" action="' . $repost['action'] . '">';
 85+
 86+ foreach ( $repost['fields'] as $key => $value ) {
 87+ $output .= '<input type="hidden" name="' . htmlspecialchars( $key ) . '" value="' . htmlspecialchars( $value ) . '" />';
 88+ }
 89+
 90+ $output .= $this->msgWiki( 'contrib-tracking-redirect' );
 91+
 92+ // Offer a button to post the form if the user has no Javascript support
 93+ $output .= '<noscript>';
 94+ $output .= $this->msgWiki( 'contrib-tracking-continue' );
 95+ $output .= '<input type="submit" value="' . $this->msg( 'contrib-tracking-button' ) . '" />';
 96+ $output .= '</noscript>';
 97+
 98+ $output .= '</form>';
 99+
 100+ $wgOut->addHTML( $output );
 101+
 102+ // Automatically post the form if the user has Javascript support
 103+ $wgOut->addHTML( '<script type="text/javascript">document.contributiontracking.submit();</script>' );
 104+ }
 105+
 106+ function msg() {
 107+ return wfMsgExt( func_get_arg( 0 ), array( 'escape', 'language' => $this->lang ) );
 108+ }
 109+
 110+ function msgWiki( $key ) {
 111+ return wfMsgExt( $key, array( 'parse', 'language' => $this->lang ) );
 112+ }
 113+
 114+}
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking_body.php
___________________________________________________________________
Added: svn:mergeinfo
1115 Merged /trunk/extensions/ContributionReporting/ContributionTracking_body.php:r74440-74491
2116 Merged /branches/wmf-deployment/extensions/ContributionTracking/ContributionTracking_body.php:r60970
3117 Merged /branches/wmf/1.16wmf4/extensions/ContributionTracking/ContributionTracking_body.php:r67177,69199,76243,77266
4118 Merged /trunk/extensions/ContributionTracking/ContributionTracking_body.php:r64690-96037
5119 Merged /trunk/phase3/extensions/ContributionTracking/ContributionTracking_body.php:r63545-63546,63549,63643,63764,63897-63901,64113,64509,65387,65391,65555,65590,65650,65816,77555,77558-77560,77563-77565,77573
Added: svn:eol-style
6120 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.i18n.php
@@ -0,0 +1,1329 @@
 2+<?php
 3+/**
 4+ * Internationalisation for ContributionTracking extension
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$messages = array();
 11+
 12+/** English
 13+ * @author David Strauss
 14+ */
 15+$messages['en'] = array(
 16+ 'contributiontracking-desc' => 'Contribution tracking for the Wikimedia fundraiser',
 17+ 'contributiontracking' => 'Contribution tracking',
 18+
 19+ 'contrib-tracking-error' => 'Error',
 20+ 'contrib-tracking-error-text' => 'Invalid form submission',
 21+
 22+ 'contrib-tracking-submitting' => 'Submitting to payment processor...',
 23+ 'contrib-tracking-continue' => 'If you are not automatically redirected, click the button to complete your donation at PayPal.',
 24+ 'contrib-tracking-redirect' => 'You will be automatically redirected to PayPal to complete your donation.',
 25+
 26+ 'contrib-tracking-button' => 'Continue',
 27+
 28+ 'contrib-tracking-item-name-onetime' => 'One-time donation',
 29+ 'contrib-tracking-item-name-recurring' => 'Recurring monthly donation',
 30+
 31+);
 32+
 33+/** Message documentation (Message documentation)
 34+ * @author Bennylin
 35+ * @author Darth Kule
 36+ * @author Umherirrender
 37+ */
 38+$messages['qqq'] = array(
 39+ 'contributiontracking-desc' => '{{desc}}',
 40+ 'contrib-tracking-error' => '{{Identical|Error}}',
 41+ 'contrib-tracking-button' => '{{Identical|Continue}}',
 42+);
 43+
 44+/** Afrikaans (Afrikaans)
 45+ * @author Naudefj
 46+ */
 47+$messages['af'] = array(
 48+ 'contrib-tracking-error' => 'Fout',
 49+ 'contrib-tracking-button' => 'Gaan voort',
 50+);
 51+
 52+/** Aragonese (Aragonés)
 53+ * @author Juanpabl
 54+ */
 55+$messages['an'] = array(
 56+ 'contributiontracking-desc' => "Seguimiento de contrebucions d'a replegad de fondos de Wikimedia",
 57+ 'contributiontracking' => 'Seguimiento de contrebucions',
 58+ 'contrib-tracking-error' => 'Error',
 59+ 'contrib-tracking-error-text' => 'Error en o formulario',
 60+ 'contrib-tracking-submitting' => 'Ninviando ta o procesador de pagos...',
 61+ 'contrib-tracking-continue' => 'Si ye endrezato automaticament, faiga click en o botón ta rematar a suya donación en PayPal.',
 62+ 'contrib-tracking-button' => 'Continar',
 63+);
 64+
 65+/** Arabic (العربية)
 66+ * @author Meno25
 67+ * @author OsamaK
 68+ */
 69+$messages['ar'] = array(
 70+ 'contributiontracking-desc' => 'تتبع المساهمة لجامع تبرعات ويكيميديا',
 71+ 'contributiontracking' => 'تتبع المساهمة',
 72+ 'contrib-tracking-error' => 'خطأ',
 73+ 'contrib-tracking-error-text' => 'نموذج تسليم غير صحيح',
 74+ 'contrib-tracking-submitting' => 'تسليم إلى معالج الدفع...',
 75+ 'contrib-tracking-continue' => 'إذا لم تحول تلقائيا، انقر على الزر لإكمال التبرع على باي بال.',
 76+ 'contrib-tracking-button' => 'استمر',
 77+);
 78+
 79+/** Aramaic (ܐܪܡܝܐ)
 80+ * @author Basharh
 81+ */
 82+$messages['arc'] = array(
 83+ 'contrib-tracking-error' => 'ܦܘܕܐ',
 84+);
 85+
 86+/** Egyptian Spoken Arabic (مصرى)
 87+ * @author Ghaly
 88+ * @author Meno25
 89+ * @author Ramsis II
 90+ */
 91+$messages['arz'] = array(
 92+ 'contributiontracking-desc' => 'تتبع المساهمة لجامع تبرعات ويكيميديا',
 93+ 'contributiontracking' => 'تتبع المساهة',
 94+ 'contrib-tracking-error' => 'غلط',
 95+ 'contrib-tracking-error-text' => 'استمارة التقديم مش صحيحة',
 96+ 'contrib-tracking-submitting' => 'تقديم لمعالج الدفع ...',
 97+ 'contrib-tracking-continue' => 'لو ما اتحولتش اوتوماتيكى، دوس على الزرار علشان تكمل التبرع بتاعك على الباى بال',
 98+ 'contrib-tracking-button' => 'استمر',
 99+);
 100+
 101+/** Azerbaijani (Azərbaycanca)
 102+ * @author Cekli829
 103+ */
 104+$messages['az'] = array(
 105+ 'contrib-tracking-error' => 'Xəta',
 106+);
 107+
 108+/** Bashkir (Башҡортса)
 109+ * @author Assele
 110+ */
 111+$messages['ba'] = array(
 112+ 'contributiontracking-desc' => 'Викимедиа файҙаһына иғәнә йыйыуҙы күҙәтеү',
 113+ 'contributiontracking' => 'Иғәнә йыйыуҙы күҙәтеү',
 114+ 'contrib-tracking-error' => 'Хата',
 115+ 'contrib-tracking-error-text' => 'Форманы ебәреү хатаһы',
 116+ 'contrib-tracking-submitting' => 'Түләүҙәрҙе эшкәртеү ҡоралына ебәреү...',
 117+ 'contrib-tracking-continue' => 'Әгәр һеҙ автоматик рәүештә йүнәлтелгән булмаһағыҙ, PayPal сайтында иғәнә биреүҙе тамамлау өсөн, төймәгә баҫығыҙ.',
 118+ 'contrib-tracking-redirect' => 'Һеҙ иғәнә биреүҙе тамамлау өсөн автоматик рәүештә PayPal сайтына йүнәлтеләсәкһегеҙ.',
 119+ 'contrib-tracking-button' => 'Дауам итергә',
 120+ 'contrib-tracking-item-name-onetime' => 'Бер тапҡыр булған иғәнә',
 121+ 'contrib-tracking-item-name-recurring' => 'Ай һайын ҡабатланған иғәнә',
 122+);
 123+
 124+/** Belarusian (Taraškievica orthography) (‪Беларуская (тарашкевіца)‬)
 125+ * @author EugeneZelenko
 126+ * @author Jim-by
 127+ * @author Red Winged Duck
 128+ * @author Wizardist
 129+ */
 130+$messages['be-tarask'] = array(
 131+ 'contributiontracking-desc' => 'Сачэньне за зборам ахвяраваньняў на карысьць фундацыі «Вікімэдыя»',
 132+ 'contributiontracking' => 'Сачэньне за зборам ахвяраваньняў',
 133+ 'contrib-tracking-error' => 'Памылка',
 134+ 'contrib-tracking-error-text' => 'Няслушная перадача зьвестак з формы',
 135+ 'contrib-tracking-submitting' => 'Адпраўка апрацоўшчыку плацяжоў...',
 136+ 'contrib-tracking-continue' => 'Калі Вы не былі аўтаматычна перанакіраваны, націсьніце кнопку, каб завяршыць Ваша ахвяраваньне праз сыстэму PayPal.',
 137+ 'contrib-tracking-redirect' => 'Вы будзеце аўтаматычна перанакіраваныя ў PayPal для сканчэньня працэдуры.',
 138+ 'contrib-tracking-button' => 'Працягваць',
 139+ 'contrib-tracking-item-name-onetime' => 'Аднаразовае ахвяраваньне',
 140+ 'contrib-tracking-item-name-recurring' => 'Паўтараючаяся штомесячнае ахвяраваньне',
 141+);
 142+
 143+/** Bulgarian (Български)
 144+ * @author DCLXVI
 145+ * @author Spiritia
 146+ * @author Turin
 147+ */
 148+$messages['bg'] = array(
 149+ 'contributiontracking-desc' => 'Проследяване на даренията в дарителската акция на Уикимедия',
 150+ 'contributiontracking' => 'Проследяване на дарението',
 151+ 'contrib-tracking-error' => 'Грешка',
 152+ 'contrib-tracking-error-text' => 'Неправилно изпратена форма',
 153+ 'contrib-tracking-submitting' => 'Изпращане за обработка на плащането...',
 154+ 'contrib-tracking-continue' => 'Ако не бъдете пренасочени автоматично, натиснете бутона, за да завършите дарението през PayPal.',
 155+ 'contrib-tracking-button' => 'Продължаване',
 156+ 'contrib-tracking-item-name-onetime' => 'Еднократно дарение',
 157+);
 158+
 159+/** Bengali (বাংলা)
 160+ * @author Bellayet
 161+ * @author Wikitanvir
 162+ */
 163+$messages['bn'] = array(
 164+ 'contributiontracking' => 'অনুদান অনুসরণ',
 165+ 'contrib-tracking-error' => 'ত্রুটি',
 166+ 'contrib-tracking-error-text' => 'অগ্রহণযোগ্য ফর্ম জমা',
 167+ 'contrib-tracking-submitting' => 'পেমেন্ট প্রসেসরের কাছে প্রদান করা হচ্ছে...',
 168+ 'contrib-tracking-continue' => 'আপনাকে যদি স্বয়ংক্রিয়ভাবে পুনর্নির্দেশিত করা না হয়, তবে পেপ্যাল-এ আপনার অনুদান সম্পন্ন করতে নিচের বাটনে ক্লিক করুন।',
 169+ 'contrib-tracking-redirect' => 'অনুদান সম্পূর্ণ করতে আপনাকে স্বয়ংক্রিয়ভাবে পেপ্যালে পুনর্নির্দেশ করা হবে।',
 170+ 'contrib-tracking-button' => 'অগ্রসর হোন',
 171+ 'contrib-tracking-item-name-onetime' => 'একক-সময় অনুদান',
 172+);
 173+
 174+/** Breton (Brezhoneg)
 175+ * @author Fulup
 176+ */
 177+$messages['br'] = array(
 178+ 'contributiontracking-desc' => "Heuliañ an donezonoù evit dastumadeg arc'hant Wikimedia",
 179+ 'contributiontracking' => 'Heuliañ an donezonoù',
 180+ 'contrib-tracking-error' => 'Fazi',
 181+ 'contrib-tracking-error-text' => 'Furmskrid kaset direizh',
 182+ 'contrib-tracking-submitting' => "O kas d'ar reizhiad tretiñ ar paeamantoù...",
 183+ 'contrib-tracking-continue' => 'Ma ne vezit ket adkaset ent emgefre, klikit war ar bouton-mañ evit kas da benn ho paeamant gant Paypal.',
 184+ 'contrib-tracking-redirect' => 'Adkaset e viot ent emgefre war-zu PayPal evit seveniñ an donezon.',
 185+ 'contrib-tracking-button' => "Kenderc'hel",
 186+ 'contrib-tracking-item-name-onetime' => 'Donezon nemetañ',
 187+ 'contrib-tracking-item-name-recurring' => 'Donezon miziek ingal',
 188+);
 189+
 190+/** Bosnian (Bosanski)
 191+ * @author CERminator
 192+ */
 193+$messages['bs'] = array(
 194+ 'contributiontracking-desc' => 'Praćenje doprinosa za Wikimedia donacije',
 195+ 'contributiontracking' => 'Praćenje doprinosa',
 196+ 'contrib-tracking-error' => 'Greška',
 197+ 'contrib-tracking-error-text' => 'Slanje nevaljanog obrasca',
 198+ 'contrib-tracking-submitting' => 'Šaljem za proces plaćanja...',
 199+ 'contrib-tracking-continue' => 'Ako niste automatski preusmjereni, kliknite na dugme da završite Vašu donaciju putem PayPala.',
 200+ 'contrib-tracking-redirect' => 'Bit ćete automatski preusmjereni na PayPal da dovršite vašu donaciju.',
 201+ 'contrib-tracking-button' => 'nastavi',
 202+ 'contrib-tracking-item-name-onetime' => 'Jednokratna donacija',
 203+ 'contrib-tracking-item-name-recurring' => 'Mjesečna ponavljajuća donacija',
 204+);
 205+
 206+/** Catalan (Català)
 207+ * @author Davidpar
 208+ * @author Paucabot
 209+ * @author Solde
 210+ */
 211+$messages['ca'] = array(
 212+ 'contributiontracking-desc' => 'Contribució de seguiment per a la recaptació de fons de Wikimedia',
 213+ 'contributiontracking' => 'Rastreig de contribucions',
 214+ 'contrib-tracking-error' => 'Error',
 215+ 'contrib-tracking-error-text' => 'Enviament de formulari no vàlid',
 216+ 'contrib-tracking-submitting' => 'Enviant al processador de pagaments...',
 217+ 'contrib-tracking-continue' => 'Si no sou automàticament redirigit, clicau el botó per completar la vostra donació a PayPal.',
 218+ 'contrib-tracking-button' => 'Continua',
 219+);
 220+
 221+/** Czech (Česky)
 222+ * @author Mormegil
 223+ */
 224+$messages['cs'] = array(
 225+ 'contributiontracking-desc' => 'Sledování finančních příspěvků v průběhu kampaně nadace Wikimedia',
 226+ 'contributiontracking' => 'Sledování příspěvků',
 227+ 'contrib-tracking-error' => 'Chyba',
 228+ 'contrib-tracking-error-text' => 'Odeslaný formulář nebyl správně vyplněn',
 229+ 'contrib-tracking-submitting' => 'Platba se odesílá ke zpracování…',
 230+ 'contrib-tracking-continue' => 'Pokud nebudete automaticky přesměrován(a), klikněte na tlačítko, abyste mohl(a) dokončit svůj příspěvek pomocí služby PayPal.',
 231+ 'contrib-tracking-redirect' => 'Pro dokončení příspěvku budete automaticky přesměrování na PayPal.',
 232+ 'contrib-tracking-button' => 'Pokračovat',
 233+ 'contrib-tracking-item-name-onetime' => 'Jednorázový dar',
 234+ 'contrib-tracking-item-name-recurring' => 'Pravidelný měsíční příspěvek',
 235+);
 236+
 237+/** German (Deutsch)
 238+ * @author Imre
 239+ * @author Kghbln
 240+ * @author The Evil IP address
 241+ */
 242+$messages['de'] = array(
 243+ 'contributiontracking-desc' => 'Ermöglicht die Spendennachverfolgung für die Wikimedia-Spendenkampagne',
 244+ 'contributiontracking' => 'Spendennachverfolgung',
 245+ 'contrib-tracking-error' => 'Fehler',
 246+ 'contrib-tracking-error-text' => 'Ungültige Übertragung des Formulars',
 247+ 'contrib-tracking-submitting' => 'Übertragung an den Zahlungsdienstleister …',
 248+ 'contrib-tracking-continue' => 'Sofern du nicht automatisch weitergeleitet wirst, klicke bitte auf „{{int:contrib-tracking-button}}“, um deine Spende über PayPal abzuschließen.',
 249+ 'contrib-tracking-redirect' => 'Du wirst automatisch zu PayPal weitergeleitet, um deine Spende abzuschließen zu können.',
 250+ 'contrib-tracking-button' => 'Weiter',
 251+ 'contrib-tracking-item-name-onetime' => 'Einmalige Spende',
 252+ 'contrib-tracking-item-name-recurring' => 'Monatlich wiederholende Spende',
 253+);
 254+
 255+/** German (formal address) (‪Deutsch (Sie-Form)‬)
 256+ * @author Imre
 257+ * @author Kghbln
 258+ */
 259+$messages['de-formal'] = array(
 260+ 'contrib-tracking-continue' => 'Sofern Sie nicht automatisch weitergeleitet werden, klicken Sie bitte auf „{{int:contrib-tracking-button}}“, um Ihre Spende über PayPal abzuschließen.',
 261+ 'contrib-tracking-redirect' => 'Sie werden automatisch zu PayPal weitergeleitet, um Ihre Spende abzuschließen zu können.',
 262+);
 263+
 264+/** Zazaki (Zazaki)
 265+ * @author Xoser
 266+ */
 267+$messages['diq'] = array(
 268+ 'contributiontracking-desc' => 'Yardimê peteyî tekip kerdoğ qe Wîkîmediya pere berz kerdoğî',
 269+ 'contributiontracking' => 'Yardimê peteyî tekip kerdoğ',
 270+ 'contrib-tracking-error' => 'Ğelet',
 271+ 'contrib-tracking-error-text' => 'Ena form raşt niyo',
 272+ 'contrib-tracking-submitting' => 'Processor de pere teslim keno...',
 273+ 'contrib-tracking-continue' => 'Eka ti otomatik redirekt nibiyo, goceki rê bitexne u yardîmê pere xo ser PayPalî ra biker.',
 274+ 'contrib-tracking-button' => 'Dewam bike',
 275+);
 276+
 277+/** Lower Sorbian (Dolnoserbski)
 278+ * @author Michawiki
 279+ */
 280+$messages['dsb'] = array(
 281+ 'contributiontracking-desc' => 'Slědowanje darow za pósćiwańsku kampanju Wikimedije',
 282+ 'contributiontracking' => 'Slědowanje darow',
 283+ 'contrib-tracking-error' => 'Zmólka',
 284+ 'contrib-tracking-error-text' => 'Njepłaśiwe słanje formulara',
 285+ 'contrib-tracking-submitting' => 'Płaśenje wótpósćeła se k wobźěłowanjeju...',
 286+ 'contrib-tracking-continue' => 'Joli njepósrědnjaš se awtomatiski dalej, klikni na tłocašk, aby dokóńcył swój dar pśez PayPal.',
 287+ 'contrib-tracking-redirect' => 'Buźošo so awtomatiski k PayPaloju dalej pósrědnjaś, aby wy swójo pósćiwanje skóńcył.',
 288+ 'contrib-tracking-button' => 'Dalej',
 289+ 'contrib-tracking-item-name-onetime' => 'Jadnorazny dar',
 290+ 'contrib-tracking-item-name-recurring' => 'Wóspjetujucy se mjasecny dar',
 291+);
 292+
 293+/** Ewe (Eʋegbe) */
 294+$messages['ee'] = array(
 295+ 'contrib-tracking-error' => 'Vodada',
 296+);
 297+
 298+/** Greek (Ελληνικά)
 299+ * @author Konsnos
 300+ * @author Omnipaedista
 301+ */
 302+$messages['el'] = array(
 303+ 'contributiontracking-desc' => 'Παρακολούθηση συνεισφορών για τον έρανο του Wikimedia',
 304+ 'contributiontracking' => 'Παακολούθηση συνεισφορών',
 305+ 'contrib-tracking-error' => 'Σφάλμα',
 306+ 'contrib-tracking-error-text' => 'Υποβολή άκυρου τύπου',
 307+ 'contrib-tracking-submitting' => 'Υποβολή στον επεξεργαστή πληρωμών...',
 308+ 'contrib-tracking-continue' => 'Εάν δεν προωθηθήκατε αυτόματα, πατήστε το κουμπί για να ολοκληρώσετε τη δωρεάν σας μέσω PayPal.',
 309+ 'contrib-tracking-button' => 'Συνέχεια',
 310+);
 311+
 312+/** Esperanto (Esperanto)
 313+ * @author Yekrats
 314+ */
 315+$messages['eo'] = array(
 316+ 'contributiontracking-desc' => 'Kontribuada atentilo por la monkampajno Wikimedia',
 317+ 'contributiontracking' => 'Kontribua kontrolado',
 318+ 'contrib-tracking-error' => 'Eraro',
 319+ 'contrib-tracking-error-text' => 'Malvalida enigo',
 320+ 'contrib-tracking-submitting' => 'Sendante al mona traktejo...',
 321+ 'contrib-tracking-continue' => 'Se vi ne aŭtomate estus alidirektita, klaku la butonon por fini vian donacon ĉe PayPal.',
 322+ 'contrib-tracking-redirect' => 'Vi estos aŭtomatike alidirektita al PayPal por kompletigi vian donacon.',
 323+ 'contrib-tracking-button' => 'Daŭrigi',
 324+ 'contrib-tracking-item-name-onetime' => 'Unuopa donaco',
 325+ 'contrib-tracking-item-name-recurring' => 'Ĉiumonata donaco',
 326+);
 327+
 328+/** Spanish (Español)
 329+ * @author Crazymadlover
 330+ * @author Remember the dot
 331+ */
 332+$messages['es'] = array(
 333+ 'contributiontracking-desc' => 'Contribución para el seguimiento del recaudador de fondos de Wikimedia',
 334+ 'contributiontracking' => 'Seguimiento contribucional',
 335+ 'contrib-tracking-error' => 'Error',
 336+ 'contrib-tracking-error-text' => 'Envío de formulario inválido',
 337+ 'contrib-tracking-submitting' => 'Entregando al procesador de pagos...',
 338+ 'contrib-tracking-continue' => 'Si usted no es automáticamente redirigido, haga click en el botón para completar su donación en Paypal.',
 339+ 'contrib-tracking-button' => 'Continuar',
 340+);
 341+
 342+/** Estonian (Eesti)
 343+ * @author Avjoska
 344+ * @author Pikne
 345+ */
 346+$messages['et'] = array(
 347+ 'contributiontracking-desc' => 'Jälgib Wikimedia korjanduse laekumisi.',
 348+ 'contrib-tracking-error' => 'Viga',
 349+ 'contrib-tracking-submitting' => 'Maksevahendaja juurde suunamine...',
 350+ 'contrib-tracking-continue' => 'Kui sind ei suunata automaatselt, klõpsa nuppu, et annetus PayPalis lõpule viia.',
 351+ 'contrib-tracking-redirect' => 'Annetuse lõpule viimiseks suunatakse sind automaatselt PayPali.',
 352+ 'contrib-tracking-button' => 'Jätka',
 353+);
 354+
 355+/** Basque (Euskara)
 356+ * @author An13sa
 357+ */
 358+$messages['eu'] = array(
 359+ 'contrib-tracking-error' => 'Errorea',
 360+ 'contrib-tracking-button' => 'Jarraitu',
 361+);
 362+
 363+/** Persian (فارسی)
 364+ * @author Huji
 365+ * @author Komeil 4life
 366+ */
 367+$messages['fa'] = array(
 368+ 'contributiontracking-desc' => 'پی‌گیری کمک‌های انجام شده به جذب سرمایهٔ ویکی‌مدیا',
 369+ 'contributiontracking' => 'پی‌گیری کمک‌ها',
 370+ 'contrib-tracking-error' => 'خطا',
 371+ 'contrib-tracking-error-text' => 'اطلاعات ارسالی فرم غیر مجاز است',
 372+ 'contrib-tracking-submitting' => 'ارسال به پردازش کنندهٔ پرداخت...',
 373+ 'contrib-tracking-continue' => 'اگر به طور خودکار به صفحهٔ مورد نظر هدایت نشدید، برای تکمیل کمک مالی خود در PayPal بر روی دکمه کلیک کنید.',
 374+ 'contrib-tracking-button' => 'ادامه',
 375+);
 376+
 377+/** Finnish (Suomi)
 378+ * @author Crt
 379+ * @author Nike
 380+ * @author Tarmo
 381+ */
 382+$messages['fi'] = array(
 383+ 'contributiontracking-desc' => 'Lahjoitusseuranta Wikimedian rahankeräykseen.',
 384+ 'contributiontracking' => 'Lahjoitusseuranta',
 385+ 'contrib-tracking-error' => 'Virhe',
 386+ 'contrib-tracking-error-text' => 'Lähetetty lomake oli viallinen',
 387+ 'contrib-tracking-submitting' => 'Lähetetään maksujen käsittelyyn...',
 388+ 'contrib-tracking-continue' => 'Jos sinua ei ohjata eteenpäin automaattisesti, viimeistele lahjoituksesi PayPalissa napsauttamalla.',
 389+ 'contrib-tracking-button' => 'Jatka',
 390+);
 391+
 392+/** French (Français)
 393+ * @author Korrigan
 394+ * @author Peter17
 395+ * @author PieRRoMaN
 396+ * @author Verdy p
 397+ */
 398+$messages['fr'] = array(
 399+ 'contributiontracking-desc' => 'Suivi des contributions pour la levée de fonds de Wikimedia',
 400+ 'contributiontracking' => 'Suivi des contributions',
 401+ 'contrib-tracking-error' => 'Erreur',
 402+ 'contrib-tracking-error-text' => 'Soumission du formulaire invalide',
 403+ 'contrib-tracking-submitting' => 'Envoi au système de traitement des paiements...',
 404+ 'contrib-tracking-continue' => 'Si vous n’êtes pas redirigé automatiquement, cliquez ce bouton pour compléter votre don avec Paypal.',
 405+ 'contrib-tracking-redirect' => 'Vous allez être redirigé automatiquement vers PayPal pour terminer votre don.',
 406+ 'contrib-tracking-button' => 'Continuer',
 407+ 'contrib-tracking-item-name-onetime' => 'Don unique',
 408+ 'contrib-tracking-item-name-recurring' => 'Don mensuel récurrent',
 409+);
 410+
 411+/** Franco-Provençal (Arpetan)
 412+ * @author ChrisPtDe
 413+ */
 414+$messages['frp'] = array(
 415+ 'contributiontracking-desc' => 'Survelyence de les contribucions por la levâ de fonds de Wikimedia.',
 416+ 'contributiontracking' => 'Survelyence de les contribucions',
 417+ 'contrib-tracking-error' => 'Èrror',
 418+ 'contrib-tracking-error-text' => 'Somission du formulèro envalida',
 419+ 'contrib-tracking-submitting' => 'Èxpèdicion u sistèmo de trètament des payements...',
 420+ 'contrib-tracking-continue' => 'Se vos éte pas redirigiê ôtomaticament, clicâd cél boton por complètar voutron don avouéc PayPal.',
 421+ 'contrib-tracking-button' => 'Continuar',
 422+ 'contrib-tracking-item-name-onetime' => 'Don solèt',
 423+);
 424+
 425+/** Friulian (Furlan)
 426+ * @author Klenje
 427+ */
 428+$messages['fur'] = array(
 429+ 'contrib-tracking-button' => 'Va indevant',
 430+);
 431+
 432+/** Galician (Galego)
 433+ * @author Toliño
 434+ */
 435+$messages['gl'] = array(
 436+ 'contributiontracking-desc' => 'Seguimento das contribucións da recadación de fondos da Wikimedia',
 437+ 'contributiontracking' => 'Seguimento das contribucións',
 438+ 'contrib-tracking-error' => 'Erro',
 439+ 'contrib-tracking-error-text' => 'Envío do formulario inválido',
 440+ 'contrib-tracking-submitting' => 'Enviando ao procesador de pagamentos...',
 441+ 'contrib-tracking-continue' => 'Se non es redirixido automaticamente, fai clic no botón para completar a túa doazón por PayPal.',
 442+ 'contrib-tracking-redirect' => 'Serás redirixido automaticamente ao PayPal para completar a túa doazón.',
 443+ 'contrib-tracking-button' => 'Continuar',
 444+ 'contrib-tracking-item-name-onetime' => 'Doazón única',
 445+ 'contrib-tracking-item-name-recurring' => 'Doazón mensual recorrente',
 446+);
 447+
 448+/** Ancient Greek (Ἀρχαία ἑλληνικὴ)
 449+ * @author Crazymadlover
 450+ * @author Omnipaedista
 451+ */
 452+$messages['grc'] = array(
 453+ 'contrib-tracking-error' => 'Σφάλμα',
 454+ 'contrib-tracking-button' => 'Ἀκολουθεῖν',
 455+);
 456+
 457+/** Swiss German (Alemannisch)
 458+ * @author Als-Holder
 459+ */
 460+$messages['gsw'] = array(
 461+ 'contributiontracking-desc' => 'Spändeverfolgig fir d Wikimedia Spändekampagne',
 462+ 'contributiontracking' => 'Spändeverfolgig',
 463+ 'contrib-tracking-error' => 'Fähler',
 464+ 'contrib-tracking-error-text' => 'Nit giltig Ibertragigsformular',
 465+ 'contrib-tracking-submitting' => 'Ibertragig an dr Zahligsprovider ...',
 466+ 'contrib-tracking-continue' => 'Wänn Si nit automatisch wytergleitet wäre, no drucke Si bitte uf „Wyter“ go Ihri Spände iber PayPal abzschließe.',
 467+ 'contrib-tracking-redirect' => 'Du wirsch automatisch zue PayPal wytergleitet go Dyy Spände vervollständige.',
 468+ 'contrib-tracking-button' => 'Wyter',
 469+ 'contrib-tracking-item-name-onetime' => 'Eimoligi Spänd',
 470+ 'contrib-tracking-item-name-recurring' => 'Spänd, wu all Monet soll widerholt wäre',
 471+);
 472+
 473+/** Gujarati (ગુજરાતી)
 474+ * @author Dineshjk
 475+ */
 476+$messages['gu'] = array(
 477+ 'contrib-tracking-error' => 'ત્રુટિ',
 478+);
 479+
 480+/** Hebrew (עברית)
 481+ * @author Amire80
 482+ * @author Rotem Liss
 483+ * @author YaronSh
 484+ */
 485+$messages['he'] = array(
 486+ 'contributiontracking-desc' => 'מעקב תרומות להתרמה לקרן ויקימדיה',
 487+ 'contributiontracking' => 'מעקב תרומות',
 488+ 'contrib-tracking-error' => 'שגיאה',
 489+ 'contrib-tracking-error-text' => 'שליחת פורום בלתי תקינה',
 490+ 'contrib-tracking-submitting' => 'נשלח למעבד התרומות...',
 491+ 'contrib-tracking-continue' => 'אם ההפניה אינה מתבצעת אוטומטית, לחצו על הכפתור כדי להשלים את תרומתכם באתר PayPal.',
 492+ 'contrib-tracking-redirect' => 'כעת תתבצע העברה אוטומטית ל־PayPal כדי להשלים את תרומתך.',
 493+ 'contrib-tracking-button' => 'המשך',
 494+ 'contrib-tracking-item-name-onetime' => 'תרומה חד־פעמית',
 495+ 'contrib-tracking-item-name-recurring' => 'תרומה חודשית חוזרת',
 496+);
 497+
 498+/** Croatian (Hrvatski)
 499+ * @author Dalibor Bosits
 500+ * @author SpeedyGonsales
 501+ */
 502+$messages['hr'] = array(
 503+ 'contributiontracking-desc' => 'Praćenje doprinosa za Wikimedijino prikupljanje sredstava',
 504+ 'contributiontracking' => 'Praćenje doprinosa',
 505+ 'contrib-tracking-error' => 'Greška',
 506+ 'contrib-tracking-error-text' => 'Neispravno podnošenje obrasca',
 507+ 'contrib-tracking-submitting' => 'Podnošenje obrascu za plaćanje...',
 508+ 'contrib-tracking-continue' => 'Ako niste automatski preusmjereni, kliknite na tipku kako biste dovršili svoju donaciju s PayPalom.',
 509+ 'contrib-tracking-redirect' => 'Bit ćete automatski preusmjereni na PayPal za dovršetak vaše donacije.',
 510+ 'contrib-tracking-button' => 'Nastavi',
 511+ 'contrib-tracking-item-name-onetime' => 'Jednokratna donacija',
 512+ 'contrib-tracking-item-name-recurring' => 'Mjesečna ponavljajuća donacija',
 513+);
 514+
 515+/** Upper Sorbian (Hornjoserbsce)
 516+ * @author Michawiki
 517+ */
 518+$messages['hsb'] = array(
 519+ 'contributiontracking-desc' => 'Sćěhowanje přinoškow za darjensku kampanju Wikimedije',
 520+ 'contributiontracking' => 'Sćěhowanje přinoškow',
 521+ 'contrib-tracking-error' => 'Zmylk',
 522+ 'contrib-tracking-error-text' => 'Njepłaćiwe formularne přenjesenje',
 523+ 'contrib-tracking-submitting' => 'Přenjesenje k płaćenskemu sposrědkowarjej',
 524+ 'contrib-tracking-continue' => 'Jeli njeposrědkuješ so awtomatisce dale, klikń na tłóčatko, zo by swój dar přez PayPal přewjedł.',
 525+ 'contrib-tracking-redirect' => 'Sposrědkuješ so awtomatisce dale do PayPal, zo by darjenje wotzamknył.',
 526+ 'contrib-tracking-button' => 'Dale',
 527+ 'contrib-tracking-item-name-onetime' => 'Jónkrócny dar',
 528+ 'contrib-tracking-item-name-recurring' => 'Wospjetowacy so měsačny dar',
 529+);
 530+
 531+/** Hungarian (Magyar)
 532+ * @author Dani
 533+ * @author Glanthor Reviol
 534+ */
 535+$messages['hu'] = array(
 536+ 'contributiontracking-desc' => 'Adományok követése a Wikimédia adománygyűjtő kampányában',
 537+ 'contributiontracking' => 'Adakozás nyomonkövetése',
 538+ 'contrib-tracking-error' => 'Hiba',
 539+ 'contrib-tracking-error-text' => 'Az elküldött űrlap érvénytelen',
 540+ 'contrib-tracking-submitting' => 'Küldés a fizetés-feldolgozóhoz…',
 541+ 'contrib-tracking-continue' => 'Ha az oldal nem irányít át automatikusan, kattints a gombra hogy befejezd az adományozást a PayPalen.',
 542+ 'contrib-tracking-button' => 'Folytatás',
 543+);
 544+
 545+/** Interlingua (Interlingua)
 546+ * @author McDutchie
 547+ */
 548+$messages['ia'] = array(
 549+ 'contributiontracking-desc' => 'Sequimento de contributiones pro le collecta de fundos pro Wikimedia',
 550+ 'contributiontracking' => 'Sequimento de contributiones',
 551+ 'contrib-tracking-error' => 'Error',
 552+ 'contrib-tracking-error-text' => 'Submission de formulario invalide',
 553+ 'contrib-tracking-submitting' => 'In submission al processator de pagamentos...',
 554+ 'contrib-tracking-continue' => 'Si tu non es automaticamente redirigite, clicca super le button pro completar tu donation con PayPal.',
 555+ 'contrib-tracking-redirect' => 'Tu essera redirigite automaticamente verso PayPal pro completar tu donation.',
 556+ 'contrib-tracking-button' => 'Continuar',
 557+ 'contrib-tracking-item-name-onetime' => 'Donation unic',
 558+ 'contrib-tracking-item-name-recurring' => 'Donation periodic mensual',
 559+);
 560+
 561+/** Indonesian (Bahasa Indonesia)
 562+ * @author Bennylin
 563+ * @author IvanLanin
 564+ */
 565+$messages['id'] = array(
 566+ 'contributiontracking-desc' => 'Pelacak kontribusi untuk penggalangan dana Wikimedia',
 567+ 'contributiontracking' => 'Pelacakan kontribusi',
 568+ 'contrib-tracking-error' => 'Galat',
 569+ 'contrib-tracking-error-text' => 'Pengiriman formulir tidak sah',
 570+ 'contrib-tracking-submitting' => 'Mengirimkan ke pemroses pembayaran...',
 571+ 'contrib-tracking-continue' => 'Jika halaman ini tidak otomatis dialihkan, klik tombol ini untuk menyelesaikan sumbangan Anda lewat PayPal.',
 572+ 'contrib-tracking-redirect' => 'Anda akan otomatis diarahkan ke PayPal untuk menyelesaikan sumbangan Anda.',
 573+ 'contrib-tracking-button' => 'Lanjutkan',
 574+ 'contrib-tracking-item-name-onetime' => 'Sumbangan satu kali',
 575+ 'contrib-tracking-item-name-recurring' => 'Sumbangan bulanan berulang',
 576+);
 577+
 578+/** Igbo (Igbo) */
 579+$messages['ig'] = array(
 580+ 'contrib-tracking-error' => 'Nsogbú',
 581+);
 582+
 583+/** Ido (Ido)
 584+ * @author Malafaya
 585+ */
 586+$messages['io'] = array(
 587+ 'contrib-tracking-error' => 'Eroro',
 588+);
 589+
 590+/** Italian (Italiano)
 591+ * @author Beta16
 592+ * @author BrokenArrow
 593+ * @author Darth Kule
 594+ * @author Rippitippi
 595+ */
 596+$messages['it'] = array(
 597+ 'contributiontracking-desc' => 'Monitoraggio donazioni per la raccolta fondi Wikimedia',
 598+ 'contributiontracking' => 'Monitoraggio donazioni',
 599+ 'contrib-tracking-error' => 'Errore',
 600+ 'contrib-tracking-error-text' => "Errore nell'invio del modulo",
 601+ 'contrib-tracking-submitting' => 'Invio al gestore del pagamento...',
 602+ 'contrib-tracking-continue' => 'Se il collegamento non avviene automaticamente, fare clic sul pulsante per completare la donazione con PayPal.',
 603+ 'contrib-tracking-redirect' => 'Verrai reindirizzato automaticamente su PayPal per completare la donazione.',
 604+ 'contrib-tracking-button' => 'Continua',
 605+ 'contrib-tracking-item-name-onetime' => 'Donazione singola',
 606+ 'contrib-tracking-item-name-recurring' => 'Donazione ricorrente mensile',
 607+);
 608+
 609+/** Japanese (日本語)
 610+ * @author Fryed-peach
 611+ * @author Mizusumashi
 612+ * @author Ohgi
 613+ * @author 青子守歌
 614+ */
 615+$messages['ja'] = array(
 616+ 'contributiontracking-desc' => 'ウィキメディア資金調達者向け寄付追跡',
 617+ 'contributiontracking' => '寄付追跡',
 618+ 'contrib-tracking-error' => 'エラー',
 619+ 'contrib-tracking-error-text' => '無効なフォームの提出',
 620+ 'contrib-tracking-submitting' => '納付処理係に提出中...',
 621+ 'contrib-tracking-continue' => '自動的に転送されない場合は、ボタンを押して PayPal での寄付を終えてください。',
 622+ 'contrib-tracking-redirect' => '寄付を完了するため、PayPalに自動的に転送されます。',
 623+ 'contrib-tracking-button' => '続行',
 624+ 'contrib-tracking-item-name-onetime' => '1回だけ寄付',
 625+ 'contrib-tracking-item-name-recurring' => '月ごとに定期寄付',
 626+);
 627+
 628+/** Javanese (Basa Jawa)
 629+ * @author Pras
 630+ */
 631+$messages['jv'] = array(
 632+ 'contributiontracking-desc' => 'Panjejakan kontribusi kanggo pangumpul dana Wikimedia',
 633+ 'contributiontracking' => 'Panjejakan kontribusi',
 634+ 'contrib-tracking-error' => 'Kasalahan',
 635+ 'contrib-tracking-error-text' => 'Pangajuan formulir ora sah',
 636+ 'contrib-tracking-submitting' => 'Ngirim menyang prosèsor pambayaran...',
 637+ 'contrib-tracking-continue' => 'Yèn panjenengan ora sacara otomatis dialihaké, klik tombol kanggo ngrampungaké sumbangan panjenengan ing Paypal.',
 638+ 'contrib-tracking-button' => 'Terus',
 639+);
 640+
 641+/** Georgian (ქართული)
 642+ * @author Malafaya
 643+ * @author გიორგიმელა
 644+ */
 645+$messages['ka'] = array(
 646+ 'contributiontracking-desc' => 'ვიკიმედიის ფონდის სასარგებლოდ შეწირულებების კონტროლი',
 647+ 'contributiontracking' => 'შეწირულობების შეგროვების კონტროლი',
 648+ 'contrib-tracking-error' => 'შეცდომა',
 649+ 'contrib-tracking-error-text' => 'არასწორი ფორმის ნებართვა',
 650+ 'contrib-tracking-submitting' => 'მიმდინარეობს გადახდების დამმუშავებლისდამი გაგზავნა...',
 651+ 'contrib-tracking-continue' => 'თუ თქვენ არ იყავით ავტომატურად გადამისამართებული, დააჭირეთ ღილაკს რათა დაასრულოთ თქვენი შემოწირულობა PayPal-ზე.',
 652+ 'contrib-tracking-button' => 'გაგრძელება',
 653+);
 654+
 655+/** Khmer (ភាសាខ្មែរ)
 656+ * @author Thearith
 657+ * @author គីមស៊្រុន
 658+ */
 659+$messages['km'] = array(
 660+ 'contrib-tracking-error' => 'កំហុស',
 661+ 'contrib-tracking-continue' => 'ប្រសិនបើអ្នកមិនត្រូវបានបញ្ជូនបន្តដោយស្វ័យប្រវត្តិទេ សូចចុចប៊ូតុងដើម្បីសំរេចការបរិច្ចាគរបស់អ្នកនៅ PayPal។',
 662+ 'contrib-tracking-redirect' => 'អ្នកនឹងត្រូវបញ្ជូនបន្តដោយស្វ័យប្រវត្តិទៅកាន់ PayPal ដើម្បីសំរេចការបរិច្ចាគរបស់អ្នក។',
 663+ 'contrib-tracking-button' => 'បន្ត',
 664+ 'contrib-tracking-item-name-onetime' => 'ការបរិច្ចាគសំរាប់តែមួយលើក',
 665+ 'contrib-tracking-item-name-recurring' => 'ការបរិច្ចាគប្រចាំខែ',
 666+);
 667+
 668+/** Korean (한국어)
 669+ * @author Ilovesabbath
 670+ * @author Kwj2772
 671+ */
 672+$messages['ko'] = array(
 673+ 'contributiontracking-desc' => '위키미디어 모금을 위한 기부 추적',
 674+ 'contributiontracking' => '기부 추적',
 675+ 'contrib-tracking-error' => '오류',
 676+ 'contrib-tracking-error-text' => '부적절한 양식 제출',
 677+ 'contrib-tracking-submitting' => '결재 처리 요청 중...',
 678+ 'contrib-tracking-continue' => '다음 단계로 자동으로 넘어가지 않는다면, 페이팔에서 기부 완료를 완료하기 위해서 버튼을 클릭하세요.',
 679+ 'contrib-tracking-button' => '계속',
 680+);
 681+
 682+/** Colognian (Ripoarisch)
 683+ * @author Purodha
 684+ */
 685+$messages['ksh'] = array(
 686+ 'contributiontracking-desc' => 'Spende Verfollje för de Wikimedija Shtefftung iere Jeld-Sammel-Kampanje',
 687+ 'contributiontracking' => 'Spende Verfollje',
 688+ 'contrib-tracking-error' => 'Fähler',
 689+ 'contrib-tracking-error-text' => 'Unjöltijje Date övverdraare för dat Fommulaa',
 690+ 'contrib-tracking-submitting' => 'Övverjäve aan dä <i lang="en">Provider</i> för et Bezahle&nbsp;...',
 691+ 'contrib-tracking-continue' => 'Wann De nit automattesch ömjelengk wees, donn op dä Knopp klecke, öm Ding Spend met <i lang="en">PayPal</i> fäädesch ze maache.',
 692+ 'contrib-tracking-redirect' => 'Do küß automattesch pä Ömleidong noh <i lang="en">PayPal</i>, öm Ding Spänd fäädesch ze maache.',
 693+ 'contrib-tracking-button' => 'Wigger',
 694+ 'contrib-tracking-item-name-onetime' => 'Eimohl_Spänd',
 695+ 'contrib-tracking-item-name-recurring' => 'Moonatlesch_Spänd',
 696+);
 697+
 698+/** Cornish (Kernowek)
 699+ * @author Kw-Moon
 700+ */
 701+$messages['kw'] = array(
 702+ 'contrib-tracking-error' => 'Gwall',
 703+);
 704+
 705+/** Luxembourgish (Lëtzebuergesch)
 706+ * @author Robby
 707+ */
 708+$messages['lb'] = array(
 709+ 'contributiontracking-desc' => 'Suivi vun den Donen vun der Wikimedia-Spendenakioun',
 710+ 'contributiontracking' => 'Iwwersiicht vun den Donen',
 711+ 'contrib-tracking-error' => 'Feeler',
 712+ 'contrib-tracking-error-text' => 'Feeler bäi der Iwwermëttlung vum Formulaire',
 713+ 'contrib-tracking-submitting' => 'Weiderschécken op de System fir ze bezuelen ...',
 714+ 'contrib-tracking-continue' => 'Wann Dir net automatesch virugeleed gitt, da clickt w.e.g. op de Knäppchen fir ären Don iwwer PayPal ofzeschléissen.',
 715+ 'contrib-tracking-redirect' => 'Dir gitt automatesch op PayPal virugeleet fir Ären Don ofzeschléissen.',
 716+ 'contrib-tracking-button' => 'Weider',
 717+ 'contrib-tracking-item-name-onetime' => 'Eemolegen Don',
 718+ 'contrib-tracking-item-name-recurring' => 'Don deen all Mount widderholl gëtt',
 719+);
 720+
 721+/** Limburgish (Limburgs)
 722+ * @author Ooswesthoesbes
 723+ */
 724+$messages['li'] = array(
 725+ 'contributiontracking-desc' => 'Vólge ven gifte veur de fónswèrving ven Wikimedia',
 726+ 'contributiontracking' => 'Gifte vólge',
 727+ 'contrib-tracking-error' => 'Fout',
 728+ 'contrib-tracking-error-text' => 'Óngèldige formuleerinveur',
 729+ 'contrib-tracking-submitting' => "Bezig mit 't oetveure venne betaling...",
 730+ 'contrib-tracking-continue' => 'Es se neet automatisch wörs doorgestuurdj, klik den óp de knoep óm dien gif via PayPal aaf te make.',
 731+ 'contrib-tracking-button' => 'Doorgaon',
 732+);
 733+
 734+/** Lithuanian (Lietuvių)
 735+ * @author Matasg
 736+ */
 737+$messages['lt'] = array(
 738+ 'contrib-tracking-error' => 'Klaida',
 739+ 'contrib-tracking-error-text' => 'Neteisingas formos pateikimas',
 740+ 'contrib-tracking-submitting' => 'Pateikiama mokėjimo procesoriui...',
 741+ 'contrib-tracking-continue' => 'Jei nebūsite automatiškai nukreiptas, paspauskite mygtuką, kad galėtumėte užbaigti savo aukojimą PayPal.',
 742+ 'contrib-tracking-button' => 'Tęsti',
 743+);
 744+
 745+/** Latvian (Latviešu)
 746+ * @author Papuass
 747+ */
 748+$messages['lv'] = array(
 749+ 'contrib-tracking-button' => 'Turpināt',
 750+);
 751+
 752+/** Eastern Mari (Олык Марий)
 753+ * @author Сай
 754+ */
 755+$messages['mhr'] = array(
 756+ 'contrib-tracking-error' => 'Йоҥылыш',
 757+);
 758+
 759+/** Macedonian (Македонски)
 760+ * @author Bjankuloski06
 761+ * @author Brest
 762+ */
 763+$messages['mk'] = array(
 764+ 'contributiontracking-desc' => 'Следење на донации во акцијата за собирање на средства на Викимедија',
 765+ 'contributiontracking' => 'Следење на донации',
 766+ 'contrib-tracking-error' => 'Грешка',
 767+ 'contrib-tracking-error-text' => 'Неважечко поднесување на образецот',
 768+ 'contrib-tracking-submitting' => 'Поднесувам на обработувачот на плаќања...',
 769+ 'contrib-tracking-continue' => 'Ако се случи да не бидете пренасочени автоматски, кликнете на копчето за да ја довршите донацијата на PayPal.',
 770+ 'contrib-tracking-redirect' => 'Системот автоматски ќе ве префрли на PayPal за да ја довршите донацијата.',
 771+ 'contrib-tracking-button' => 'Продолжи',
 772+ 'contrib-tracking-item-name-onetime' => 'Еднократна донација',
 773+ 'contrib-tracking-item-name-recurring' => 'Месечна донација',
 774+);
 775+
 776+/** Malayalam (മലയാളം)
 777+ * @author Praveenp
 778+ */
 779+$messages['ml'] = array(
 780+ 'contributiontracking-desc' => 'വിക്കിമീഡിയ ധനശേഖരണത്തിലേയ്ക്കുള്ള സംഭാവനകൾ അനുഗമിക്കൽ',
 781+ 'contributiontracking' => 'സംഭാവനകൾ പിന്തുടരൽ',
 782+ 'contrib-tracking-error' => 'പിഴവ്',
 783+ 'contrib-tracking-error-text' => 'ഫോം സമർപ്പണം അസാധുവായ വിധത്തിലാണ്',
 784+ 'contrib-tracking-submitting' => 'പണമടയ്ക്കാനായി സമർപ്പിക്കുന്നു...',
 785+ 'contrib-tracking-continue' => 'താങ്കൾ സ്വതേ തിരിച്ചുവിടപ്പെട്ടില്ലങ്കിൽ, പേയ്‌‌പാൽ വഴിയുള്ള താങ്കളുടെ സംഭാവന പൂർത്തിയാക്കാനായി ബട്ടൺ ഞെക്കുക.',
 786+ 'contrib-tracking-redirect' => 'താങ്കളുടെ സംഭാവന പ്രക്രിയ പൂർത്തിയാക്കാൻ സ്വയം പേപാലിലോട്ട് തിരിച്ചുവിടുന്നതാണ്.',
 787+ 'contrib-tracking-button' => 'തുടരുക',
 788+ 'contrib-tracking-item-name-onetime' => 'ഒറ്റത്തവണ സംഭാവന',
 789+ 'contrib-tracking-item-name-recurring' => 'ആവർത്തിക്കുന്ന മാസംതോറുമുള്ള സംഭാവന',
 790+);
 791+
 792+/** Malay (Bahasa Melayu)
 793+ * @author Anakmalaysia
 794+ * @author Aviator
 795+ */
 796+$messages['ms'] = array(
 797+ 'contributiontracking-desc' => 'Jejak sumbangan dana Wikimedia',
 798+ 'contributiontracking' => 'Jejak sumbangan',
 799+ 'contrib-tracking-error' => 'Ralat',
 800+ 'contrib-tracking-error-text' => 'Borang yang diserahkan tidak sah',
 801+ 'contrib-tracking-submitting' => 'Borang sedang diserahkan kepada pemproses pembayaran...',
 802+ 'contrib-tracking-continue' => 'Sekiranya anda tidak dialihkan secara automatik, klik di sini untuk menyempurnakan derma anda di PayPal.',
 803+ 'contrib-tracking-redirect' => 'Anda akan dilencongkan ke PayPal secara automatik untuk melengkapkan dermaan anda.',
 804+ 'contrib-tracking-button' => 'Teruskan',
 805+ 'contrib-tracking-item-name-onetime' => 'Dermaan sekali',
 806+ 'contrib-tracking-item-name-recurring' => 'Dermaan berulang bulanan',
 807+);
 808+
 809+/** Erzya (Эрзянь)
 810+ * @author Botuzhaleny-sodamo
 811+ */
 812+$messages['myv'] = array(
 813+ 'contrib-tracking-button' => 'Поладомс',
 814+);
 815+
 816+/** Nahuatl (Nāhuatl)
 817+ * @author Fluence
 818+ */
 819+$messages['nah'] = array(
 820+ 'contrib-tracking-error' => 'Ahcuallōtl',
 821+);
 822+
 823+/** Low German (Plattdüütsch)
 824+ * @author Slomox
 825+ */
 826+$messages['nds'] = array(
 827+ 'contributiontracking-desc' => 'Spennen verfolgen för de Wikimedia-Spennenkampagne',
 828+ 'contributiontracking' => 'Spennen folgen',
 829+ 'contrib-tracking-error' => 'Fehler',
 830+ 'contrib-tracking-error-text' => 'Ungüllig Formularöverdragung',
 831+ 'contrib-tracking-submitting' => 'An’t Överdragen an Betahl-Provider ...',
 832+ 'contrib-tracking-continue' => 'Wenn du nich automaatsch wiederleidt warrst, denn klick op „Wieder“, dat du diene Spenn över PayPal afsluten kannst.',
 833+ 'contrib-tracking-button' => 'Wiedermaken',
 834+);
 835+
 836+/** Dutch (Nederlands)
 837+ * @author Siebrand
 838+ */
 839+$messages['nl'] = array(
 840+ 'contributiontracking-desc' => 'Volgen van donaties voor de fondswerving van Wikimedia',
 841+ 'contributiontracking' => 'Donaties volgen',
 842+ 'contrib-tracking-error' => 'Fout',
 843+ 'contrib-tracking-error-text' => 'Ongeldige formulierinvoer',
 844+ 'contrib-tracking-submitting' => 'Bezig met het uitvoeren van de betaling...',
 845+ 'contrib-tracking-continue' => 'Als u niet automatisch wordt doorgestuurd, klik dan op de knop om uw donatie via PayPal te voltooien.',
 846+ 'contrib-tracking-redirect' => 'U wordt automatisch doorgeleid naar PayPal om de transactie te voltooien.',
 847+ 'contrib-tracking-button' => 'Doorgaan',
 848+ 'contrib-tracking-item-name-onetime' => 'Eenmalige donatie',
 849+ 'contrib-tracking-item-name-recurring' => 'Terugkerende maandelijkse donatie',
 850+);
 851+
 852+/** Norwegian Nynorsk (‪Norsk (nynorsk)‬)
 853+ * @author Finnrind
 854+ * @author Harald Khan
 855+ */
 856+$messages['nn'] = array(
 857+ 'contributiontracking-desc' => 'Bidragssporing for innsamlinga til Wikimedia',
 858+ 'contributiontracking' => 'Bidragssporing',
 859+ 'contrib-tracking-error' => 'Feil',
 860+ 'contrib-tracking-error-text' => 'Ugyldig skjema',
 861+ 'contrib-tracking-submitting' => 'Sender til betalingshandtering...',
 862+ 'contrib-tracking-continue' => 'Om du ikkje blir omdirigert automatisk, klikk på knappen for å gjennomføra donasjonen hjå PayPal.',
 863+ 'contrib-tracking-button' => 'Hald fram',
 864+);
 865+
 866+/** Norwegian (bokmål)‬ (‪Norsk (bokmål)‬)
 867+ * @author Finnrind
 868+ * @author Jon Harald Søby
 869+ * @author Nghtwlkr
 870+ */
 871+$messages['no'] = array(
 872+ 'contributiontracking-desc' => 'Bidragssporing for Wikimedias innsamling',
 873+ 'contributiontracking' => 'Bidragssporing',
 874+ 'contrib-tracking-error' => 'Feil',
 875+ 'contrib-tracking-error-text' => 'Ugyldig skjema',
 876+ 'contrib-tracking-submitting' => 'Sender til betalingshåndtering...',
 877+ 'contrib-tracking-continue' => 'Om du ikke omdirigeres automatisk, klikk på knappen for å gjennomføre donasjonen hos PayPal.',
 878+ 'contrib-tracking-redirect' => 'Du vil automatisk bli omdirigert til PayPal for å fullføre donasjonen din.',
 879+ 'contrib-tracking-button' => 'Fortsett',
 880+ 'contrib-tracking-item-name-onetime' => 'Engangsdonasjon',
 881+ 'contrib-tracking-item-name-recurring' => 'Regelmessige månedlige donasjoner',
 882+);
 883+
 884+/** Occitan (Occitan)
 885+ * @author Cedric31
 886+ */
 887+$messages['oc'] = array(
 888+ 'contributiontracking-desc' => 'Seguit de las contribucions per la levada de fonses de Wikimedia',
 889+ 'contributiontracking' => 'Seguit de las contribucions',
 890+ 'contrib-tracking-error' => 'Error',
 891+ 'contrib-tracking-error-text' => 'Error dins lo formulari',
 892+ 'contrib-tracking-submitting' => 'Mandadís al sistèma de pagament...',
 893+ 'contrib-tracking-continue' => 'Clicatz sul boton per acabar vòstre don amb Paypal.',
 894+ 'contrib-tracking-button' => 'Contunhar',
 895+);
 896+
 897+/** Oriya (ଓଡ଼ିଆ)
 898+ * @author Odisha1
 899+ */
 900+$messages['or'] = array(
 901+ 'contrib-tracking-error' => 'ତ୍ରୁଟି',
 902+ 'contrib-tracking-button' => 'ଚାଲୁରଖ',
 903+);
 904+
 905+/** Deitsch (Deitsch)
 906+ * @author Xqt
 907+ */
 908+$messages['pdc'] = array(
 909+ 'contrib-tracking-error' => 'Mischteek',
 910+ 'contrib-tracking-button' => 'Weiter',
 911+);
 912+
 913+/** Polish (Polski)
 914+ * @author Leinad
 915+ * @author Sp5uhe
 916+ */
 917+$messages['pl'] = array(
 918+ 'contributiontracking-desc' => 'Przekierowanie do systemu obsługi wpłat dla darczyńców Fundacji Wikimedia',
 919+ 'contributiontracking' => 'Przekierowanie do systemu obsługi wpłat',
 920+ 'contrib-tracking-error' => 'Błąd',
 921+ 'contrib-tracking-error-text' => 'Niewłaściwy format danych wejściowych',
 922+ 'contrib-tracking-submitting' => 'Przesyłanie danych do systemu płatności...',
 923+ 'contrib-tracking-continue' => 'Jeśli nie {{GENDER:|zostałeś przeniesiony|zostałaś przeniesiona|przeniesiono Cię}} automatycznie, kliknij przycisk aby dokończyć wpłacanie darowizny za pomocą systemu PayPal.',
 924+ 'contrib-tracking-redirect' => 'Zostaniesz automatycznie przeniesiony do PayPal aby wykonać wpłatę.',
 925+ 'contrib-tracking-button' => 'Kontynuuj',
 926+ 'contrib-tracking-item-name-onetime' => 'Jednorazowa darowizna',
 927+ 'contrib-tracking-item-name-recurring' => 'Powtarzająca się comiesięczna darowizna',
 928+);
 929+
 930+/** Piedmontese (Piemontèis)
 931+ * @author Borichèt
 932+ * @author Dragonòt
 933+ */
 934+$messages['pms'] = array(
 935+ 'contributiontracking-desc' => 'Trassadura dle contribussion për la racòlta fond ëd Wikimedia',
 936+ 'contributiontracking' => 'Trassadura dle contribussion',
 937+ 'contrib-tracking-error' => 'Eror',
 938+ 'contrib-tracking-error-text' => 'Spedission ëd forma nen bon-a',
 939+ 'contrib-tracking-submitting' => 'Spedission al motor ëd pagament ...',
 940+ 'contrib-tracking-continue' => "S'it ses pa rediressionà automaticament, sgnaca ël boton për completé toa donassion a PayPal.",
 941+ 'contrib-tracking-redirect' => 'A sarà anviarà automaticament su PayPal për completé soa donassion.',
 942+ 'contrib-tracking-button' => 'Continua',
 943+ 'contrib-tracking-item-name-onetime' => 'Donassion sìngola',
 944+ 'contrib-tracking-item-name-recurring' => 'Donassion arcorenta mensil',
 945+);
 946+
 947+/** Pashto (پښتو)
 948+ * @author Ahmed-Najib-Biabani-Ibrahimkhel
 949+ */
 950+$messages['ps'] = array(
 951+ 'contrib-tracking-error' => 'تېروتنه',
 952+);
 953+
 954+/** Portuguese (Português)
 955+ * @author Hamilton Abreu
 956+ * @author Malafaya
 957+ * @author Waldir
 958+ */
 959+$messages['pt'] = array(
 960+ 'contributiontracking-desc' => 'Monitorização de donativos para a angariação de fundos da Wikimedia',
 961+ 'contributiontracking' => 'Monitorização de donativos',
 962+ 'contrib-tracking-error' => 'Erro',
 963+ 'contrib-tracking-error-text' => 'Envio de formulário inválido',
 964+ 'contrib-tracking-submitting' => 'A enviar ao processador de pagamentos...',
 965+ 'contrib-tracking-continue' => 'Se não for redireccionado automaticamente, clique no botão para completar o seu donativo no PayPal.',
 966+ 'contrib-tracking-redirect' => 'Será reencaminhado automaticamente para o Paypal para terminar o donativo.',
 967+ 'contrib-tracking-button' => 'Continuar',
 968+ 'contrib-tracking-item-name-onetime' => 'Donativo único',
 969+ 'contrib-tracking-item-name-recurring' => 'Donativo mensal recorrente',
 970+);
 971+
 972+/** Brazilian Portuguese (Português do Brasil)
 973+ * @author Eduardo.mps
 974+ * @author Giro720
 975+ */
 976+$messages['pt-br'] = array(
 977+ 'contributiontracking-desc' => 'Seguimento de doações para a coleta de fundos da Wikimedia',
 978+ 'contributiontracking' => 'Seguimento de doações',
 979+ 'contrib-tracking-error' => 'Erro',
 980+ 'contrib-tracking-error-text' => 'Submissão de formulário inválida',
 981+ 'contrib-tracking-submitting' => 'Submetendo ao processador de pagamentos...',
 982+ 'contrib-tracking-continue' => 'Se você não for redirecionado automaticamente, clique no botão para completar a sua doação no PayPal.',
 983+ 'contrib-tracking-redirect' => 'Será reencaminhado automaticamente para o Paypal para terminar a sua doação.',
 984+ 'contrib-tracking-button' => 'Continuar',
 985+ 'contrib-tracking-item-name-onetime' => 'Doação única',
 986+ 'contrib-tracking-item-name-recurring' => 'Doação mensal recorrente',
 987+);
 988+
 989+/** Romanian (Română)
 990+ * @author Firilacroco
 991+ * @author KlaudiuMihaila
 992+ * @author Stelistcristi
 993+ */
 994+$messages['ro'] = array(
 995+ 'contributiontracking' => 'Urmărirea contribuțiilor',
 996+ 'contrib-tracking-error' => 'Eroare',
 997+ 'contrib-tracking-error-text' => 'Trimiterea formularului este invalidă',
 998+ 'contrib-tracking-submitting' => 'Trimitere la procesorul de plată...',
 999+ 'contrib-tracking-button' => 'Continuați',
 1000+);
 1001+
 1002+/** Tarandíne (Tarandíne)
 1003+ * @author Joetaras
 1004+ */
 1005+$messages['roa-tara'] = array(
 1006+ 'contrib-tracking-error' => 'Errore',
 1007+ 'contrib-tracking-error-text' => 'Module de sottomissione invalide',
 1008+ 'contrib-tracking-button' => 'Condinue',
 1009+);
 1010+
 1011+/** Russian (Русский)
 1012+ * @author DCamer
 1013+ * @author Александр Сигачёв
 1014+ */
 1015+$messages['ru'] = array(
 1016+ 'contributiontracking-desc' => 'Отслеживание сбора пожертвований в пользу Викимедии',
 1017+ 'contributiontracking' => 'Отслеживание сбора пожертвований',
 1018+ 'contrib-tracking-error' => 'Ошибка',
 1019+ 'contrib-tracking-error-text' => 'Неправильное подчинение формы',
 1020+ 'contrib-tracking-submitting' => 'Отправка обработчику платежей…',
 1021+ 'contrib-tracking-continue' => 'Если вы не были автоматически перенаправлены, нажмите на кнопку, чтобы завершить ваше пожертвование на сайте PayPal.',
 1022+ 'contrib-tracking-redirect' => 'Вы будете автоматически перенаправлены на PayPal для завершения вашего пожертвования.',
 1023+ 'contrib-tracking-button' => 'Продолжить',
 1024+ 'contrib-tracking-item-name-onetime' => 'Единовременный взнос',
 1025+ 'contrib-tracking-item-name-recurring' => 'Ежемесячный взнос',
 1026+);
 1027+
 1028+/** Rusyn (Русиньскый)
 1029+ * @author Gazeb
 1030+ */
 1031+$messages['rue'] = array(
 1032+ 'contributiontracking' => 'Слїдованя приспевків',
 1033+ 'contrib-tracking-error' => 'Хыба',
 1034+ 'contrib-tracking-error-text' => 'Одосланый формуларь не быв правилно выповненый',
 1035+ 'contrib-tracking-submitting' => 'Платба ся одосылать про спрацованя ...',
 1036+ 'contrib-tracking-button' => 'Продовжовати',
 1037+);
 1038+
 1039+/** Sakha (Саха тыла)
 1040+ * @author HalanTul
 1041+ */
 1042+$messages['sah'] = array(
 1043+ 'contributiontracking-desc' => 'Викимедиаҕа киирбит үп ханна барбытын көрүү',
 1044+ 'contributiontracking' => 'Үп хантан киирбитин көрүү',
 1045+ 'contrib-tracking-error' => 'Алҕас',
 1046+ 'contrib-tracking-error-text' => 'Форма сыыһа сигэнэр',
 1047+ 'contrib-tracking-submitting' => 'Төлөбүрү таҥастааччыга ыытыы...',
 1048+ 'contrib-tracking-continue' => 'Аптамаатынан наадалаах сиргэ тиийбэтэх буоллаххына, тимэҕи баттаан PayPal тиһилигин көмөтүнэн сиэртибэлээһиҥҥин түмүктээ.',
 1049+ 'contrib-tracking-redirect' => 'Сиэртибэлээһиҥҥин түмүктүүргэ аптамаатынан PayPal саайтыгар утаарыллыаҥ.',
 1050+ 'contrib-tracking-button' => 'Салгыы',
 1051+ 'contrib-tracking-item-name-onetime' => 'Биир төлөбүрүнэн',
 1052+ 'contrib-tracking-item-name-recurring' => 'Ый аайы төлөбүр',
 1053+);
 1054+
 1055+/** Sinhala (සිංහල)
 1056+ * @author බිඟුවා
 1057+ */
 1058+$messages['si'] = array(
 1059+ 'contrib-tracking-error' => 'දෝෂය',
 1060+);
 1061+
 1062+/** Slovak (Slovenčina)
 1063+ * @author Helix84
 1064+ */
 1065+$messages['sk'] = array(
 1066+ 'contributiontracking-desc' => 'Sledovanie príspevkov fundraisingu nadácie Wikimedia',
 1067+ 'contributiontracking' => 'Sledovanie príspevkov',
 1068+ 'contrib-tracking-error' => 'Chyba',
 1069+ 'contrib-tracking-error-text' => 'Zaslaný neplatný obsah formulára',
 1070+ 'contrib-tracking-submitting' => 'Odosiela sa na spracovanie platieb...',
 1071+ 'contrib-tracking-continue' => 'Ak nebudete automaticky presmerovaný, kliknite na tlačidlo, čím dokončíte váš príspevok prostredníctvom PayPal.',
 1072+ 'contrib-tracking-redirect' => 'Budete automaticky presmerovaný na PayPal, kde môžete dokončiť váš dar.',
 1073+ 'contrib-tracking-button' => 'Pokračovať',
 1074+ 'contrib-tracking-item-name-onetime' => 'Jednorazový dar',
 1075+ 'contrib-tracking-item-name-recurring' => 'Opakovaný mesačný dar',
 1076+);
 1077+
 1078+/** Slovenian (Slovenščina)
 1079+ * @author Dbc334
 1080+ */
 1081+$messages['sl'] = array(
 1082+ 'contributiontracking-desc' => 'Sledenje prispevkov za zbiralca sredstev Wikimedie',
 1083+ 'contributiontracking' => 'Sledenje prispevkov',
 1084+ 'contrib-tracking-error' => 'Napaka',
 1085+ 'contrib-tracking-error-text' => 'Neveljavni oddan obrazec',
 1086+ 'contrib-tracking-submitting' => 'Pošiljanje predelovalcu plačil ...',
 1087+ 'contrib-tracking-continue' => 'Če niste samodejno preusmerjeni, kliknite gumb, da dokončate svoj prispevek pri PayPal.',
 1088+ 'contrib-tracking-redirect' => 'Samodejno boste preusmerjeni na PayPal za dokončanje vašega prispevka.',
 1089+ 'contrib-tracking-button' => 'Nadaljuj',
 1090+ 'contrib-tracking-item-name-onetime' => 'Enkratni prispevek',
 1091+ 'contrib-tracking-item-name-recurring' => 'Ponavljajoči se mesečni prispevki',
 1092+);
 1093+
 1094+/** Serbian Cyrillic ekavian (‪Српски (ћирилица)‬)
 1095+ * @author Јованвб
 1096+ */
 1097+$messages['sr-ec'] = array(
 1098+ 'contrib-tracking-error' => 'Грешка',
 1099+ 'contrib-tracking-button' => 'Настави',
 1100+);
 1101+
 1102+/** Serbian Latin ekavian (‪Srpski (latinica)‬)
 1103+ * @author Michaello
 1104+ */
 1105+$messages['sr-el'] = array(
 1106+ 'contrib-tracking-error' => 'Greška',
 1107+ 'contrib-tracking-button' => 'Nastavi',
 1108+);
 1109+
 1110+/** Swedish (Svenska)
 1111+ * @author Boivie
 1112+ * @author Tobulos1
 1113+ */
 1114+$messages['sv'] = array(
 1115+ 'contributiontracking-desc' => 'Bidragsspårning för Wikimedia-insamlingen',
 1116+ 'contributiontracking' => 'Bidragsspårning',
 1117+ 'contrib-tracking-error' => 'Fel',
 1118+ 'contrib-tracking-error-text' => 'Ogiltigt skickande av formulär',
 1119+ 'contrib-tracking-submitting' => 'Skickar till betalningshanterare...',
 1120+ 'contrib-tracking-continue' => 'Om du inte kommer vidare automatiskt, klicka på knappen för att slutföra din donation hos PayPal.',
 1121+ 'contrib-tracking-redirect' => 'Du kommer att omdirigeras automatiskt till PayPal för att slutföra din donation.',
 1122+ 'contrib-tracking-button' => 'Fortsätt',
 1123+ 'contrib-tracking-item-name-onetime' => 'Engångs-donation',
 1124+ 'contrib-tracking-item-name-recurring' => 'Återkommande månatlig donation',
 1125+);
 1126+
 1127+/** Swahili (Kiswahili) */
 1128+$messages['sw'] = array(
 1129+ 'contrib-tracking-error' => 'Hitilafu',
 1130+);
 1131+
 1132+/** Tamil (தமிழ்)
 1133+ * @author TRYPPN
 1134+ */
 1135+$messages['ta'] = array(
 1136+ 'contrib-tracking-error' => 'தவறு',
 1137+ 'contrib-tracking-button' => 'தொடரவும்',
 1138+);
 1139+
 1140+/** Telugu (తెలుగు)
 1141+ * @author Veeven
 1142+ */
 1143+$messages['te'] = array(
 1144+ 'contrib-tracking-error' => 'పొరపాటు',
 1145+ 'contrib-tracking-continue' => 'స్వయంచాలకంగా మిమ్మల్ని దారి మళ్ళించకపోతే, పేపాల్ లో మీ విరాళాన్ని పూర్తిచెయ్యడానికి మీరే బొత్తాన్ని నొక్కండి.',
 1146+ 'contrib-tracking-button' => 'కొనసాగించు',
 1147+);
 1148+
 1149+/** Tajik (Cyrillic) (Тоҷикӣ (Cyrillic))
 1150+ * @author Ibrahim
 1151+ */
 1152+$messages['tg-cyrl'] = array(
 1153+ 'contrib-tracking-error' => 'Хато',
 1154+ 'contrib-tracking-button' => 'Идома',
 1155+);
 1156+
 1157+/** Tajik (Latin) (Тоҷикӣ (Latin))
 1158+ * @author Liangent
 1159+ */
 1160+$messages['tg-latn'] = array(
 1161+ 'contrib-tracking-error' => 'Xato',
 1162+ 'contrib-tracking-button' => 'Idoma',
 1163+);
 1164+
 1165+/** Thai (ไทย)
 1166+ * @author Korrawit
 1167+ * @author Woraponboonkerd
 1168+ */
 1169+$messages['th'] = array(
 1170+ 'contrib-tracking-error' => 'เกิดความผิดพลาด',
 1171+ 'contrib-tracking-submitting' => 'กำลังส่งไปยังผู้ดำเนินการจ่ายเงิน...',
 1172+ 'contrib-tracking-continue' => 'ถ้าคุณยังไม่ได้ถูกนำทางไปหน้าอื่นโดยอัตโนมัติ คลิกที่ปุ่มเพื่อเสร็จสิ้นการบริจาคที่ PayPal',
 1173+ 'contrib-tracking-button' => 'ดำเนินการต่อ',
 1174+);
 1175+
 1176+/** Turkmen (Türkmençe)
 1177+ * @author Hanberke
 1178+ */
 1179+$messages['tk'] = array(
 1180+ 'contributiontracking-desc' => 'Wikimedia fond ýygnaýjysy üçin bagyş trekingi',
 1181+ 'contributiontracking' => 'Goşant trekingi',
 1182+ 'contrib-tracking-error' => 'Säwlik',
 1183+ 'contrib-tracking-error-text' => 'Nädogry form tabşyrmasy',
 1184+ 'contrib-tracking-submitting' => 'Töleg işleýjä tabşyrylýar...',
 1185+ 'contrib-tracking-continue' => 'Eger awtomatik gönükdirilmeseňiz, PayPal-däki bagyşyňyzy tamamlamak üçin düwmäni tykladyň.',
 1186+ 'contrib-tracking-button' => 'Dowam et',
 1187+);
 1188+
 1189+/** Tagalog (Tagalog)
 1190+ * @author AnakngAraw
 1191+ * @author Sky Harbor
 1192+ */
 1193+$messages['tl'] = array(
 1194+ 'contributiontracking-desc' => 'Pagsunod sa bakas (pagtugaygay) para sa pangangalap ng salaping panustos (pondo) ng Wikimedia',
 1195+ 'contributiontracking' => 'Pagtugaygay/pagsunod sa bakas ng ambag',
 1196+ 'contrib-tracking-error' => 'Kamalian',
 1197+ 'contrib-tracking-error-text' => 'Hindi tanggap na anyo/pormularyo ng pagpasa',
 1198+ 'contrib-tracking-submitting' => 'Ipinapasa/ipinapadala sa tagapagsagawa (tagaproseso) ng bayad...',
 1199+ 'contrib-tracking-continue' => 'Kapag hindi ka kusang naituro/awtomatikong naihatid, pindutin ang pindutan upang mabuo ang iyong ambag/abuloy doon sa PayPal.',
 1200+ 'contrib-tracking-redirect' => 'Automatiko kang ikakarga sa PayPal upang makumpleto ang iyong pagkakaloob.',
 1201+ 'contrib-tracking-button' => 'Magpatuloy',
 1202+ 'contrib-tracking-item-name-onetime' => 'Abuloy na pang-isang pagkakataon',
 1203+ 'contrib-tracking-item-name-recurring' => 'Umuulit na buwanang ambag',
 1204+);
 1205+
 1206+/** Turkish (Türkçe)
 1207+ * @author Joseph
 1208+ */
 1209+$messages['tr'] = array(
 1210+ 'contributiontracking-desc' => 'Vikimedya fonsağlayıcı için katkı izlemesi',
 1211+ 'contributiontracking' => 'Katkı izlemesi',
 1212+ 'contrib-tracking-error' => 'Hata',
 1213+ 'contrib-tracking-error-text' => 'Geçersiz form teslimi',
 1214+ 'contrib-tracking-submitting' => 'Ödeme işlemcisine gönderiliyor...',
 1215+ 'contrib-tracking-continue' => "Eğer otomatik olarak yönlendirilmediyseniz, PayPal'daki bağışınızı tamamlamak için düğmeye basın.",
 1216+ 'contrib-tracking-button' => 'Devam et',
 1217+);
 1218+
 1219+/** Ukrainian (Українська)
 1220+ * @author Aleksandrit
 1221+ * @author Тест
 1222+ */
 1223+$messages['uk'] = array(
 1224+ 'contributiontracking-desc' => 'Відстеження збору пожертвувань на користь Вікімедіі',
 1225+ 'contributiontracking' => 'Відстеження збору пожертвувань',
 1226+ 'contrib-tracking-error' => 'Помилка',
 1227+ 'contrib-tracking-error-text' => 'Неправильне підпорядкування форми',
 1228+ 'contrib-tracking-submitting' => 'Відправка оброблювачеві платежів...',
 1229+ 'contrib-tracking-continue' => 'Якщо ви не були автоматично перенаправлені, натисніть на кнопку, щоб завершити вашу пожертву на сайті PayPal.',
 1230+ 'contrib-tracking-redirect' => 'Ви будете автоматично перенаправлені на PayPal для завершення вашого пожертвування.',
 1231+ 'contrib-tracking-button' => 'Продовжити',
 1232+ 'contrib-tracking-item-name-onetime' => 'Одноразове пожертвування',
 1233+ 'contrib-tracking-item-name-recurring' => 'Періодичне щомісячне пожертвування',
 1234+);
 1235+
 1236+/** Vèneto (Vèneto)
 1237+ * @author Candalua
 1238+ */
 1239+$messages['vec'] = array(
 1240+ 'contributiontracking-desc' => 'Traciamento dei contributi par la racolta fondi Wikimedia',
 1241+ 'contributiontracking' => 'Traciamento dei contributi',
 1242+ 'contrib-tracking-error' => 'Eròr',
 1243+ 'contrib-tracking-error-text' => 'Invio de modulo mia valido',
 1244+ 'contrib-tracking-submitting' => "So' drio contatar el processador del pagamento...",
 1245+ 'contrib-tracking-continue' => 'Se no te vien reindirissà automaticamente, struca el boton par conpletar la to donassion su PayPal.',
 1246+ 'contrib-tracking-button' => 'Và vanti',
 1247+);
 1248+
 1249+/** Veps (Vepsan kel')
 1250+ * @author Игорь Бродский
 1251+ */
 1252+$messages['vep'] = array(
 1253+ 'contributiontracking-desc' => 'Rahoidenkeradusen WikiMediale kaclend',
 1254+ 'contributiontracking' => 'Lahjoičendan kaclend',
 1255+ 'contrib-tracking-error' => 'Petuz',
 1256+ 'contrib-tracking-error-text' => 'Forman vär alištuz',
 1257+ 'contrib-tracking-submitting' => 'Oigendamine maksuiden processorale...',
 1258+ 'contrib-tracking-continue' => 'Ku teid ei udesoigendanuded avtomatižešti, paindat kingiteshe, miše lahjoita rahoid PayPal-saital.',
 1259+ 'contrib-tracking-button' => 'Jatkta',
 1260+);
 1261+
 1262+/** Vietnamese (Tiếng Việt)
 1263+ * @author Minh Nguyen
 1264+ * @author Vinhtantran
 1265+ */
 1266+$messages['vi'] = array(
 1267+ 'contributiontracking-desc' => 'Dò đóng góp cho chương trình gây quỹ Wikimedia',
 1268+ 'contributiontracking' => 'Dò đóng góp',
 1269+ 'contrib-tracking-error' => 'Lỗi',
 1270+ 'contrib-tracking-error-text' => 'Đăng mẫu sau quy cách',
 1271+ 'contrib-tracking-submitting' => 'Đang đăng bộ xử lý tính tiền...',
 1272+ 'contrib-tracking-continue' => 'Nếu không được tự động đổi hướng, nhấn vào nút để hoàn thành quyên góp của bạn tại PayPal.',
 1273+ 'contrib-tracking-redirect' => 'Bạn sẽ được tự động chuyển hướng đến PayPal để hoàn thành đóng góp của bạn.',
 1274+ 'contrib-tracking-button' => 'Tiếp tục',
 1275+ 'contrib-tracking-item-name-onetime' => 'Quyên góp một lần',
 1276+ 'contrib-tracking-item-name-recurring' => 'Quyên góp định kỳ hàng tháng',
 1277+);
 1278+
 1279+/** Volapük (Volapük)
 1280+ * @author Smeira
 1281+ */
 1282+$messages['vo'] = array(
 1283+ 'contrib-tracking-error' => 'Pöl',
 1284+ 'contrib-tracking-error-text' => 'Fometised no lonöföl',
 1285+ 'contrib-tracking-button' => 'Föfiö!',
 1286+);
 1287+
 1288+/** Yiddish (ייִדיש)
 1289+ * @author פוילישער
 1290+ */
 1291+$messages['yi'] = array(
 1292+ 'contrib-tracking-error' => 'גרײַז',
 1293+);
 1294+
 1295+/** Simplified Chinese (‪中文(简体)‬)
 1296+ * @author Chenzw
 1297+ * @author Hydra
 1298+ * @author Liangent
 1299+ * @author Wilsonmess
 1300+ */
 1301+$messages['zh-hans'] = array(
 1302+ 'contributiontracking-desc' => '跟踪维基媒体筹款的贡献',
 1303+ 'contributiontracking' => '跟踪的贡献',
 1304+ 'contrib-tracking-error' => '错误',
 1305+ 'contrib-tracking-error-text' => '无效的表单提交',
 1306+ 'contrib-tracking-submitting' => '正在提交到支付处理器……',
 1307+ 'contrib-tracking-continue' => '若阁下没有被重定向至PayPal页面,请点击按钮以完成您的捐助。',
 1308+ 'contrib-tracking-redirect' => '您将被自动重定向到 PayPal 来完成你的捐款。',
 1309+ 'contrib-tracking-button' => '继续',
 1310+ 'contrib-tracking-item-name-onetime' => '一次性捐赠',
 1311+ 'contrib-tracking-item-name-recurring' => '每月定期捐款',
 1312+);
 1313+
 1314+/** Traditional Chinese (‪中文(繁體)‬)
 1315+ * @author Liangent
 1316+ * @author Mark85296341
 1317+ */
 1318+$messages['zh-hant'] = array(
 1319+ 'contributiontracking-desc' => '追蹤維基媒體籌款的貢獻',
 1320+ 'contributiontracking' => '追蹤的貢獻',
 1321+ 'contrib-tracking-error' => '錯誤',
 1322+ 'contrib-tracking-error-text' => '無效的表單提交',
 1323+ 'contrib-tracking-submitting' => '正在提交到支付處理器……',
 1324+ 'contrib-tracking-continue' => '若閣下沒有被重定向至 PayPal 頁面,請點擊按鈕以完成您的捐助。',
 1325+ 'contrib-tracking-redirect' => '您將被自動重定向到 PayPal 來完成你的捐款。',
 1326+ 'contrib-tracking-button' => '繼續',
 1327+ 'contrib-tracking-item-name-onetime' => '一次性捐助',
 1328+ 'contrib-tracking-item-name-recurring' => '每月定期捐款',
 1329+);
 1330+
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.i18n.php
___________________________________________________________________
Added: svn:eol-style
11331 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.processor.php
@@ -0,0 +1,456 @@
 2+<?php
 3+/**
 4+ * Centralized class used by both the old interstitial page, and the API to
 5+ * process transactions and send donors off to the correct gateway location.
 6+ * @author Katie Horn <khorn@wikimedia.org>
 7+ */
 8+class ContributionTrackingProcessor {
 9+
 10+ /**
 11+ * If a database connection has already been established, it returns that
 12+ * connection. Otherwise, it establishes one, and returns that.
 13+ * @global string $wgContributionTrackingDBserver : DB Server name, defined
 14+ * in ContributionTracking.php
 15+ * @global string $wgContributionTrackingDBname : Database name, defined in
 16+ * ContributionTracking.php
 17+ * @global string $wgContributionTrackingDBuser : Database user, defined in
 18+ * ContributionTracking.php
 19+ * @global string $wgContributionTrackingDBpassword : Database password,
 20+ * defined in ContributionTracking.php
 21+ * @staticvar DatabaseMysql $db
 22+ * @return DatabaseMysql The established database connection
 23+ */
 24+ static function contributionTrackingConnection() {
 25+ global $wgContributionTrackingDBserver, $wgContributionTrackingDBname;
 26+ global $wgContributionTrackingDBuser, $wgContributionTrackingDBpassword;
 27+
 28+ static $db;
 29+
 30+ if ( !$db ) {
 31+ $db = new DatabaseMysql(
 32+ $wgContributionTrackingDBserver,
 33+ $wgContributionTrackingDBuser,
 34+ $wgContributionTrackingDBpassword,
 35+ $wgContributionTrackingDBname );
 36+ $db->query( "SET names utf8" );
 37+ }
 38+
 39+ return $db;
 40+ }
 41+
 42+ /**
 43+ * Looks up the url specified in $ref. If it is known, the existing id is
 44+ * returned. If it is new, a row is added to contribution_owa_ref and the
 45+ * new id is returned.
 46+ * @param string $ref owa_ref URL
 47+ * @return integer ID of the URL in the contribution_tracking_owa_ref table,
 48+ * 0 if something went wrong.
 49+ */
 50+ static function get_owa_ref_id( $ref ) {
 51+ // Replication lag means sometimes a new event will not exist in the table yet
 52+ $dbw = ContributionTrackingProcessor::contributionTrackingConnection(); //wfGetDB( DB_MASTER );
 53+ $id_num = $dbw->selectField(
 54+ 'contribution_tracking_owa_ref',
 55+ 'id',
 56+ array( 'url' => $ref ),
 57+ __METHOD__
 58+ );
 59+ // Once we're on mysql 5, we can use replace() instead of this selectField --> insert or update hooey
 60+ if ( $id_num === false ) {
 61+ $dbw->insert(
 62+ 'contribution_tracking_owa_ref',
 63+ array( 'url' => ( string ) $ref ),
 64+ __METHOD__
 65+ );
 66+ $id_num = $dbw->insertId();
 67+ }
 68+ return $id_num === false ? 0 : $id_num;
 69+ }
 70+
 71+ /**
 72+ * Saves a record of a new contribution to the contribution_tracking_table
 73+ * @param array $params A staged array of parameters that can be processed
 74+ * by the ContributionTrackingProcessor.
 75+ * @return integer The id of the saved contribution in the
 76+ * contribution_tracking table
 77+ */
 78+ static function saveNewContribution( $params = array( ) ) {
 79+ $db = ContributionTrackingProcessor::contributionTrackingConnection();
 80+
 81+ $params['ts'] = $db->timestamp();
 82+
 83+ $owa_ref = null;
 84+ if ( array_key_exists( 'owa_ref', $params ) && $params['owa_ref'] != null ) {
 85+ if ( $params['owa_ref'] == null || is_numeric( $params['owa_ref'] ) ) {
 86+ $owa_ref = $params['owa_ref'];
 87+ } else {
 88+ $owa_ref = ContributionTrackingProcessor::get_owa_ref_id( $params['owa_ref'] );
 89+ }
 90+ }
 91+ $params['owa_ref'] = $owa_ref;
 92+
 93+ $tracked_contribution = ContributionTrackingProcessor::stage_contribution( $params );
 94+
 95+ $db->insert( 'contribution_tracking', $tracked_contribution );
 96+ $contribution_tracking_id = $db->insertId();
 97+
 98+ return $contribution_tracking_id;
 99+ }
 100+
 101+ /**
 102+ * Stages the contribution parameters
 103+ * @param array $params Key-value pairs of the contribution parameters we
 104+ * want to pass in.
 105+ * @return array Staged key-value pairs ready to be saved as a contribution.
 106+ */
 107+ static function stage_contribution( $params ) {
 108+
 109+ //change the posted names to match the db where necessary
 110+ ContributionTrackingProcessor::rekey( $params, 'comment', 'note' );
 111+ ContributionTrackingProcessor::rekey_invert_boolean( $params, 'comment-option', 'anonymous' );
 112+ ContributionTrackingProcessor::rekey_invert_boolean( $params, 'email-opt', 'optout' );
 113+
 114+ $tracked_contribution = ContributionTrackingProcessor::mergeArrayDefaults( $params, ContributionTrackingProcessor::getContributionDefaults(), true );
 115+
 116+ return $tracked_contribution;
 117+ }
 118+
 119+ /**
 120+ * Stages the relevent data that will be sent to the gateway
 121+ * @global string $wgContributionTrackingPayPalRecurringIPN URL for paypal
 122+ * recurring donations : Defined in ContributionTracking.php
 123+ * @global string $wgContributionTrackingPayPalIPN URL for paypal recurring
 124+ * donations : Defined in ContributionTracking.php
 125+ * @param array $params Parameters to post to the gateway
 126+ * @return array Staged array
 127+ */
 128+ static function stage_repost( $params ) {
 129+ global $wgContributionTrackingPayPalRecurringIPN, $wgContributionTrackingPayPalIPN;
 130+ //TODO: assert that gateway makes The Sense here.
 131+ //change the posted names to match the db where necessary
 132+ ContributionTrackingProcessor::rekey( $params, 'amountGiven', 'amount_given' );
 133+ ContributionTrackingProcessor::rekey( $params, 'returnto', 'return' );
 134+
 135+ //booleanize!
 136+ ContributionTrackingProcessor::stage_checkbox( $params, 'recurring_paypal' );
 137+
 138+ //poke our language function with the current parameters - this sets the static var correctly
 139+ $params['language'] = ContributionTrackingProcessor::getLanguage( $params );
 140+
 141+ if ( array_key_exists( 'recurring_paypal', $params ) && $params['recurring_paypal'] ) {
 142+ $params['notify_url'] = $wgContributionTrackingPayPalRecurringIPN;
 143+ $params['item_name'] = ContributionTrackingProcessor::msg( 'contrib-tracking-item-name-recurring' );
 144+ } else {
 145+ $params['notify_url'] = $wgContributionTrackingPayPalIPN;
 146+ $params['item_name'] = ContributionTrackingProcessor::msg( 'contrib-tracking-item-name-onetime' );
 147+ }
 148+
 149+ $repost_params = ContributionTrackingProcessor::mergeArrayDefaults( $params, ContributionTrackingProcessor::getRepostDefaults(), true );
 150+ return $repost_params;
 151+ }
 152+
 153+ /**
 154+ * Effectively changes the name of a key in an array. If the key does not
 155+ * exist, no change is made.
 156+ * @param array $array The array to rekey (by reference)
 157+ * @param string $oldkey The key to change
 158+ * @param string $newkey The new value for the key
 159+ */
 160+ static function rekey( &$array, $oldkey, $newkey ) {
 161+ if ( array_key_exists( $oldkey, $array ) ) {
 162+ $array[$newkey] = $array[$oldkey];
 163+ unset( $array[$oldkey] );
 164+ }
 165+ }
 166+
 167+ /**
 168+ * There are a few values that come in, which are both generated by
 169+ * checkboxes, and are the exact inverse of the way we save them in the
 170+ * table.
 171+ * For these values, if the key exists (and is not explicit false), it is
 172+ * received as "true". Therefore, the rekey'd value should be false.
 173+ * However, the old key not existing isn't exactly conclusive.
 174+ * @param array $array The array to rekey (by reference)
 175+ * @param string $oldkey The key to change
 176+ * @param string $invertedkey The key meant to contain the inverted boolean
 177+ * of the old key.
 178+ */
 179+ static function rekey_invert_boolean( &$array, $oldkey, $invertedkey ) {
 180+ if ( array_key_exists( $oldkey, $array ) ) {
 181+ if ( $array[$oldkey] !== false ) {
 182+ unset( $array[$oldkey] );
 183+ $array[$invertedkey] = false;
 184+ } else {
 185+ $array[$invertedkey] = 1;
 186+ }
 187+ return;
 188+ }
 189+
 190+ if ( array_key_exists( $invertedkey, $array ) ) {
 191+ ContributionTrackingProcessor::stage_checkbox( $array, $invertedkey );
 192+ return;
 193+ }
 194+
 195+ //at this point, neither key exists. We go with the default.
 196+ $default = ContributionTrackingProcessor::getContributionDefaults();
 197+ if ( array_key_exists( $invertedkey, $default ) ) {
 198+ $array[$invertedkey] = $default[$invertedkey];
 199+ }
 200+ }
 201+
 202+ /**
 203+ * Stages a value generated by a checkbox or similar control, for use in our
 204+ * database. If the key exists and has not been set to exactly false, it's
 205+ * "true".
 206+ * @param array $array The array containing the value to stage, by reference
 207+ * @param string $key The key of a checkbox-generated value
 208+ */
 209+ static function stage_checkbox( &$array, $key ) {
 210+ //apparently so far in the code, if the key exists, the value is considered true
 211+ //and is therefore set to "1"
 212+ if ( array_key_exists( $key, $array ) && $array[$key] !== false ) {
 213+ $array[$key] = 1;
 214+ }
 215+ }
 216+
 217+ /**
 218+ * Returns a default value for every relevent field in a new contribution.
 219+ * @return array Default values for a new contribution.
 220+ */
 221+ static function getContributionDefaults() {
 222+ return array( //defaults
 223+ 'note' => null,
 224+ 'referrer' => null,
 225+ 'anonymous' => 0,
 226+ 'utm_source' => null,
 227+ 'utm_medium' => null,
 228+ 'utm_campaign' => null,
 229+ 'optout' => 0,
 230+ 'language' => null,
 231+ 'owa_session' => null,
 232+ 'owa_ref' => null,
 233+ 'ts' => null,
 234+ );
 235+ }
 236+
 237+ /**
 238+ * Returns a default value for every relevent field in a repost to a gateway
 239+ * @return array Default values for a payment gateway repost
 240+ */
 241+ static function getRepostDefaults() {
 242+ return array( //defaults
 243+ 'gateway' => '',
 244+ 'tshirt' => false,
 245+ 'size' => false,
 246+ 'premium_language' => false,
 247+ 'currency_code' => 'USD',
 248+ 'return' => 'Donate-thanks/' . ContributionTrackingProcessor::getLanguage(),
 249+ 'fname' => '',
 250+ 'lname' => '',
 251+ 'email' => '',
 252+ 'recurring_paypal' => '0',
 253+ 'amount' => '',
 254+ 'amount_given' => '',
 255+ 'contribution_tracking_id' => '',
 256+ 'notify_url' => '',
 257+ 'item_name' => '',
 258+ 'address1' => '',
 259+ 'city' => '',
 260+ 'state' => '',
 261+ 'zip' => '',
 262+ 'country' => 'US',
 263+ 'address_override' => '0'
 264+
 265+ );
 266+ }
 267+
 268+ /**
 269+ * Merges an array of parameters from a payment form, with an array of
 270+ * default values. Additionally: Values in the $params array will only be
 271+ * returned if there is a corresponding key in the $defaults array.
 272+ * @param array $params Form / API data
 273+ * @param array $defaults A set of default values for a particular
 274+ * transaction type
 275+ * @param boolean $nullify If true, keys with empty string values will be
 276+ * set to null in the return array.
 277+ * @return array
 278+ */
 279+ static function mergeArrayDefaults( $params, $defaults, $nullify=false ) {
 280+ $ret = $defaults;
 281+ foreach ( $ret as $key => $value ) {
 282+ if ( array_key_exists( $key, $params ) ) {
 283+ $ret[$key] = $params[$key];
 284+ }
 285+ if ( $nullify && $ret[$key] === '' ) {
 286+ $ret[$key] = null;
 287+ }
 288+ }
 289+ return $ret;
 290+ }
 291+
 292+ /**
 293+ * Takes staged transaction data, and constructs the key/value pairs
 294+ * formatted to be reposted to the gateway specified in $input['gateway']
 295+ * @global string $wgContributionTrackingPayPalBusiness 'Business' string
 296+ * for PayPal: Defined in ContributionTracking.php
 297+ * @global string $wgContributionTrackingReturnToURLDefault Default URL to
 298+ * return to after the transaction was processed by the gateway. Used if
 299+ * none supplied.
 300+ * @param array $input The staged data to repost to a gateway.
 301+ * @return array Key/value pairs, ready to be reposted to the specified
 302+ * gateway to complete the transaction.
 303+ */
 304+ static function getRepostFields( $input ) {
 305+ global $wgContributionTrackingPayPalBusiness, $wgContributionTrackingReturnToURLDefault;
 306+ // Set the action and tracking ID fields
 307+ $input = ContributionTrackingProcessor::stage_repost( $input );
 308+
 309+ $repost = array( );
 310+ $repost['action'] = 'http://wikimediafoundation.org/';
 311+ $amount_field_name = 'amount'; // the amount fieldname may be different depending on the service
 312+ if ( $input['gateway'] == 'paypal' ) {
 313+
 314+ $repost['action'] = 'https://www.paypal.com/cgi-bin/webscr';
 315+
 316+ // Premiums
 317+ if ( array_key_exists( 'tshirt', $input ) && $input['tshirt'] ) {
 318+ $repost['fields']['on0'] = 'Shirt size';
 319+ $repost['fields']['os0'] = $input['size'];
 320+ $repost['fields']['on1'] = 'Shirt language';
 321+ $repost['fields']['os1'] = $input['premium_language'];
 322+ $repost['fields']['no_shipping'] = 2;
 323+ }
 324+
 325+ // PayPal
 326+ $repost['fields']['business'] = $wgContributionTrackingPayPalBusiness;
 327+ $repost['fields']['item_number'] = 'DONATE';
 328+ $repost['fields']['no_note'] = '0';
 329+
 330+ $returnText = $input['return'];
 331+ $returnTitle = Title::newFromText( $returnText );
 332+ if ( $returnTitle ) {
 333+ $returnto = wfExpandUrl( $returnTitle->getFullUrl(), PROTO_CURRENT );
 334+ } else {
 335+ $returnto = $wgContributionTrackingReturnToURLDefault . "/$language";
 336+ }
 337+ $repost['fields']['return'] = $returnto;
 338+ $repost['fields']['currency_code'] = $input['currency_code'];
 339+
 340+ // additional fields to pass to PayPal from single-step credit card form and 1st step with address fields
 341+ if ( array_key_exists( 'fname', $input ) && !empty( $input['fname'] ) ) {
 342+ $repost['fields']['first_name'] = $input['fname'];
 343+ }
 344+ if ( array_key_exists( 'lname', $input ) && !empty( $input['lname'] ) ) {
 345+ $repost['fields']['last_name'] = $input['lname'];
 346+ }
 347+ if ( array_key_exists( 'email', $input ) && !empty( $input['email'] ) ) {
 348+ $repost['fields']['email'] = $input['email'];
 349+ }
 350+
 351+ if ( array_key_exists( 'address1', $input ) && !empty( $input['address1'] ) ) {
 352+ $repost['fields']['address1'] = $input['address1'];
 353+ }
 354+
 355+ if ( array_key_exists( 'city', $input ) && !empty( $input['city'] ) ) {
 356+ $repost['fields']['city'] = $input['city'];
 357+ }
 358+
 359+ if ( array_key_exists( 'state', $input ) && !empty( $input['state'] ) ) {
 360+ $repost['fields']['state'] = $input['state'];
 361+ }
 362+
 363+ if ( array_key_exists( 'zip', $input ) && !empty( $input['zip'] ) ) {
 364+ $repost['fields']['zip'] = $input['zip'];
 365+ }
 366+
 367+
 368+ if ( array_key_exists( 'country', $input ) && !empty( $input['country'] ) ) {
 369+ $repost['fields']['country'] = $input['country'];
 370+ }
 371+
 372+ if ( array_key_exists( 'address_override', $input ) && !empty( $input['address_override'] ) ) {
 373+ $repost['fields']['address_override'] = $input['address_override'];
 374+ }
 375+
 376+
 377+ // if this is a recurring donation, we have add'l fields to send to paypal
 378+ if ( $input['recurring_paypal'] && $input['recurring_paypal'] != 0 ) {
 379+
 380+ $repost['fields']['t3'] = "M"; // The unit of measurement for for p3 (M = month)
 381+ $repost['fields']['p3'] = '1'; // Billing cycle duration
 382+ $repost['fields']['srt'] = '12'; // # of billing cycles
 383+ $repost['fields']['src'] = '1'; // Make this 'recurring'
 384+ $repost['fields']['sra'] = '1'; // Turn on re-attempt on failure
 385+ $repost['fields']['cmd'] = '_xclick-subscriptions';
 386+ $amount_field_name = 'a3';
 387+ $repost['fields']['notify_url'] = $input['notify_url'];
 388+ $repost['fields']['item_name'] = $input['item_name'];
 389+ } else {
 390+ $repost['fields']['cmd'] = '_xclick';
 391+ $repost['fields']['notify_url'] = $input['notify_url'];
 392+ $repost['fields']['item_name'] = $input['item_name'];
 393+ }
 394+ } else if ( $input['gateway'] == 'moneybookers' ) {
 395+ $repost['action'] = 'https://www.moneybookers.com/app/payment.pl';
 396+
 397+ // Tracking
 398+ $repost['fields']['merchant_fields'] = 'os0';
 399+
 400+ // Moneybookers
 401+ $repost['fields']['pay_to_email'] = 'donation@wikipedia.org';
 402+ $repost['fields']['status_url'] = 'https://civicrm.wikimedia.org/fundcore_gateway/moneybookers';
 403+ $repost['fields']['language'] = 'en';
 404+ $repost['fields']['detail1_description'] = 'One-time donation';
 405+ $repost['fields']['detail1_text'] = 'DONATE';
 406+ $repost['fields']['currency'] = $input['currency_code'];
 407+ } else {
 408+ throw new MWException( "Unknown payment gateway!" );
 409+ }
 410+
 411+ // Normalized amount
 412+ $repost['fields'][$amount_field_name] = $input['amount'];
 413+ if ( $input['amount_given'] ) {
 414+ $repost['fields'][$amount_field_name] = $input['amount_given'];
 415+ }
 416+
 417+ // Tracking
 418+ $repost['fields']['custom'] = $input['contribution_tracking_id'];
 419+
 420+ return $repost;
 421+ }
 422+
 423+ /**
 424+ * Sets any language that is expressly specified in the posted parameters.
 425+ * If no language is expressly set, it gets the global language code.
 426+ * @global Language $wgLang
 427+ * @staticvar string $language The language code currently in use
 428+ * @param array $params Request parameters that may or may not contain a
 429+ * 'language' key.
 430+ * @return string A valid language code
 431+ */
 432+ static function getLanguage( $params = '' ) {
 433+ static $language = '';
 434+
 435+ if ( is_array( $params ) && array_key_exists( 'language', $params ) && $params['language'] != null ) {
 436+ //set/reset if something inteligable got sent.
 437+ $language = $params['language'];
 438+ }
 439+
 440+ if ( $language == '' ) { //if we have nothing by this point...
 441+ global $wgLang;
 442+ $language = $wgLang->getCode();
 443+ }
 444+
 445+ return $language;
 446+ }
 447+
 448+ /**
 449+ * Gets a message in the local language
 450+ * @param string $key Message key
 451+ * @return string translated message
 452+ */
 453+ static function msg( $key ) {
 454+ return wfMsgExt( $key, array( 'escape', 'language' => ContributionTrackingProcessor::getLanguage() ) );
 455+ }
 456+
 457+}
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.processor.php
___________________________________________________________________
Added: svn:mergeinfo
1458 Merged /trunk/extensions/ContributionReporting/ContributionTracking.processor.php:r74440-74491
2459 Merged /branches/wmf-deployment/extensions/ContributionTracking/ContributionTracking.processor.php:r60970
3460 Merged /branches/wmf/1.16wmf4/extensions/ContributionTracking/ContributionTracking.processor.php:r67177,69199,76243,77266
4461 Merged /trunk/extensions/ContributionTracking/ContributionTracking.processor.php:r64690-95543,95664-96036
5462 Merged /trunk/phase3/extensions/ContributionTracking/ContributionTracking.processor.php:r63545-63546,63549,63643,63764,63897-63901,64113,64509,65387,65391,65555,65590,65650,65816,77555,77558-77560,77563-77565,77573
Added: svn:eol-style
6463 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.php
@@ -0,0 +1,135 @@
 2+<?php
 3+
 4+# Alert the user that this is not a valid entry point to MediaWiki if they try to access the special pages file directly.
 5+if ( !defined( 'MEDIAWIKI' ) ) {
 6+ echo <<<EOT
 7+To install my extension, put the following line in LocalSettings.php:
 8+require_once( "\$IP/extensions/ContributionTracking/ContributionTracking.php" );
 9+EOT;
 10+ exit( 1 );
 11+}
 12+
 13+$wgExtensionCredits['specialpage'][] = array(
 14+ 'path' => __FILE__,
 15+ 'name' => 'ContributionTracking',
 16+ 'url' => 'http://www.mediawiki.org/wiki/Extension:ContributionTracking',
 17+ 'author' => 'David Strauss',
 18+ 'descriptionmsg' => 'contributiontracking-desc',
 19+);
 20+
 21+$dir = dirname( __FILE__ ) . '/';
 22+
 23+$wgExtensionMessagesFiles['ContributionTracking'] = $dir . 'ContributionTracking.i18n.php';
 24+$wgExtensionAliasesFiles['ContributionTracking'] = $dir . 'ContributionTracking.alias.php';
 25+$wgAutoloadClasses['ContributionTracking'] = $dir . 'ContributionTracking_body.php';
 26+$wgSpecialPages['ContributionTracking'] = 'ContributionTracking';
 27+
 28+$wgAutoloadClasses['ContributionTrackingTester'] = $dir . 'ContributionTracking_Tester.php';
 29+$wgSpecialPages['ContributionTrackingTester'] = 'ContributionTrackingTester';
 30+
 31+//give sysops access to the tracking tester form.
 32+$wgGroupPermissions['sysop']['ViewContributionTrackingTester'] = true;
 33+$wgAvailableRights[] = 'ViewContributionTrackingTester';
 34+
 35+$wgAutoloadClasses['ApiContributionTracking'] = $dir . 'ApiContributionTracking.php';
 36+$wgAutoloadClasses['ContributionTrackingProcessor'] = $dir . 'ContributionTracking.processor.php';
 37+
 38+//this only works if contribution tracking is inside a mediawiki DB, which typically it isn't.
 39+$wgHooks['LoadExtensionSchemaUpdates'][] = 'efContributionTrackingLoadUpdates';
 40+
 41+// Resource modules
 42+$ctResourceTemplate = array(
 43+ 'localBasePath' => $dir . 'modules',
 44+ 'remoteExtPath' => 'ContributionTracking/modules',
 45+);
 46+$wgResourceModules['jquery.contributionTracking'] = array(
 47+ 'scripts' => 'jquery.contributionTracking.js',
 48+ 'dependencies' => 'jquery.json',
 49+) + $ctResourceTemplate;
 50+
 51+
 52+/**
 53+ * The default 'return to' URL for a thank you page after posting to the contribution
 54+ *
 55+ * NO trailing slash, please
 56+ */
 57+$wgContributionTrackingReturnToURLDefault = 'http://wikimediafoundation.org/wiki/Thank_You';
 58+
 59+$wgContributionTrackingDBserver = $wgDBserver;
 60+$wgContributionTrackingDBname = $wgDBname;
 61+$wgContributionTrackingDBuser = $wgDBuser;
 62+$wgContributionTrackingDBpassword = $wgDBpassword;
 63+
 64+/**
 65+ * IPN listener address for regular PayPal trxns
 66+ */
 67+$wgContributionTrackingPayPalIPN = 'https://civicrm.wikimedia.org/fundcore_gateway/paypal';
 68+
 69+/**
 70+ * IPN listener address for recurring payment PayPal trxns
 71+ */
 72+$wgContributionTrackingPayPalRecurringIPN = 'https://civicrm.wikimedia.org/fundcore_gateway/paypal';
 73+
 74+/**
 75+ * 'Business' string for PayPal
 76+ */
 77+$wgContributionTrackingPayPalBusiness = 'donations@wikimedia.org';
 78+
 79+# Unit tests
 80+$wgHooks['UnitTestsList'][] = 'efContributionTrackingUnitTests';
 81+
 82+function efContributionTrackingUnitTests( &$files ) {
 83+ $files[] = dirname( __FILE__ ) . '/tests/ContributionTrackingTest.php';
 84+ $files[] = dirname( __FILE__ ) . '/tests/ContributionTrackingProcessorTest.php';
 85+ $files[] = dirname( __FILE__ ) . '/tests/ContributionTrackingAPITest.php';
 86+ return true;
 87+}
 88+
 89+// api modules
 90+$wgAPIModules['contributiontracking'] = 'ApiContributionTracking';
 91+
 92+/**
 93+ * @param $updater DatabaseUpdater
 94+ * @return bool
 95+ */
 96+function efContributionTrackingLoadUpdates( $updater = null ){
 97+
 98+ $dir = dirname( __FILE__ ) . '/';
 99+ if ( $updater === null ) {
 100+ global $wgExtNewTables, $wgExtNewFields;
 101+
 102+ $wgExtNewTables[] = array( 'contribution_tracking', $dir . 'ContributionTracking.sql' );
 103+ $wgExtNewTables[] = array( 'contribution_tracking_owa_ref', $dir . 'ContributionTracking_OWA_ref.sql' );
 104+
 105+ $wgExtNewFields[] = array(
 106+ 'contribution_tracking',
 107+ 'owa_session',
 108+ $dir . 'patch-owa.sql',
 109+ );
 110+ } else {
 111+ global $wgContributionTrackingDBname;
 112+
 113+ if($updater->getDB()->getDBName() === $wgContributionTrackingDBname) {
 114+ $updater->addExtensionTable( 'contribution_tracking', $dir . 'ContributionTracking.sql' );
 115+ $updater->addExtensionTable( 'contribution_tracking_owa_ref', $dir . 'ContributionTracking_OWA_ref.sql' );
 116+ $updater->addExtensionUpdate( array( 'addField', 'contribution_tracking', 'owa_session',
 117+ $dir . 'patch-owa.sql', true ) );
 118+ } else { //We are configured not to use the main mediawiki db.
 119+ //Unless the updater is modified not to run
 120+ //'LoadExtensionSchemaUpdates' hooks in its constructor (or do so
 121+ //conditionally), we're going to have to do these manually.
 122+ $ctDB = ContributionTrackingProcessor::contributionTrackingConnection();
 123+ if (!$ctDB->tableExists('contribution_tracking')){
 124+ $ctDB->sourceFile($dir . 'ContributionTracking.sql');
 125+ }
 126+ if (!$ctDB->tableExists('contribution_tracking_owa_ref')){
 127+ $ctDB->sourceFile($dir . 'ContributionTracking_OWA_ref.sql');
 128+ }
 129+ if (!$ctDB->fieldExists('contribution_tracking', 'owa_session')){
 130+ $ctDB->sourceFile($dir . 'patch-owa.sql');
 131+ }
 132+ }
 133+ }
 134+ return true;
 135+
 136+}
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking.php
___________________________________________________________________
Added: svn:keywords
1137 + LastChangedDate LastChangedRevision
Added: svn:eol-style
2138 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/modules/jquery.contributionTracking.js
@@ -0,0 +1,115 @@
 2+/**
 3+ * Turns any form with the bare minimum "appropriate" fields into a form that
 4+ * can get a donor to a gateway with no interstitial page.
 5+ * To use:
 6+ * *) Install the ContributionTracking Extension.
 7+ * *) Include this module on a page.
 8+ * *) On that page, create a form that contains (at least) the fields
 9+ * required by ApiContributionTracking.
 10+ * *) Make sure that form's submit button has a unique ID.
 11+ * *) Assign that button the class of "ajax_me".
 12+ *
 13+ * @author Katie Horn <khorn@wikimedia.org>
 14+ */
 15+
 16+( function( $ ) {
 17+
 18+ /**
 19+ * Binds the onclick function to everything with a class of "ajax_me".
 20+ */
 21+ $.bindAjaxControls = function(){
 22+ $(".ajax_me:disabled").removeAttr("disabled");
 23+ $(".ajax_me").click(function() {
 24+ this.disabled = true;
 25+ $.goAjax(this.id);
 26+ return false; //prevent regular form submission.
 27+ //TODO: Think about the button disabling and enabling.
 28+ //TODO: also think about a barber pole. That would go here.
 29+ });
 30+ }
 31+
 32+ /**
 33+ * Turns the first parent form from the passing object, to an object we can
 34+ * pass to the API.
 35+ * Takes the button ID in string form.
 36+ */
 37+ $.serializeForm = function(buttonID){
 38+ buttonID = "#" + buttonID;
 39+ var form = $(buttonID).parents("form:first");
 40+
 41+ var serializedForm = form.serializeArray();
 42+ var finalObj = {};
 43+
 44+ for (key in serializedForm){
 45+ if(serializedForm[key]['value'] != ""){
 46+ finalObj[serializedForm[key]['name']] = serializedForm[key]['value'];
 47+ }
 48+ }
 49+ return finalObj;
 50+ }
 51+
 52+ /**
 53+ * Sends the formatted ajax request to the API, turns the result into a
 54+ * hidden form, and immediately posts that form on return.
 55+ * Takes the button ID in string form.
 56+ */
 57+ $.goAjax = function(buttonID) {
 58+
 59+ var postData = $.serializeForm(buttonID);
 60+ postData.action = "contributiontracking";
 61+ postData.format = "json";
 62+ //$.debugPostObjectWithAlert(postData);
 63+
 64+ var processAjaxReturn = function(data, status){
 65+ //TODO: Improve the language of the success and error dialogs.
 66+
 67+ if(status != "success"){
 68+ window.alert("Status: " + status);
 69+ $(buttonID).removeAttr("disabled");
 70+ $(".ajax_me:disabled").removeAttr("disabled");
 71+ return;
 72+ }
 73+
 74+ if(data["error"]){
 75+ //TODO: localization. And i18n. And stuff.
 76+ window.alert("The following error has occurred:\r\n" + data["error"]["info"]);
 77+ $(buttonID).removeAttr("disabled");
 78+ $(".ajax_me:disabled").removeAttr("disabled");
 79+ return;
 80+ }
 81+
 82+ if ($('#hideyform').length){
 83+ $('#hideyform').empty(); //just in case something is already hiding in the hideyform.
 84+ } else {
 85+ $('<div id="hideyform"></div>').appendTo('body');
 86+ }
 87+ $('<form id="immediate_repost" action="' + data["returns"]["action"]["url"] + '"></form>').appendTo('#hideyform');
 88+ for (key in data["returns"]["fields"]) {
 89+ $('<input type="hidden" id="' + key +'" name="' + key +'" value="' + data["returns"]["fields"][key] +'">').appendTo('#immediate_repost');
 90+ }
 91+ $('#immediate_repost').submit();
 92+
 93+ }
 94+
 95+ $.post(
 96+ mw.config.get('wgScriptPath') + '/api.php',
 97+ postData,
 98+ processAjaxReturn,
 99+ 'json');
 100+ }
 101+
 102+ /**
 103+ * Just for easy debugging. Should not actually be called anywhere.
 104+ * TODO: Take this out when we know we're done here.
 105+ */
 106+ $.debugPostObjectWithAlert = function(object){
 107+ var contents = "";
 108+ for (key in object){
 109+ contents += key + " = " + object[key] + "\r\n";
 110+ }
 111+ window.alert(contents);
 112+ }
 113+
 114+} )( jQuery );
 115+
 116+$.bindAjaxControls();
\ No newline at end of file
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/modules/jquery.contributionTracking.js
___________________________________________________________________
Added: svn:eol-style
1117 + native
Index: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking_Tester.php
@@ -0,0 +1,67 @@
 2+<?php
 3+
 4+/**
 5+ * This is a page that exists solely for the purpose of manually testing all
 6+ * aspects of the ContributionTracking API: Both send (querystring) and receive
 7+ * (jquery processing and reposting). Could also be used for browser-based
 8+ * regression testing of these components.
 9+ * The form is built with all the fields the API will let through the filter.
 10+ * Required are marked with "***".
 11+ * This page is only visible to sysops.
 12+ */
 13+class ContributionTrackingTester extends UnlistedSpecialPage {
 14+
 15+ function __construct() {
 16+ parent::__construct( 'ContributionTrackingTester', 'ViewContributionTrackingTester' );
 17+ }
 18+
 19+ function execute( $language ) {
 20+ global $wgUser;
 21+ if ( !$this->userCanExecute( $wgUser ) ) {
 22+ $this->displayRestrictionError();
 23+ return;
 24+ }
 25+
 26+ global $wgRequest, $wgOut, $wgContributionTrackingReturnToURLDefault;
 27+
 28+ $wgOut->addModules( 'jquery.contributionTracking' );
 29+
 30+ if ( !preg_match( '/^[a-z-]+$/', $language ) ) {
 31+ $language = 'en';
 32+ }
 33+ $this->lang = Language::factory( $language );
 34+
 35+ $this->setHeaders();
 36+
 37+ $apiObj = new ApiContributionTracking( null, null );
 38+ $formfields = $apiObj->getFinalParams();
 39+
 40+ //$wgOut->addWikiText(print_r($formfields, true));
 41+ $wgOut->addHTML( '<form id="landingpage_submit"><table>' );
 42+
 43+ foreach ( $formfields as $name => $attribs ) {
 44+ if ( array_key_exists( 8, $attribs ) ) {
 45+ $required = "***";
 46+ } else {
 47+ $required = "";
 48+ }
 49+ $wgOut->addHTML( '<tr><td>' . $required . $name . '</td><td>' );
 50+ if ( $attribs[2] == 'string' ) {
 51+ $wgOut->addHTML( '<input type="text" id="' . $name . '" name="' . $name . '">' );
 52+ }
 53+ if ( $attribs[2] == 'boolean' ) {
 54+ $wgOut->addHTML( '<input type="checkbox" id="' . $name . '" name="' . $name . '">' );
 55+ }
 56+
 57+ $wgOut->addHTML( '</td></tr>' );
 58+ }
 59+
 60+ $wgOut->addHTML( '<tr><td colspan=2 align=center><button id="ajax_contribution" class="ajax_me">Fire away!</button></td></tr>' );
 61+ $wgOut->addHTML( '</table></form>' );
 62+ }
 63+
 64+ function msgWiki( $key ) {
 65+ return wfMsgExt( $key, array( 'parse', 'language' => $this->lang ) );
 66+ }
 67+
 68+}
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking/ContributionTracking_Tester.php
___________________________________________________________________
Added: svn:eol-style
169 + native
Property changes on: branches/fundraising/deployment/payments_1.17/extensions/ContributionTracking
___________________________________________________________________
Added: svn:mergeinfo
270 Merged /branches/wmf/1.16wmf4/extensions/ContributionTracking:r67177,69199,76243,77266
371 Merged /trunk/extensions/ContributionTracking:r64690-95543
472 Merged /trunk/phase3/extensions/ContributionTracking:r63545-63546,63549,63643,63764,63897-63901,64113,64509,65387,65391,65555,65590,65650,65816,77555,77558-77560,77563-77565,77573
573 Merged /trunk/extensions/ContributionReporting:r74440-74491
674 Merged /branches/wmf-deployment/extensions/ContributionTracking:r60970

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r97502Creating new WMF branchreedy15:15, 19 September 2011

Status & tagging log