Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
FundraiserLandingPage
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 7
552
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 / 52
0.00% covered (danger)
0.00%
0 / 1
110
 fundraiserLandingPageMakeSafe
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 fundraiserLandingPageSwitchLanguage
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 fundraiserLandingPageSwitchCountry
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 isFundraiseUp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFundraiseUpJavascript
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\FundraiserLandingPage\Specials;
4
5/*
6 * SpecialPage definition for FundraiserLandingPage.  Extending UnlistedSpecialPage
7 * since this page does not need to listed in Special:SpecialPages.
8 *
9 * @author Peter Gehres <pgehres@wikimedia.org>
10 */
11
12use Parser;
13use Title;
14use UnlistedSpecialPage;
15
16class FundraiserLandingPage extends UnlistedSpecialPage {
17    public function __construct() {
18        parent::__construct( 'FundraiserLandingPage' );
19    }
20
21    /**
22     * @param string $par
23     */
24    public function execute( $par ) {
25        global $wgFundraiserLPDefaults, $wgFundraiserLandingPageMaxAge,
26            $wgContributionTrackingFundraiserMaintenance,
27            $wgContributionTrackingFundraiserMaintenanceUnsched;
28
29        if ( $wgContributionTrackingFundraiserMaintenance
30            || $wgContributionTrackingFundraiserMaintenanceUnsched
31        ) {
32            $this->getOutput()->redirect(
33                Title::newFromText( 'Special:FundraiserMaintenance' )->getFullURL(), '302'
34            );
35        }
36
37        $out = $this->getOutput();
38        $request = $this->getRequest();
39
40        // Set squid age
41        $out->setCdnMaxage( $wgFundraiserLandingPageMaxAge );
42
43        if ( $this->isFundraiseUp() ) {
44            $out->addScript( $this->getFundraiseUpJavascript() );
45        }
46        $this->setHeaders();
47
48        // set the page title to something useful
49        $titleMsg = $this->msg( 'donate_interface-make-your-donation' );
50        if ( !is_callable( [ $out, 'setPageTitleMsg' ] ) ) {
51            // Backward compatibility with MW < 1.41
52            $out->setPageTitle( $titleMsg );
53        } else {
54            // MW >= 1.41
55            $out->setPageTitleMsg( $titleMsg );
56        }
57
58        // and add a <meta name="description"> tag to give search engines a useful blurb
59        $out->addMeta( 'description', $this->msg( 'fundraiserlandingpage-meta-description' ) );
60
61        // And mark the page as allowed for search engine indexing (default for SpecialPages is noindex)
62        $out->setIndexPolicy( 'index' );
63
64        // clear output variable to be safe
65        $output = '';
66
67        // begin generating the template call
68        $template = self::fundraiserLandingPageMakeSafe(
69            $request->getText( 'template', $wgFundraiserLPDefaults[ 'template' ] )
70        );
71        $output .= "{{ $template\n";
72
73        // get the required variables (except template and country) to use for the landing page
74        $requiredParams = [
75            'appeal',
76            'appeal-template',
77            'form-template',
78            'form-countryspecific'
79        ];
80        foreach ( $requiredParams as $requiredParam ) {
81            $param = self::fundraiserLandingPageMakeSafe(
82                $request->getText( $requiredParam, $wgFundraiserLPDefaults[$requiredParam] )
83            );
84            // Add them to the template call
85            $output .= "$requiredParam = $param\n";
86        }
87
88        // get the country code
89        $country = $request->getVal( 'country' );
90        // If country still isn't set, set it to the default
91        if ( !$country ) {
92            $country = $wgFundraiserLPDefaults[ 'country' ];
93        }
94        $country = self::fundraiserLandingPageMakeSafe( $country );
95        $output .= "| country = $country\n";
96
97        // @phan-suppress-next-line PhanUselessBinaryAddRight
98        $excludeKeys = $requiredParams + [ 'template', 'country', 'title' ];
99
100        // if there are any other parameters passed in the querystring, add them
101        if ( $request->getQueryValuesOnly() ) {
102            foreach ( $request->getQueryValuesOnly() as $k_unsafe => $v_unsafe ) {
103                // skip the required variables
104                if ( in_array( $k_unsafe, $excludeKeys ) ) {
105                    continue;
106                }
107                // get the variable's name and value
108                $key = self::fundraiserLandingPageMakeSafe( $k_unsafe );
109                $val = self::fundraiserLandingPageMakeSafe( $v_unsafe );
110                // print to the template in wiki-syntax
111                $output .= "$key = $val\n";
112            }
113        }
114
115        // close the template call
116        $output .= "}}";
117
118        // Hijack parser internals to workaround T156184.  This should be safe
119        // since we've sanitized all params.
120        $parserOptions = $out->parserOptions();
121        $parserOptions->setAllowUnsafeRawHtml( true );
122
123        // print the output to the page
124        $out->addWikiTextAsInterface( $output );
125    }
126
127    /**
128     * This function limits the possible characters passed as template keys and
129     * values to letters, numbers, hyphens, underscores, and the forward slash.
130     * The function also performs standard escaping of the passed values.
131     *
132     * @param mixed $value The unsafe value to escape and check for invalid characters
133     * @param string $default A default value to return if when making the $string safe no
134     *                 results are returned.
135     *
136     * @return string A string matching the regex or an empty string
137     * @suppress SecurityCheck-DoubleEscaped double escaping is on purpose per the inline
138     *                                       comment
139     */
140    private static function fundraiserLandingPageMakeSafe( $value, $default = '' ) {
141        if ( $default != '' ) {
142            $default = self::fundraiserLandingPageMakeSafe( $default );
143        }
144
145        if ( !is_string( $value ) ) {
146            // In case someone has passed in an array as a request parameter
147            return $default;
148        }
149
150        $num = preg_match( '/^([-a-zA-Z0-9_\/]+)$/', $value, $matches );
151
152        if ( $num == 1 ) {
153            # theoretically this is overkill, but better safe than sorry
154            return wfEscapeWikiText( htmlspecialchars( $matches[1] ) );
155        }
156        return $default;
157    }
158
159    /**
160     * Attempts to load a language localized template. Precedence is Language,
161     * Country, Root. It is assumed that all parts of the title are separated
162     * with '/'.
163     *
164     * @param Parser $parser Reference to the WM parser object
165     * @param string $page The template page root to load
166     * @param string $language The language to attempt to localize onto
167     * @param string $country The country to attempt to localize onto
168     *
169     * @return array The wikitext template
170     */
171    public static function fundraiserLandingPageSwitchLanguage( $parser, $page = '',
172        $language = 'en', $country = 'XX'
173    ) {
174        $page = self::fundraiserLandingPageMakeSafe( $page );
175        $country = self::fundraiserLandingPageMakeSafe( $country, 'XX' );
176        $language = self::fundraiserLandingPageMakeSafe( $language, 'en' );
177
178        if ( Title::newFromText( "Template:$page/$language/$country" )->exists() ) {
179            $tpltext = "$page/$language/$country";
180        } elseif ( Title::newFromText( "Template:$page/$language" )->exists() ) {
181            $tpltext = "$page/$language";
182        } else {
183            // If all the variants don't exist, then merely return the base. If
184            // something really screwy happened and the base doesn't exist either
185            // we will let the WM error handler sort it out.
186
187            $tpltext = $page;
188        }
189
190        return [ "{{Template:$tpltext}}", 'noparse' => false ];
191    }
192
193    /**
194     * Attempts to load a language localized template. Precedence is Country,
195     * Language, Root. It is assumed that all parts of the title are separated
196     * with '/'.
197     *
198     * @param Parser $parser Reference to the WM parser object
199     * @param string $page The template page root to load
200     * @param string $country The country to attempt to localize onto
201     * @param string $language The language to attempt to localize onto
202     *
203     * @return array The wikitext template
204     */
205    public static function fundraiserLandingPageSwitchCountry( $parser, $page = '', $country = 'XX',
206        $language = 'en'
207    ) {
208        $page = self::fundraiserLandingPageMakeSafe( $page );
209        $country = self::fundraiserLandingPageMakeSafe( $country, 'XX' );
210        $language = self::fundraiserLandingPageMakeSafe( $language, 'en' );
211
212        if ( Title::newFromText( "Template:$page/$country/$language" )->exists() ) {
213            $tpltext = "$page/$country/$language";
214
215        } elseif ( Title::newFromText( "Template:$page/$country" )->exists() ) {
216            $tpltext = "$page/$country";
217
218        } else {
219            // If all the variants don't exist, then merely return the base. If
220            // something really screwy happened and the base doesn't exist either
221            // we will let the WM error handler sort it out.
222
223            $tpltext = $page;
224        }
225
226        return [ "{{Template:$tpltext}}", 'noparse' => false ];
227    }
228
229    /**
230     * Check if template is fundraiseup
231     * @return bool
232     */
233    private function isFundraiseUp() {
234        return $this->getRequest()->getVal( 'fundraiseupScript' ) === "1";
235    }
236
237    /**
238     * Javascript to add fundraiseup to DonateWiki page
239     *
240     * @return string
241     */
242    private function getFundraiseUpJavascript() {
243        return <<<EOF
244            <script>(function(w,d,s,n,a){if(!w[n]){var l='call,catch,on,once,set,then,track'
245            .split(','),i,o=function(n){return'function'==typeof n?o.l.push([arguments])&&o
246            :function(){return o.l.push([n,arguments])&&o}},t=d.getElementsByTagName(s)[0],
247            j=d.createElement(s);j.async=!0;j.src='https://cdn.fundraiseup.com/widget/'+a;
248            t.parentNode.insertBefore(j,t);o.s=Date.now();o.v=4;o.h=w.location.href;o.l=[];
249            for(i=0;i<7;i++)o[l[i]]=o(l[i]);w[n]=o}
250            })(window,document,'script','FundraiseUp','AVLMPSRU');
251            </script>
252EOF;
253    }
254}