Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
FundraiserRedirector
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 4
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
272
 reload
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 isValidIsoCountryCode
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/*
3 * Provides redirect service for donors coming from external sites (so that they get
4 * directed to the proper form for their country).
5 *
6 * @author Ryan Kaldari <rkaldari@wikimedia.org>
7 * @author Peter Gehres <pgehres@wikimedia.org>
8 */
9
10namespace MediaWiki\Extension\FundraiserLandingPage\Specials;
11
12use MediaWiki\MediaWikiServices;
13use SpecialPage;
14use UnlistedSpecialPage;
15
16class FundraiserRedirector extends UnlistedSpecialPage {
17    public function __construct() {
18        parent::__construct( 'FundraiserRedirector' );
19    }
20
21    /**
22     * @param string $par
23     */
24    public function execute( $par ) {
25        global $wgFundraiserLPDefaults, $wgFundraiserLandingPageChapters;
26
27        // Country passed in the URL param gets first precedence.
28        $country = $this->getRequest()->getVal( 'country' );
29        if ( !self::isValidIsoCountryCode( $country ) ) {
30            $country = '';
31        }
32
33        // Get country from the GeoIP cookie if present.
34        if ( !$country ) {
35            $geoip = $this->getRequest()->getCookie( 'GeoIP', '' );
36            if ( $geoip ) {
37                $components = explode( ':', $geoip );
38                $country = $components[0];
39            }
40        }
41
42        if ( !$country ) {
43            // If country isn't set, try realoding the page (redirecting to the same page
44            // with a 'reloaded' URL param to prevent a loop). This may be necessary if
45            // no GeoIP cookie was previously set for this domain. While our front-end
46            // cache showuld always set a GeoIP cookie, it won't be visible server-side
47            // until it's reflected back by the browser. For details, see T317427.
48            if ( !$this->getRequest()->getBool( 'reloaded' ) ) {
49                $this->reload();
50                return;
51            }
52
53            // Still no country? use the default.
54            $country = $wgFundraiserLPDefaults[ 'country' ];
55        }
56
57        // Set the language parameter
58        $language = $this->getRequest()->getVal( 'uselang' );
59        // If not set, try the browser language
60        if ( !$language ) {
61            $mwLanguages = array_keys( MediaWikiServices::getInstance()->getLanguageNameUtils()->getLanguageNames() );
62            $languages = array_keys( $this->getRequest()->getAcceptLang() );
63            foreach ( $languages as $tryLanguage ) {
64                if ( in_array( $tryLanguage, $mwLanguages ) ) {
65                    // use the language if it is supported in MediaWiki
66                    $language = $tryLanguage;
67                    // don't search further
68                    break;
69                }
70            }
71        }
72
73        $params = [
74            'country' => $country,
75            'uselang' => $language,
76            // set default tracking variables that will be overridden
77            // by anything passed in the query string
78            'wmf_medium' => "spontaneous",
79            'wmf_source' => "fr-redir",
80            'wmf_campaign' => "spontaneous",
81        ];
82
83        // Pass any other params that are set
84        // If we arrived here with a 'reloaded' param, don't pass that along when
85        // redirecting to Special:FundraiserLandingPage.
86        $excludeKeys = [ 'country', 'title', 'reloaded' ];
87        if ( $this->getRequest()->getQueryValuesOnly() ) {
88            foreach ( $this->getRequest()->getQueryValuesOnly() as $key => $value ) {
89                // Skip the required variables
90                if ( !in_array( $key, $excludeKeys ) ) {
91                    // Change any starting with utm_ to wmf_ - T351325
92                    $params[ str_replace( 'utm_', 'wmf_', $key ) ] = $value;
93                }
94            }
95        }
96
97        // set the default redirect
98        $redirectURL = SpecialPage::getTitleFor( 'FundraiserLandingPage' )->getLocalUrl( $params );
99
100        // if the country is covered by a payment-processing chapter, redirect
101        // the donor to the chapter's default landing page
102        if ( array_key_exists( $params['country'], $wgFundraiserLandingPageChapters ) ) {
103            // Get the message key for the chapter's landing page
104            $message_key = $wgFundraiserLandingPageChapters[ $params['country'] ];
105            // Get the url for the chapter's landing page
106            $message = $this->msg( $message_key )->plain();
107            // if the message is not equal to the default message that is returned
108            // for a missing message, set the redirect URL to the message
109            if ( $message != "<$message_key>" ) {
110                $redirectURL = $message;
111
112                if ( strpos( $redirectURL, "LandingCheck" ) !== false ) {
113                    // the chapter is using LandingCheck, so go ahead and send
114                    // all of the params as well
115                    $querystring = http_build_query( $params );
116
117                    if ( strpos( $redirectURL, "?" ) === false ) {
118                        $redirectURL .= "?" . $querystring;
119                    } else {
120                        $redirectURL .= "&" . $querystring;
121                    }
122                }
123            }
124        }
125        // Redirect
126        $this->getOutput()->redirect( $redirectURL );
127    }
128
129    /**
130     * Reload the page by redirecting to the same URL, adding a 'reloaded' URL param,
131     * and preserving other params from the current request.
132     *
133     * @return void
134     */
135    private function reload() {
136        $params = $this->getRequest()->getQueryValuesOnly();
137
138        // Title may be re-added below by getTitleFor()
139        unset( $params[ 'title' ] );
140
141        // 'reloaded' param used to prevent an infinite redirect loop
142        $params[ 'reloaded' ] = 'true';
143
144        $redirectURL = SpecialPage::getTitleFor( 'FundraiserRedirector' )
145            ->getLocalUrl( $params );
146
147        $this->getOutput()->redirect( $redirectURL );
148    }
149
150    /**
151     * Checks to see if $country is a valid iso 3166-1 country code.
152     * DOES NOT VERIFY THAT WE FUNDRAISE THERE. Only that the code makes sense.
153     * @param string $country the code we want to check
154     * @return bool
155     */
156    public static function isValidIsoCountryCode( $country ) {
157        /**
158         * List of valid iso 3166 country codes, regenerated on 1380836686
159         * Code generated by a happy script at
160         * https://gerrit.wikimedia.org/r/#/admin/projects/wikimedia/fundraising/tools,branches
161         */
162        $iso_3166_codes = [
163            'AF', 'AX', 'AL', 'DZ', 'AS', 'AD', 'AO', 'AI', 'AQ', 'AG', 'AR', 'AM', 'AW', 'AU',
164            'AT', 'AZ', 'BS', 'BH', 'BD', 'BB', 'BY', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BQ',
165            'BA', 'BW', 'BV', 'BR', 'IO', 'BN', 'BG', 'BF', 'BI', 'KH', 'CM', 'CA', 'CV', 'KY',
166            'CF', 'TD', 'CL', 'CN', 'CX', 'CC', 'CO', 'KM', 'CG', 'CD', 'CK', 'CR', 'CI', 'HR',
167            'CU', 'CW', 'CY', 'CZ', 'DK', 'DJ', 'DM', 'DO', 'EC', 'EG', 'SV', 'GQ', 'ER', 'EE',
168            'ET', 'FK', 'FO', 'FJ', 'FI', 'FR', 'GF', 'PF', 'TF', 'GA', 'GM', 'GE', 'DE', 'GH',
169            'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT', 'GG', 'GN', 'GW', 'GY', 'HT', 'HM', 'VA',
170            'HN', 'HK', 'HU', 'IS', 'IN', 'ID', 'IR', 'IQ', 'IE', 'IM', 'IL', 'IT', 'JM', 'JP',
171            'JE', 'JO', 'KZ', 'KE', 'KI', 'KP', 'KR', 'KW', 'KG', 'LA', 'LV', 'LB', 'LS', 'LR',
172            'LY', 'LI', 'LT', 'LU', 'MO', 'MK', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ',
173            'MR', 'MU', 'YT', 'MX', 'FM', 'MD', 'MC', 'MN', 'ME', 'MS', 'MA', 'MZ', 'MM', 'NA',
174            'NR', 'NP', 'NL', 'NC', 'NZ', 'NI', 'NE', 'NG', 'NU', 'NF', 'MP', 'NO', 'OM', 'PK',
175            'PW', 'PS', 'PA', 'PG', 'PY', 'PE', 'PH', 'PN', 'PL', 'PT', 'PR', 'QA', 'RE', 'RO',
176            'RU', 'RW', 'BL', 'SH', 'KN', 'LC', 'MF', 'PM', 'VC', 'WS', 'SM', 'ST', 'SA', 'SN',
177            'RS', 'SC', 'SL', 'SG', 'SX', 'SK', 'SI', 'SB', 'SO', 'ZA', 'GS', 'SS', 'ES', 'LK',
178            'SD', 'SR', 'SJ', 'SZ', 'SE', 'CH', 'SY', 'TW', 'TJ', 'TZ', 'TH', 'TL', 'TG', 'TK',
179            'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB', 'US', 'UM', 'UY',
180            'UZ', 'VU', 'VE', 'VN', 'VG', 'VI', 'WF', 'EH', 'YE', 'ZM', 'ZW',
181        ];
182
183        return in_array( $country, $iso_3166_codes );
184    }
185
186}