r95544 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r95543‎ | r95544 | r95545 >
Date:23:20, 25 August 2011
Author:awjrichards
Status:ok
Tags:
Comment:
Modified paths:
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ApiContributionTracking.php (added) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.alias.php (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.i18n.php (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.php (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.processor.php (added) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.sql (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking_Tester.php (added) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking_body.php (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/modules (added) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/patch-owa.sql (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/ContributionTracking/tests (added) (history)

Diff [purge]

Index: branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.alias.php
@@ -33,6 +33,11 @@
3434 'ContributionTracking' => array( 'Slědowanje_pśinoskow' ),
3535 );
3636
 37+/** Esperanto (Esperanto) */
 38+$specialPageAliases['eo'] = array(
 39+ 'ContributionTracking' => array( 'Kontribukontado' ),
 40+);
 41+
3742 /** Persian (فارسی) */
3843 $specialPageAliases['fa'] = array(
3944 'ContributionTracking' => array( 'ردگیری_مشارکت' ),
@@ -45,9 +50,14 @@
4651
4752 /** Galician (Galego) */
4853 $specialPageAliases['gl'] = array(
49 - 'ContributionTracking' => array( 'Seguimento das contribucións' ),
 54+ 'ContributionTracking' => array( 'Seguimento_das_contribucións' ),
5055 );
5156
 57+/** Haitian (Kreyòl ayisyen) */
 58+$specialPageAliases['ht'] = array(
 59+ 'ContributionTracking' => array( 'SwiviKontribisyon' ),
 60+);
 61+
5262 /** Hungarian (Magyar) */
5363 $specialPageAliases['hu'] = array(
5464 'ContributionTracking' => array( 'Adományok_követése' ),
@@ -148,6 +158,11 @@
149159 'ContributionTracking' => array( 'TraciamentoContributi' ),
150160 );
151161
 162+/** Simplified Chinese (‪中文(简体)‬) */
 163+$specialPageAliases['zh-hans'] = array(
 164+ 'ContributionTracking' => array( '跟踪贡献' ),
 165+);
 166+
152167 /**
153168 * For backwards compatibility with MediaWiki 1.15 and earlier.
154169 */
Index: branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.sql
@@ -1,16 +1,18 @@
2 -CREATE TABLE IF NOT EXISTS /*_*/`contribution_tracking` (
3 - `id` int(10) unsigned NOT NULL 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 - PRIMARY KEY (`id`),
15 - UNIQUE KEY `contribution_id` (`contribution_id`),
16 - KEY `ts` (`ts`)
 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
1716 ) /*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
Index: branches/wmf/1.17wmf1/extensions/ContributionTracking/patch-owa.sql
@@ -1,5 +1,5 @@
22 --
33 -- create contribution_tracking.owa_session and owa_ref
44 --
5 -ALTER TABLE /*_*/contribution_tracking ADD owa_session varbinary(255);
6 -ALTER TABLE /*_*/contribution_tracking ADD owa_ref INTEGER;
 5+ALTER TABLE /*_*/contribution_tracking ADD owa_session varbinary(255) default NULL;
 6+ALTER TABLE /*_*/contribution_tracking ADD owa_ref int(11) default NULL;
Index: branches/wmf/1.17wmf1/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/wmf/1.17wmf1/extensions/ContributionTracking/ApiContributionTracking.php
___________________________________________________________________
Added: svn:eol-style
1199 + native
Added: svn:keywords
2200 + Id
Index: branches/wmf/1.17wmf1/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' => $returnTitle->getFullUrl(),
 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' => $returnTitle->getFullUrl(),
 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' => $returnTitle->getFullUrl(), //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' => $returnTitle->getFullUrl(),
 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/wmf/1.17wmf1/extensions/ContributionTracking/tests/ContributionTrackingAPITest.php
___________________________________________________________________
Added: svn:eol-style
1287 + native
Index: branches/wmf/1.17wmf1/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' => $returnTitle->getFullUrl(),
 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' => $returnTitle->getFullURL(), //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' => $returnTitle->getFullURL(),
 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/wmf/1.17wmf1/extensions/ContributionTracking/tests/ContributionTrackingProcessorTest.php
___________________________________________________________________
Added: svn:eol-style
1431 + native
Index: branches/wmf/1.17wmf1/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' => $returnTitle->getFullUrl(),
 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' => $returnTitle->getFullUrl(),
 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' => $returnTitle->getFullUrl(), //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' => $returnTitle->getFullUrl(),
 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/wmf/1.17wmf1/extensions/ContributionTracking/tests/ContributionTrackingTest.php
___________________________________________________________________
Added: svn:eol-style
1332 + native
Index: branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking_body.php
@@ -1,177 +1,87 @@
22 <?php
33
44 class ContributionTracking extends UnlistedSpecialPage {
 5+
56 function __construct() {
67 parent::__construct( 'ContributionTracking' );
78 }
89
9 - function get_owa_ref_id($ref){
10 - // Replication lag means sometimes a new event will not exist in the table yet
11 - $dbw = contributionTrackingConnection(); //wfGetDB( DB_MASTER );
12 - $id_num = $dbw->selectField(
13 - 'contribution_tracking_owa_ref',
14 - 'id',
15 - array( 'url' => $ref ),
16 - __METHOD__
17 - );
18 - // Once we're on mysql 5, we can use replace() instead of this selectField --> insert or update hooey
19 - if ( $id_num === false ) {
20 - $dbw->insert(
21 - 'contribution_tracking_owa_ref',
22 - array( 'url' => (string) $event_name ),
23 - __METHOD__
24 - );
25 - $id_num = $dbw->insertId();
26 - }
27 - return $id_num === false ? 0 : $id_num;
28 - }
29 -
30 -
3110 function execute( $language ) {
32 - global $wgRequest, $wgOut, $wgContributionTrackingPayPalIPN, $wgContributionTrackingReturnToURLDefault,
33 - $wgContributionTrackingPayPalRecurringIPN, $wgContributionTrackingPayPalBusiness;
34 -
 11+ global $wgRequest, $wgOut, $wgContributionTrackingReturnToURLDefault;
 12+
3513 if ( !preg_match( '/^[a-z-]+$/', $language ) ) {
3614 $language = 'en';
3715 }
3816 $this->lang = Language::factory( $language );
39 -
 17+
4018 $this->setHeaders();
41 -
 19+
4220 $gateway = $wgRequest->getText( 'gateway' );
43 - if( !in_array( $gateway, array( 'paypal', 'moneybookers' ) ) ) {
 21+ if ( !in_array( $gateway, array( 'paypal', 'moneybookers' ) ) ) {
4422 $wgOut->showErrorPage( 'contrib-tracking-error', 'contrib-tracking-error-text' );
4523 return;
4624 }
47 -
48 - $db = contributionTrackingConnection();
4925
50 - $ts = $db->timestamp();
51 -
52 - $owa_ref = $wgRequest->getText('owa_ref', null);
53 - if($owa_ref != null && !is_numeric($owa_ref)){
54 - $owa_ref = $this->get_owa_ref_id($owa_ref);
 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 );
5544 }
5645
57 - $tracked_contribution = array(
58 - 'note' => $wgRequest->getText('comment', null),
59 - 'referrer' => $wgRequest->getText('referrer', null),
60 - 'anonymous' => ($wgRequest->getCheck('comment-option', 0) ? 0 : 1),
61 - 'utm_source' => $wgRequest->getText('utm_source', null),
62 - 'utm_medium' => $wgRequest->getText('utm_medium', null),
63 - 'utm_campaign' => $wgRequest->getText('utm_campaign', null),
64 - 'optout' => ($wgRequest->getCheck('email-opt', 0) ? 0 : 1),
65 - 'language' => $wgRequest->getText('language', null),
66 - 'owa_session' => $wgRequest->getText('owa_session', null),
67 - 'owa_ref' => $owa_ref,
68 - 'ts' => $ts,
 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+ 'recurring_paypal' => $wgRequest->getText( 'recurring_paypal' ),
 55+ 'amount' => $wgRequest->getVal( 'amount' ),
 56+ 'amount_given' => $wgRequest->getVal( 'amountGiven' ),
 57+ 'contribution_tracking_id' => $contribution_tracking_id,
 58+ 'language' => $language,
6959 );
70 -
71 - // Make all empty strings NULL
72 - foreach ($tracked_contribution as $key => $value) {
73 - if ($value === '') {
74 - $tracked_contribution[$key] = null;
75 - }
 60+
 61+ if ( $params['tshirt'] ) {
 62+ $params['size'] = $wgRequest->getText( 'size' );
 63+ $params['premium_language'] = $wgRequest->getText( 'premium_language' );
7664 }
77 -
78 - // Store the contribution data
79 - if ( !$wgRequest->getVal( 'contribution_tracking_id', 0 )) {
80 - $db->insert( 'contribution_tracking', $tracked_contribution );
81 - }
82 - $contribution_tracking_id = $wgRequest->getVal( 'contribution_tracking_id', $db->insertId());
83 -
84 - $returnText = $wgRequest->getText( 'returnto', "Donate-thanks/$language" );
85 - $returnTitle = Title::newFromText( $returnText );
86 - if( $returnTitle ) {
87 - $returnto = $returnTitle->getFullUrl();
88 - } else {
89 - $returnto = $wgContributionTrackingReturnToURLDefault . "/$language";
90 - }
91 -
92 - // Set the action and tracking ID fields
93 - $repost = array();
94 - $action = 'http://wikimediafoundation.org/';
95 - $amount_field_name = 'amount'; // the amount fieldname may be different depending on the service
96 - if ( $gateway == 'paypal' ) {
97 -
98 - $action = 'https://www.paypal.com/cgi-bin/webscr';
9965
100 - // Premiums
101 - if ( $wgRequest->getVal( 'tshirt') == '1' ) {
102 - $repost['on0'] = 'Shirt size';
103 - $repost['os0'] = $wgRequest->getText( 'size' );
104 - $repost['on1'] = 'Shirt language';
105 - $repost['os1'] = $wgRequest->getText( 'premium_language' );
106 - $repost['no_shipping'] = 2;
 66+ foreach ( $params as $key => $value ) {
 67+ if ( $value === "" || $value === null ) {
 68+ unset( $params[$key] );
10769 }
108 -
109 - // PayPal
110 - $repost['business'] = $wgContributionTrackingPayPalBusiness;
111 - $repost['item_number'] = 'DONATE';
112 - $repost['no_note'] = '0';
113 - $repost['return'] = $returnto;
114 - $repost['currency_code'] = $wgRequest->getText( 'currency_code', 'USD' );
115 -
116 - // additional fields to pass to PayPal from single-step credit card form
117 - $repost[ 'first_name' ] = $wgRequest->getText( 'fname', null );
118 - $repost[ 'last_name' ] = $wgRequest->getText( 'lname', null );
119 - $repost[ 'email' ] = $wgRequest->getText( 'email', null );
120 -
121 - // if this is a recurring donation, we have add'l fields to send to paypal
122 - if ( $wgRequest->getText( 'recurring_paypal' ) == 'true' ) {
123 - $repost[ 't3' ] = "M"; // The unit of measurement for for p3 (M = month)
124 - $repost[ 'p3' ] = '1'; // Billing cycle duration
125 - $repost[ 'srt' ] = '12'; // # of billing cycles
126 - $repost[ 'src' ] = '1'; // Make this 'recurring'
127 - $repost[ 'sra' ] = '1'; // Turn on re-attempt on failure
128 - $repost[ 'cmd' ] = '_xclick-subscriptions';
129 - $amount_field_name = 'a3';
130 - $repost['notify_url'] = $wgContributionTrackingPayPalRecurringIPN;
131 - $repost['item_name'] = $this->msg( 'contrib-tracking-item-name-recurring' );
132 - } else {
133 - $repost['cmd'] = '_xclick';
134 - $repost['notify_url'] = $wgContributionTrackingPayPalIPN;
135 - $repost['item_name'] = $this->msg( 'contrib-tracking-item-name-onetime' );
136 - }
13770 }
138 - else if ( $gateway == 'moneybookers' ) {
139 - $action = 'https://www.moneybookers.com/app/payment.pl';
14071
141 - // Tracking
142 - $repost['merchant_fields'] = 'os0';
 72+ $repost = ContributionTrackingProcessor::getRepostFields( $params );
14373
144 - // Moneybookers
145 - $repost['pay_to_email'] = 'donation@wikipedia.org';
146 - $repost['status_url'] = 'https://civicrm.wikimedia.org/fundcore_gateway/moneybookers';
147 - $repost['language'] = 'en';
148 - $repost['detail1_description'] = 'One-time donation';
149 - $repost['detail1_text'] = 'DONATE';
150 - $repost['currency'] = $wgRequest->getText( 'currency_code', 'USD' );
151 - } else {
152 - throw new MWException( "This shouldn't happen, we validated the gateway earlier." );
153 - }
154 -
155 - // Normalized amount
156 - $repost[ $amount_field_name ] = $wgRequest->getVal( 'amount' );
157 - if ( $wgRequest->getVal( 'amountGiven' ) ) {
158 - $repost[ $amount_field_name ] = $wgRequest->getVal( 'amountGiven' );
159 - }
160 -
161 - // Tracking
162 - $repost['custom'] = $contribution_tracking_id;
163 -
16474 $wgOut->addWikiText( "{{2009/Donate-banner/$language}}" );
16575 $wgOut->addHTML( $this->msgWiki( 'contrib-tracking-submitting' ) );
166 -
 76+
16777 // Output the repost form
168 - $output = '<form method="post" name="contributiontracking" action="' . $action . '">';
 78+ $output = '<form method="post" name="contributiontracking" action="' . $repost['action'] . '">';
16979
170 - foreach ( $repost as $key => $value ) {
171 - $output .= '<input type="hidden" name="' . htmlspecialchars($key) . '" value="' . htmlspecialchars($value) . '" />';
 80+ foreach ( $repost['fields'] as $key => $value ) {
 81+ $output .= '<input type="hidden" name="' . htmlspecialchars( $key ) . '" value="' . htmlspecialchars( $value ) . '" />';
17282 }
173 -
 83+
17484 $output .= $this->msgWiki( 'contrib-tracking-redirect' );
175 -
 85+
17686 // Offer a button to post the form if the user has no Javascript support
17787 $output .= '<noscript>';
17888 $output .= $this->msgWiki( 'contrib-tracking-continue' );
@@ -184,14 +94,12 @@
18595
18696 // Automatically post the form if the user has Javascript support
18797 $wgOut->addHTML( '<script type="text/javascript">document.contributiontracking.submit();</script>' );
188 -
18998 }
19099
191 - function msg( $key ) {
192 - return wfMsgExt( $key, array( 'escape', 'language' => $this->lang ) );
 100+ function msg() {
 101+ return wfMsgExt( func_get_arg( 0 ), array( 'escape', 'language' => $this->lang ) );
193102 }
194103
195 -
196104 function msgWiki( $key ) {
197105 return wfMsgExt( $key, array( 'parse', 'language' => $this->lang ) );
198106 }
Index: branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.i18n.php
@@ -97,7 +97,14 @@
9898 'contrib-tracking-button' => 'استمر',
9999 );
100100
101 -/** Bashkir (Башҡорт)
 101+/** Azerbaijani (Azərbaycanca)
 102+ * @author Cekli829
 103+ */
 104+$messages['az'] = array(
 105+ 'contrib-tracking-error' => 'Xəta',
 106+);
 107+
 108+/** Bashkir (Башҡортса)
102109 * @author Assele
103110 */
104111 $messages['ba'] = array(
@@ -113,7 +120,7 @@
114121 'contrib-tracking-item-name-recurring' => 'Ай һайын ҡабатланған иғәнә',
115122 );
116123
117 -/** Belarusian (Taraškievica orthography) (Беларуская (тарашкевіца))
 124+/** Belarusian (Taraškievica orthography) (‪Беларуская (тарашкевіца)‬)
118125 * @author EugeneZelenko
119126 * @author Jim-by
120127 * @author Red Winged Duck
@@ -145,6 +152,7 @@
146153 'contrib-tracking-submitting' => 'Изпращане за обработка на плащането...',
147154 'contrib-tracking-continue' => 'Ако не бъдете пренасочени автоматично, натиснете бутона, за да завършите дарението през PayPal.',
148155 'contrib-tracking-button' => 'Продължаване',
 156+ 'contrib-tracking-item-name-onetime' => 'Еднократно дарение',
149157 );
150158
151159 /** Bengali (বাংলা)
@@ -152,10 +160,14 @@
153161 * @author Wikitanvir
154162 */
155163 $messages['bn'] = array(
 164+ 'contributiontracking' => 'অনুদান অনুসরণ',
156165 'contrib-tracking-error' => 'ত্রুটি',
 166+ 'contrib-tracking-error-text' => 'অগ্রহণযোগ্য ফর্ম জমা',
157167 'contrib-tracking-submitting' => 'পেমেন্ট প্রসেসরের কাছে প্রদান করা হচ্ছে...',
158168 'contrib-tracking-continue' => 'আপনাকে যদি স্বয়ংক্রিয়ভাবে পুনর্নির্দেশিত করা না হয়, তবে পেপ্যাল-এ আপনার অনুদান সম্পন্ন করতে নিচের বাটনে ক্লিক করুন।',
 169+ 'contrib-tracking-redirect' => 'অনুদান সম্পূর্ণ করতে আপনাকে স্বয়ংক্রিয়ভাবে পেপ্যালে পুনর্নির্দেশ করা হবে।',
159170 'contrib-tracking-button' => 'অগ্রসর হোন',
 171+ 'contrib-tracking-item-name-onetime' => 'একক-সময় অনুদান',
160172 );
161173
162174 /** Breton (Brezhoneg)
@@ -239,11 +251,13 @@
240252 'contrib-tracking-item-name-recurring' => 'Monatlich wiederholende Spende',
241253 );
242254
243 -/** German (formal address) (Deutsch (Sie-Form))
 255+/** German (formal address) (‪Deutsch (Sie-Form)‬)
244256 * @author Imre
 257+ * @author Kghbln
245258 */
246259 $messages['de-formal'] = array(
247 - 'contrib-tracking-continue' => 'Wenn Sie nicht automatisch weitergeleitet werden, klicken Sie bitte auf „Weiter“, um Ihre Spende über PayPal abzuschließen.',
 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.',
248262 );
249263
250264 /** Zazaki (Zazaki)
@@ -304,7 +318,10 @@
305319 'contrib-tracking-error-text' => 'Malvalida enigo',
306320 'contrib-tracking-submitting' => 'Sendante al mona traktejo...',
307321 '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.',
308323 'contrib-tracking-button' => 'Daŭrigi',
 324+ 'contrib-tracking-item-name-onetime' => 'Unuopa donaco',
 325+ 'contrib-tracking-item-name-recurring' => 'Ĉiumonata donaco',
309326 );
310327
311328 /** Spanish (Español)
@@ -401,6 +418,7 @@
402419 'contrib-tracking-submitting' => 'Èxpèdicion u sistèmo de trètament des payements...',
403420 'contrib-tracking-continue' => 'Se vos éte pas redirigiê ôtomaticament, clicâd cél boton por complètar voutron don avouéc PayPal.',
404421 'contrib-tracking-button' => 'Continuar',
 422+ 'contrib-tracking-item-name-onetime' => 'Don solèt',
405423 );
406424
407425 /** Friulian (Furlan)
@@ -569,8 +587,10 @@
570588 );
571589
572590 /** Italian (Italiano)
 591+ * @author Beta16
573592 * @author BrokenArrow
574593 * @author Darth Kule
 594+ * @author Rippitippi
575595 */
576596 $messages['it'] = array(
577597 'contributiontracking-desc' => 'Monitoraggio donazioni per la raccolta fondi Wikimedia',
@@ -579,7 +599,10 @@
580600 'contrib-tracking-error-text' => "Errore nell'invio del modulo",
581601 'contrib-tracking-submitting' => 'Invio al gestore del pagamento...',
582602 '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.',
583604 'contrib-tracking-button' => 'Continua',
 605+ 'contrib-tracking-item-name-onetime' => 'Donazione singola',
 606+ 'contrib-tracking-item-name-recurring' => 'Donazione ricorrente mensile',
584607 );
585608
586609 /** Japanese (日本語)
@@ -630,10 +653,15 @@
631654
632655 /** Khmer (ភាសាខ្មែរ)
633656 * @author Thearith
 657+ * @author គីមស៊្រុន
634658 */
635659 $messages['km'] = array(
636660 'contrib-tracking-error' => 'កំហុស',
 661+ 'contrib-tracking-continue' => 'ប្រសិនបើអ្នកមិនត្រូវបានបញ្ជូនបន្តដោយស្វ័យប្រវត្តិទេ សូចចុចប៊ូតុងដើម្បីសំរេចការបរិច្ចាគរបស់អ្នកនៅ PayPal។',
 662+ 'contrib-tracking-redirect' => 'អ្នកនឹងត្រូវបញ្ជូនបន្តដោយស្វ័យប្រវត្តិទៅកាន់ PayPal ដើម្បីសំរេចការបរិច្ចាគរបស់អ្នក។',
637663 'contrib-tracking-button' => 'បន្ត',
 664+ 'contrib-tracking-item-name-onetime' => 'ការបរិច្ចាគសំរាប់តែមួយលើក',
 665+ 'contrib-tracking-item-name-recurring' => 'ការបរិច្ចាគប្រចាំខែ',
638666 );
639667
640668 /** Korean (한국어)
@@ -656,14 +684,17 @@
657685 $messages['ksh'] = array(
658686 'contributiontracking-desc' => 'Spende Verfollje för de Wikimedija Shtefftung iere Jeld-Sammel-Kampanje',
659687 'contributiontracking' => 'Spende Verfollje',
660 - 'contrib-tracking-error' => 'Fahler',
 688+ 'contrib-tracking-error' => 'Fähler',
661689 'contrib-tracking-error-text' => 'Unjöltijje Date övverdraare för dat Fommulaa',
662690 'contrib-tracking-submitting' => 'Övverjäve aan dä <i lang="en">Provider</i> för et Bezahle&nbsp;...',
663691 '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.',
664693 'contrib-tracking-button' => 'Wigger',
 694+ 'contrib-tracking-item-name-onetime' => 'Eimohl_Spänd',
 695+ 'contrib-tracking-item-name-recurring' => 'Moonatlesch_Spänd',
665696 );
666697
667 -/** Cornish (Kernewek)
 698+/** Cornish (Kernowek)
668699 * @author Kw-Moon
669700 */
670701 $messages['kw'] = array(
@@ -758,6 +789,7 @@
759790 );
760791
761792 /** Malay (Bahasa Melayu)
 793+ * @author Anakmalaysia
762794 * @author Aviator
763795 */
764796 $messages['ms'] = array(
@@ -767,9 +799,19 @@
768800 'contrib-tracking-error-text' => 'Borang yang diserahkan tidak sah',
769801 'contrib-tracking-submitting' => 'Borang sedang diserahkan kepada pemproses pembayaran...',
770802 'contrib-tracking-continue' => 'Sekiranya anda tidak dialihkan secara automatik, klik di sini untuk menyempurnakan derma anda di PayPal.',
771 - 'contrib-tracking-button' => 'Terus',
 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',
772807 );
773808
 809+/** Erzya (Эрзянь)
 810+ * @author Botuzhaleny-sodamo
 811+ */
 812+$messages['myv'] = array(
 813+ 'contrib-tracking-button' => 'Поладомс',
 814+);
 815+
774816 /** Nahuatl (Nāhuatl)
775817 * @author Fluence
776818 */
@@ -851,6 +893,14 @@
852894 'contrib-tracking-button' => 'Contunhar',
853895 );
854896
 897+/** Oriya (ଓଡ଼ିଆ)
 898+ * @author Odisha1
 899+ */
 900+$messages['or'] = array(
 901+ 'contrib-tracking-error' => 'ତ୍ରୁଟି',
 902+ 'contrib-tracking-button' => 'ଚାଲୁରଖ',
 903+);
 904+
855905 /** Deitsch (Deitsch)
856906 * @author Xqt
857907 */
@@ -877,6 +927,7 @@
878928 );
879929
880930 /** Piedmontese (Piemontèis)
 931+ * @author Borichèt
881932 * @author Dragonòt
882933 */
883934 $messages['pms'] = array(
@@ -886,10 +937,10 @@
887938 'contrib-tracking-error-text' => 'Spedission ëd forma nen bon-a',
888939 'contrib-tracking-submitting' => 'Spedission al motor ëd pagament ...',
889940 'contrib-tracking-continue' => "S'it ses pa rediressionà automaticament, sgnaca ël boton për completé toa donassion a PayPal.",
890 - 'contrib-tracking-redirect' => 'It saras rediressionà automaticament su PayPal për completé toa donassion.',
 941+ 'contrib-tracking-redirect' => 'A sarà anviarà automaticament su PayPal për completé soa donassion.',
891942 'contrib-tracking-button' => 'Continua',
892943 'contrib-tracking-item-name-onetime' => 'Donassion sìngola',
893 - 'contrib-tracking-item-name-recurring' => 'Donassion recorenta mensil',
 944+ 'contrib-tracking-item-name-recurring' => 'Donassion arcorenta mensil',
894945 );
895946
896947 /** Pashto (پښتو)
@@ -940,6 +991,7 @@
941992 * @author Stelistcristi
942993 */
943994 $messages['ro'] = array(
 995+ 'contributiontracking' => 'Urmărirea contribuțiilor',
944996 'contrib-tracking-error' => 'Eroare',
945997 'contrib-tracking-error-text' => 'Trimiterea formularului este invalidă',
946998 'contrib-tracking-submitting' => 'Trimitere la procesorul de plată...',
@@ -983,7 +1035,7 @@
9841036 'contrib-tracking-button' => 'Продовжовати',
9851037 );
9861038
987 -/** Yakut (Саха тыла)
 1039+/** Sakha (Саха тыла)
9881040 * @author HalanTul
9891041 */
9901042 $messages['sah'] = array(
@@ -993,7 +1045,10 @@
9941046 'contrib-tracking-error-text' => 'Форма сыыһа сигэнэр',
9951047 'contrib-tracking-submitting' => 'Төлөбүрү таҥастааччыга ыытыы...',
9961048 'contrib-tracking-continue' => 'Аптамаатынан наадалаах сиргэ тиийбэтэх буоллаххына, тимэҕи баттаан PayPal тиһилигин көмөтүнэн сиэртибэлээһиҥҥин түмүктээ.',
 1049+ 'contrib-tracking-redirect' => 'Сиэртибэлээһиҥҥин түмүктүүргэ аптамаатынан PayPal саайтыгар утаарыллыаҥ.',
9971050 'contrib-tracking-button' => 'Салгыы',
 1051+ 'contrib-tracking-item-name-onetime' => 'Биир төлөбүрүнэн',
 1052+ 'contrib-tracking-item-name-recurring' => 'Ый аайы төлөбүр',
9981053 );
9991054
10001055 /** Sinhala (සිංහල)
@@ -1013,7 +1068,10 @@
10141069 'contrib-tracking-error-text' => 'Zaslaný neplatný obsah formulára',
10151070 'contrib-tracking-submitting' => 'Odosiela sa na spracovanie platieb...',
10161071 '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.',
10171073 'contrib-tracking-button' => 'Pokračovať',
 1074+ 'contrib-tracking-item-name-onetime' => 'Jednorazový dar',
 1075+ 'contrib-tracking-item-name-recurring' => 'Opakovaný mesačný dar',
10181076 );
10191077
10201078 /** Slovenian (Slovenščina)
@@ -1032,7 +1090,7 @@
10331091 'contrib-tracking-item-name-recurring' => 'Ponavljajoči se mesečni prispevki',
10341092 );
10351093
1036 -/** Serbian Cyrillic ekavian (Српски (ћирилица))
 1094+/** Serbian Cyrillic ekavian (‪Српски (ћирилица)‬)
10371095 * @author Јованвб
10381096 */
10391097 $messages['sr-ec'] = array(
@@ -1040,7 +1098,7 @@
10411099 'contrib-tracking-button' => 'Настави',
10421100 );
10431101
1044 -/** Serbian Latin ekavian (Srpski (latinica))
 1102+/** Serbian Latin ekavian (‪Srpski (latinica)‬)
10451103 * @author Michaello
10461104 */
10471105 $messages['sr-el'] = array(
@@ -1050,6 +1108,7 @@
10511109
10521110 /** Swedish (Svenska)
10531111 * @author Boivie
 1112+ * @author Tobulos1
10541113 */
10551114 $messages['sv'] = array(
10561115 'contributiontracking-desc' => 'Bidragsspårning för Wikimedia-insamlingen',
@@ -1058,7 +1117,10 @@
10591118 'contrib-tracking-error-text' => 'Ogiltigt skickande av formulär',
10601119 'contrib-tracking-submitting' => 'Skickar till betalningshanterare...',
10611120 '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.',
10621122 'contrib-tracking-button' => 'Fortsätt',
 1123+ 'contrib-tracking-item-name-onetime' => 'Engångs-donation',
 1124+ 'contrib-tracking-item-name-recurring' => 'Återkommande månatlig donation',
10631125 );
10641126
10651127 /** Swahili (Kiswahili) */
@@ -1100,12 +1162,13 @@
11011163 );
11021164
11031165 /** Thai (ไทย)
 1166+ * @author Korrawit
11041167 * @author Woraponboonkerd
11051168 */
11061169 $messages['th'] = array(
11071170 'contrib-tracking-error' => 'เกิดความผิดพลาด',
11081171 'contrib-tracking-submitting' => 'กำลังส่งไปยังผู้ดำเนินการจ่ายเงิน...',
1109 - 'contrib-tracking-continue' => 'ถ้าคุณยังไม่ได้ถูกนำทางไปหน้าอื่นโดยอัติโนมัติ คลิกที่ปุ่มเพื่อเสร็จสิ้นการบริจาคที่ PayPal',
 1172+ 'contrib-tracking-continue' => 'ถ้าคุณยังไม่ได้ถูกนำทางไปหน้าอื่นโดยอัตโนมัติ คลิกที่ปุ่มเพื่อเสร็จสิ้นการบริจาคที่ PayPal',
11101173 'contrib-tracking-button' => 'ดำเนินการต่อ',
11111174 );
11121175
@@ -1135,6 +1198,8 @@
11361199 'contrib-tracking-continue' => 'Kapag hindi ka kusang naituro/awtomatikong naihatid, pindutin ang pindutan upang mabuo ang iyong ambag/abuloy doon sa PayPal.',
11371200 'contrib-tracking-redirect' => 'Automatiko kang ikakarga sa PayPal upang makumpleto ang iyong pagkakaloob.',
11381201 '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',
11391204 );
11401205
11411206 /** Turkish (Türkçe)
@@ -1233,21 +1298,32 @@
12341299 * @author Wilsonmess
12351300 */
12361301 $messages['zh-hans'] = array(
 1302+ 'contributiontracking-desc' => '跟踪维基媒体筹款的贡献',
 1303+ 'contributiontracking' => '跟踪的贡献',
12371304 'contrib-tracking-error' => '错误',
12381305 'contrib-tracking-error-text' => '无效的表单提交',
12391306 'contrib-tracking-submitting' => '正在提交到支付处理器……',
12401307 'contrib-tracking-continue' => '若阁下没有被重定向至PayPal页面,请点击按钮以完成您的捐助。',
 1308+ 'contrib-tracking-redirect' => '您将被自动重定向到 PayPal 来完成你的捐款。',
12411309 'contrib-tracking-button' => '继续',
12421310 'contrib-tracking-item-name-onetime' => '一次性捐赠',
 1311+ 'contrib-tracking-item-name-recurring' => '每月定期捐款',
12431312 );
12441313
12451314 /** Traditional Chinese (‪中文(繁體)‬)
12461315 * @author Liangent
 1316+ * @author Mark85296341
12471317 */
12481318 $messages['zh-hant'] = array(
 1319+ 'contributiontracking-desc' => '追蹤維基媒體籌款的貢獻',
 1320+ 'contributiontracking' => '追蹤的貢獻',
12491321 'contrib-tracking-error' => '錯誤',
12501322 'contrib-tracking-error-text' => '無效的表單提交',
12511323 'contrib-tracking-submitting' => '正在提交到支付處理器……',
 1324+ 'contrib-tracking-continue' => '若閣下沒有被重定向至 PayPal 頁面,請點擊按鈕以完成您的捐助。',
 1325+ 'contrib-tracking-redirect' => '您將被自動重定向到 PayPal 來完成你的捐款。',
12521326 'contrib-tracking-button' => '繼續',
 1327+ 'contrib-tracking-item-name-onetime' => '一次性捐助',
 1328+ 'contrib-tracking-item-name-recurring' => '每月定期捐款',
12531329 );
12541330
Index: branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.processor.php
@@ -0,0 +1,423 @@
 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+ );
 259+ }
 260+
 261+ /**
 262+ * Merges an array of parameters from a payment form, with an array of
 263+ * default values. Additionally: Values in the $params array will only be
 264+ * returned if there is a corresponding key in the $defaults array.
 265+ * @param array $params Form / API data
 266+ * @param array $defaults A set of default values for a particular
 267+ * transaction type
 268+ * @param boolean $nullify If true, keys with empty string values will be
 269+ * set to null in the return array.
 270+ * @return array
 271+ */
 272+ static function mergeArrayDefaults( $params, $defaults, $nullify=false ) {
 273+ $ret = $defaults;
 274+ foreach ( $ret as $key => $value ) {
 275+ if ( array_key_exists( $key, $params ) ) {
 276+ $ret[$key] = $params[$key];
 277+ }
 278+ if ( $nullify && $ret[$key] === '' ) {
 279+ $ret[$key] = null;
 280+ }
 281+ }
 282+ return $ret;
 283+ }
 284+
 285+ /**
 286+ * Takes staged transaction data, and constructs the key/value pairs
 287+ * formatted to be reposted to the gateway specified in $input['gateway']
 288+ * @global string $wgContributionTrackingPayPalBusiness 'Business' string
 289+ * for PayPal: Defined in ContributionTracking.php
 290+ * @global string $wgContributionTrackingReturnToURLDefault Default URL to
 291+ * return to after the transaction was processed by the gateway. Used if
 292+ * none supplied.
 293+ * @param array $input The staged data to repost to a gateway.
 294+ * @return array Key/value pairs, ready to be reposted to the specified
 295+ * gateway to complete the transaction.
 296+ */
 297+ static function getRepostFields( $input ) {
 298+ global $wgContributionTrackingPayPalBusiness, $wgContributionTrackingReturnToURLDefault;
 299+ // Set the action and tracking ID fields
 300+ $input = ContributionTrackingProcessor::stage_repost( $input );
 301+
 302+ $repost = array( );
 303+ $repost['action'] = 'http://wikimediafoundation.org/';
 304+ $amount_field_name = 'amount'; // the amount fieldname may be different depending on the service
 305+ if ( $input['gateway'] == 'paypal' ) {
 306+
 307+ $repost['action'] = 'https://www.paypal.com/cgi-bin/webscr';
 308+
 309+ // Premiums
 310+ if ( array_key_exists( 'tshirt', $input ) && $input['tshirt'] ) {
 311+ $repost['fields']['on0'] = 'Shirt size';
 312+ $repost['fields']['os0'] = $input['size'];
 313+ $repost['fields']['on1'] = 'Shirt language';
 314+ $repost['fields']['os1'] = $input['premium_language'];
 315+ $repost['fields']['no_shipping'] = 2;
 316+ }
 317+
 318+ // PayPal
 319+ $repost['fields']['business'] = $wgContributionTrackingPayPalBusiness;
 320+ $repost['fields']['item_number'] = 'DONATE';
 321+ $repost['fields']['no_note'] = '0';
 322+
 323+ $returnText = $input['return'];
 324+ $returnTitle = Title::newFromText( $returnText );
 325+ if ( $returnTitle ) {
 326+ $returnto = $returnTitle->getFullUrl();
 327+ } else {
 328+ $returnto = $wgContributionTrackingReturnToURLDefault . "/$language";
 329+ }
 330+ $repost['fields']['return'] = $returnto;
 331+ $repost['fields']['currency_code'] = $input['currency_code'];
 332+
 333+ // additional fields to pass to PayPal from single-step credit card form
 334+ if ( array_key_exists( 'fname', $input ) && !empty( $input['fname'] ) ) {
 335+ $repost['fields']['first_name'] = $input['fname'];
 336+ }
 337+ if ( array_key_exists( 'lname', $input ) && !empty( $input['lname'] ) ) {
 338+ $repost['fields']['last_name'] = $input['lname'];
 339+ }
 340+ if ( array_key_exists( 'email', $input ) && !empty( $input['email'] ) ) {
 341+ $repost['fields']['email'] = $input['email'];
 342+ }
 343+
 344+ // if this is a recurring donation, we have add'l fields to send to paypal
 345+ if ( $input['recurring_paypal'] && $input['recurring_paypal'] != 0 ) {
 346+
 347+ $repost['fields']['t3'] = "M"; // The unit of measurement for for p3 (M = month)
 348+ $repost['fields']['p3'] = '1'; // Billing cycle duration
 349+ $repost['fields']['srt'] = '12'; // # of billing cycles
 350+ $repost['fields']['src'] = '1'; // Make this 'recurring'
 351+ $repost['fields']['sra'] = '1'; // Turn on re-attempt on failure
 352+ $repost['fields']['cmd'] = '_xclick-subscriptions';
 353+ $amount_field_name = 'a3';
 354+ $repost['fields']['notify_url'] = $input['notify_url'];
 355+ $repost['fields']['item_name'] = $input['item_name'];
 356+ } else {
 357+ $repost['fields']['cmd'] = '_xclick';
 358+ $repost['fields']['notify_url'] = $input['notify_url'];
 359+ $repost['fields']['item_name'] = $input['item_name'];
 360+ }
 361+ } else if ( $input['gateway'] == 'moneybookers' ) {
 362+ $repost['action'] = 'https://www.moneybookers.com/app/payment.pl';
 363+
 364+ // Tracking
 365+ $repost['fields']['merchant_fields'] = 'os0';
 366+
 367+ // Moneybookers
 368+ $repost['fields']['pay_to_email'] = 'donation@wikipedia.org';
 369+ $repost['fields']['status_url'] = 'https://civicrm.wikimedia.org/fundcore_gateway/moneybookers';
 370+ $repost['fields']['language'] = 'en';
 371+ $repost['fields']['detail1_description'] = 'One-time donation';
 372+ $repost['fields']['detail1_text'] = 'DONATE';
 373+ $repost['fields']['currency'] = $input['currency_code'];
 374+ } else {
 375+ throw new MWException( "Unknown payment gateway!" );
 376+ }
 377+
 378+ // Normalized amount
 379+ $repost['fields'][$amount_field_name] = $input['amount'];
 380+ if ( $input['amount_given'] ) {
 381+ $repost['fields'][$amount_field_name] = $input['amount_given'];
 382+ }
 383+
 384+ // Tracking
 385+ $repost['fields']['custom'] = $input['contribution_tracking_id'];
 386+
 387+ return $repost;
 388+ }
 389+
 390+ /**
 391+ * Sets any language that is expressly specified in the posted parameters.
 392+ * If no language is expressly set, it gets the global language code.
 393+ * @global Language $wgLang
 394+ * @staticvar string $language The language code currently in use
 395+ * @param array $params Request parameters that may or may not contain a
 396+ * 'language' key.
 397+ * @return string A valid language code
 398+ */
 399+ static function getLanguage( $params = '' ) {
 400+ static $language = '';
 401+
 402+ if ( is_array( $params ) && array_key_exists( 'language', $params ) && $params['language'] != null ) {
 403+ //set/reset if something inteligable got sent.
 404+ $language = $params['language'];
 405+ }
 406+
 407+ if ( $language == '' ) { //if we have nothing by this point...
 408+ global $wgLang;
 409+ $language = $wgLang->getCode();
 410+ }
 411+
 412+ return $language;
 413+ }
 414+
 415+ /**
 416+ * Gets a message in the local language
 417+ * @param string $key Message key
 418+ * @return string translated message
 419+ */
 420+ static function msg( $key ) {
 421+ return wfMsgExt( $key, array( 'escape', 'language' => ContributionTrackingProcessor::getLanguage() ) );
 422+ }
 423+
 424+}
Property changes on: branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.processor.php
___________________________________________________________________
Added: svn:eol-style
1425 + native
Index: branches/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking.php
@@ -23,12 +23,34 @@
2424 $wgExtensionAliasesFiles['ContributionTracking'] = $dir . 'ContributionTracking.alias.php';
2525 $wgAutoloadClasses['ContributionTracking'] = $dir . 'ContributionTracking_body.php';
2626 $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+
2738 //this only works if contribution tracking is inside a mediawiki DB, which typically it isn't.
28 -//$wgHooks['LoadExtensionSchemaUpdates'][] = 'efContributionTrackingLoadUpdates';
 39+$wgHooks['LoadExtensionSchemaUpdates'][] = 'efContributionTrackingLoadUpdates';
2940
 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+
3052 /**
3153 * The default 'return to' URL for a thank you page after posting to the contribution
32 - *
 54+ *
3355 * NO trailing slash, please
3456 */
3557 $wgContributionTrackingReturnToURLDefault = 'http://wikimediafoundation.org/wiki/Thank_You';
@@ -49,61 +71,65 @@
5072 $wgContributionTrackingPayPalRecurringIPN = 'https://civicrm.wikimedia.org/fundcore_gateway/paypal';
5173
5274 /**
53 - * 'Business' string for PayPal
 75+ * 'Business' string for PayPal
5476 */
5577 $wgContributionTrackingPayPalBusiness = 'donations@wikimedia.org';
5678
57 -function efContributionTrackingLoadUpdates(){
58 - global $wgExtNewTables, $wgExtNewFields;
59 - $dir = dirname( __FILE__ ) . '/';
60 - $wgExtNewTables[] = array( 'contribution_tracking', $dir . 'ContributionTracking.sql' );
61 - $wgExtNewTables[] = array( 'contribution_tracking_owa_ref', $dir . 'ContributionTracking_OWA_ref.sql' );
62 -
63 - $wgExtNewFields[] = array(
64 - 'contribution_tracking',
65 - 'owa_session',
66 - $dir . 'patch-owa.sql',
67 - );
68 - return true;
69 -
 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;
7087 }
7188
72 - //convert a referrer URL to an index in the owa_ref table
73 -function ef_contribution_tracking_owa_get_ref_id($ref){
74 - // Replication lag means sometimes a new event will not exist in the table yet
75 - $dbw = contributionTrackingConnection();
76 - $id_num = $dbw->selectField(
77 - 'contribution_tracking_owa_ref',
78 - 'id',
79 - array( 'url' => $ref ),
80 - __METHOD__
81 - );
82 - // Once we're on mysql 5, we can use replace() instead of this selectField --> insert or update hooey
83 - if ( $id_num === false ) {
84 - $dbw->insert(
85 - 'contribution_tracking_owa_ref',
86 - array( 'url' => (string) $ref ),
87 - __METHOD__
88 - );
89 - $id_num = $dbw->insertId();
90 - }
91 - return $id_num === false ? 0 : $id_num;
92 - }
 89+// api modules
 90+$wgAPIModules['contributiontracking'] = 'ApiContributionTracking';
9391
94 -function contributionTrackingConnection() {
95 - global $wgContributionTrackingDBserver, $wgContributionTrackingDBname;
96 - global $wgContributionTrackingDBuser, $wgContributionTrackingDBpassword;
 92+/**
 93+ * @param $updater DatabaseUpdater
 94+ * @return bool
 95+ */
 96+function efContributionTrackingLoadUpdates( $updater = null ){
9797
98 - static $db;
 98+ $dir = dirname( __FILE__ ) . '/';
 99+ if ( $updater === null ) {
 100+ global $wgExtNewTables, $wgExtNewFields;
99101
100 - if ( !$db ) {
101 - $db = new DatabaseMysql(
102 - $wgContributionTrackingDBserver,
103 - $wgContributionTrackingDBuser,
104 - $wgContributionTrackingDBpassword,
105 - $wgContributionTrackingDBname );
106 - $db->query( "SET names utf8" );
 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+ }
107133 }
 134+ return true;
108135
109 - return $db;
110136 }
Index: branches/wmf/1.17wmf1/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/wmf/1.17wmf1/extensions/ContributionTracking/modules/jquery.contributionTracking.js
___________________________________________________________________
Added: svn:eol-style
1117 + native
Index: branches/wmf/1.17wmf1/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/wmf/1.17wmf1/extensions/ContributionTracking/ContributionTracking_Tester.php
___________________________________________________________________
Added: svn:eol-style
169 + native
Property changes on: branches/wmf/1.17wmf1/extensions/ContributionTracking
___________________________________________________________________
Modified: svn:mergeinfo
270 Merged /trunk/extensions/ContributionTracking:r78146,78839-95543

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r78137Followup r78118: $wgUpgradeKey was already in DefaultSettings, use Tim's bett...demon20:36, 9 December 2010
r95540Followup r95525, fix failing parser tests...reedy22:28, 25 August 2011

Status & tagging log