Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 118
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
PFFormInputParserFunction
0.00% covered (danger)
0.00%
0 / 118
0.00% covered (danger)
0.00%
0 / 1
1190
0.00% covered (danger)
0.00%
0 / 1
 run
0.00% covered (danger)
0.00%
0 / 118
0.00% covered (danger)
0.00%
0 / 1
1190
1<?php
2/**
3 * '#forminput' is called as:
4 *
5 * {{#forminput:form=|size=|default value=|button text=|query string=
6 * |autocomplete on category=|autocomplete on namespace=
7 * |popup|reload|...additional query string values...}}
8 *
9 * This function returns HTML representing a form to let the user enter the
10 * name of a page to be added or edited using a Page Forms form. All
11 * arguments are optional. 'form' is the name of the PF form to be used;
12 * if it is left empty, a dropdown will appear, letting the user chose among
13 * all existing forms. 'size' represents the size of the text input (the
14 * default is the one set by OOUI), and 'default value' is the starting
15 * value of the input. 'button text' is the text that will appear on the
16 * "submit" button, and 'query string' is the set of values that you want
17 * passed in through the query string to the form. (Query string values can
18 * also be passed in directly as parameters.)
19 * Finally, you can can specify that the user will get autocompletion using
20 * the values from a category or namespace of your choice, using
21 * 'autocomplete on category' or 'autocomplete on namespace' (you can only
22 * use one). To autocomplete on all pages in the main (blank)
23 * namespace, specify "autocomplete on namespace=main".
24 * 'reload' is an optional parameter that can be used alongside either
25 * 'popup' or 'returnto'; it causes the page that the user ends up on after
26 * submitting the form to get reloaded with 'action=purge'.
27 *
28 * Example: to create an input to add or edit a page with a form called
29 * 'User' within a namespace also called 'User', and to have the form
30 * preload with the page called 'UserStub', you could call the following:
31 *
32 * {{#forminput:form=User|button text=Add or edit user
33 * |query string=namespace=User&preload=UserStub}}
34 */
35
36use MediaWiki\MediaWikiServices;
37
38class PFFormInputParserFunction {
39    /**
40     * static variable to guarantee that JavaScript for autocompletion
41     * only gets added to the page once.
42     */
43    private static $num_autocompletion_inputs = 0;
44
45    public static function run( Parser $parser ) {
46        global $wgCapitalLinks;
47
48        $params = func_get_args();
49        // We don't need the parser.
50        array_shift( $params );
51
52        $parser->getOutput()->addModules( [ 'ext.pageforms.forminput' ] );
53
54        // Set defaults.
55        $inFormName = $inValue = $inButtonStr = '';
56        $inQueryArr = [];
57        $inAutocompletionSource = '';
58        $inSize = '';
59        $classStr = "pfFormInput";
60        $inNamespaceSelector = null;
61        $inPlaceholder = null;
62        $inAutofocus = true;
63        $hasPopup = $hasReturnTo = false;
64
65        // Assign params.
66        foreach ( $params as $i => $param ) {
67            $elements = explode( '=', $param, 2 );
68
69            // Set param name and value.
70            if ( count( $elements ) > 1 ) {
71                $paramName = trim( $elements[0] );
72                // Parse (and sanitize) parameter values.
73                // We call recursivePreprocess() and not
74                // recursiveTagParse() so that URL values will
75                // not be turned into links.
76                $value = trim( $parser->recursivePreprocess( html_entity_decode( $elements[1], ENT_QUOTES ) ) );
77            } else {
78                $paramName = trim( $param );
79                $value = null;
80            }
81
82            if ( $paramName == 'form' ) {
83                $inFormName = $value;
84            } elseif ( $paramName == 'size' ) {
85                $inSize = $value;
86            } elseif ( $paramName == 'default value' ) {
87                $inValue = $value;
88            } elseif ( $paramName == 'button text' ) {
89                $inButtonStr = $value;
90            } elseif ( $paramName == 'query string' ) {
91                $inQueryArr = PFAutoEdit::convertQueryString( $value, $inQueryArr );
92            } elseif ( $paramName == 'autocomplete on category' ) {
93                $inAutocompletionSource = $value;
94                $autocompletionType = 'category';
95            } elseif ( $paramName == 'autocomplete on namespace' ) {
96                $inAutocompletionSource = $value;
97                $autocompletionType = 'namespace';
98            } elseif ( $paramName == 'namespace selector' ) {
99                $inNamespaceSelector = explode( ',', $value );
100            } elseif ( $paramName == 'placeholder' ) {
101                $inPlaceholder = $value;
102            } elseif ( $paramName == 'popup' ) {
103                PFFormLink::loadScriptsForPopupForm( $parser );
104                $classStr .= ' popupforminput';
105                $hasPopup = true;
106            } elseif ( $paramName == 'reload' ) {
107                $classStr .= ' reload';
108                $inQueryArr['reload'] = '1';
109            } elseif ( $paramName == 'no autofocus' ) {
110                $inAutofocus = false;
111            } else {
112                $value = urlencode( $value );
113                parse_str( "$paramName=$value", $arr );
114                $inQueryArr = PFUtils::arrayMergeRecursiveDistinct( $inQueryArr, $arr );
115                if ( $paramName == 'returnto' ) {
116                    $hasReturnTo = true;
117                }
118            }
119        }
120
121        if ( $hasPopup && $hasReturnTo ) {
122            return '<div class="error">Error: \'popup\' and \'returnto\' cannot be set in the same function.</div>';
123        }
124
125        $formInputAttrs = [
126            'class' => 'pfFormInputWrapper',
127            'data-size' => $inSize
128        ];
129
130        $formContents = '';
131
132        if ( $inValue != null ) {
133            $formInputAttrs['data-default-value'] = $inValue;
134        }
135
136        if ( $inNamespaceSelector !== null ) {
137            foreach ( $inNamespaceSelector as &$nsName ) {
138                $nsName = htmlspecialchars( trim( $nsName ) );
139            }
140            $possibleNamespacesStr = implode( '|', $inNamespaceSelector );
141            $formInputAttrs['data-possible-namespaces'] = $possibleNamespacesStr;
142        }
143
144        if ( $inPlaceholder != null ) {
145            $formInputAttrs['data-placeholder'] = $inPlaceholder;
146        }
147        if ( $inAutofocus ) {
148            $formInputAttrs['data-autofocus'] = true;
149        }
150        if ( !$wgCapitalLinks ) {
151            $formInputAttrs['data-autocapitalize'] = 'off';
152        }
153
154        // Now apply the necessary settings and JavaScript, depending
155        // on whether or not there's autocompletion (and whether the
156        // autocompletion is local or remote).
157        $input_num = 1;
158        if ( !empty( $inAutocompletionSource ) ) {
159            self::$num_autocompletion_inputs++;
160            $input_num = self::$num_autocompletion_inputs;
161            $inputID = 'input_' . $input_num;
162            $formInputAttrs['id'] = $inputID;
163            // This code formerly only used remote autocompletion
164            // when the number of autocompletion values was above
165            // a certain limit - as happens in regular forms -
166            // but local autocompletion didn't always work,
167            // apparently due to page caching.
168            $formInputAttrs['data-autocomplete-settings'] = $inAutocompletionSource;
169            $formInputAttrs['data-autocomplete-data-type'] = $autocompletionType;
170        }
171
172        // If the form start URL looks like "index.php?title=Special:FormStart"
173        // (i.e., it's in the default URL style), add in the title as a
174        // hidden value
175        $fs = PFUtils::getSpecialPage( 'FormStart' );
176        $fsURL = $fs->getPageTitle()->getLocalURL();
177        $pos = strpos( $fsURL, "title=" );
178        if ( $pos > -1 ) {
179            $formContents .= Html::hidden( "title", urldecode( substr( $fsURL, $pos + 6 ) ) );
180        }
181        $listOfForms = preg_split( '~(?<!\\\)' . preg_quote( ',', '~' ) . '~', $inFormName );
182        foreach ( $listOfForms as & $formName ) {
183            $formName = str_replace( "\,", ",", $formName );
184        }
185        unset( $formName );
186        if ( $inFormName == '' ) {
187            try {
188                $allForms = PFUtils::getAllForms();
189            } catch ( MWException $e ) {
190                return Html::element( 'div', [ 'class' => 'error' ], $e->getMessage() );
191            }
192            $formInputAttrs['data-possible-forms'] = implode( '|', $allForms );
193            $formInputAttrs['data-form-label'] = wfMessage( 'pf-formstart-formlabel' )->escaped();
194        } elseif ( count( $listOfForms ) == 1 ) {
195            $inFormName = str_replace( '\,', ',', $inFormName );
196            $formContents .= Html::hidden( "form", $inFormName );
197        } else {
198            $formInputAttrs['data-possible-forms'] = implode( '|', $listOfForms );
199            $formInputAttrs['data-form-label'] = wfMessage( 'pf-formstart-formlabel' )->escaped();
200        }
201
202        // Recreate the passed-in query string as a set of hidden
203        // variables.
204        if ( !empty( $inQueryArr ) ) {
205            // Query string has to be turned into hidden inputs.
206            $query_components = explode( '&', http_build_query( $inQueryArr, '', '&' ) );
207
208            foreach ( $query_components as $query_component ) {
209                $var_and_val = explode( '=', $query_component, 2 );
210                if ( count( $var_and_val ) == 2 ) {
211                    $formContents .= Html::hidden( urldecode( $var_and_val[0] ), urldecode( $var_and_val[1] ) );
212                }
213            }
214        }
215
216        $formInputAttrs['data-button-label'] = ( $inButtonStr != '' ) ? $inButtonStr : wfMessage( 'pf_formstart_createoredit' )->escaped();
217        $formContents .= Html::element( 'div', $formInputAttrs, null );
218
219        MediaWikiServices::getInstance()->getHookContainer()->run( 'PageForms::FormInputEnd', [ $params, &$formContents ] );
220
221        $str = "\t" . Html::rawElement( 'form', [
222                'name' => 'createbox',
223                'action' => $fsURL,
224                'method' => 'get',
225                'class' => $classStr
226            ], '<p>' . $formContents . '</p>'
227        ) . "\n";
228
229        return [ $str, 'noparse' => true, 'isHTML' => true ];
230    }
231}