Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
PFAutoEdit
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 2
1406
0.00% covered (danger)
0.00%
0 / 1
 run
0.00% covered (danger)
0.00%
0 / 133
0.00% covered (danger)
0.00%
0 / 1
1332
 convertQueryString
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * '#autoedit' is called as:
5 *
6 * {{#autoedit:form=|target=|link text=|link type=|tooltip=|query string=
7 * |minor|reload}}
8 *
9 * This function creates a link or button that, when clicked on,
10 * automatically modifies the specified page according to the values in the
11 * 'query string' variable.
12 *
13 * The parameters of #autoedit are called in the same format as those
14 * of #formlink. The two additions are:
15 * 'minor' - sets this to be a "minor edit"
16 * 'reload' - causes the page to reload after the user clicks the button
17 * or link.
18 */
19
20use MediaWiki\Html\Html;
21use MediaWiki\MediaWikiServices;
22use MediaWiki\Title\Title;
23
24class PFAutoEdit {
25    public static function run( Parser $parser ) {
26        global $wgPageFormsAutoeditNamespaces;
27
28        $parser->getOutput()->addModules( [ 'ext.pageforms.autoedit' ] );
29        $parser->getOutput()->setPreventClickjacking( true );
30
31        // Set defaults.
32        $formcontent = '';
33        $linkString = null;
34        $linkType = 'span';
35        $summary = null;
36        $minorEdit = false;
37        $classString = 'autoedit-trigger';
38        $inTooltip = null;
39        $inQueryArr = [];
40        $editTime = null;
41        $latestRevId = null;
42        $confirmEdit = false;
43        $redirect = '';
44        $bringToPage = false;
45        $linkToPage = '#';
46
47        // Parse parameters.
48        $params = func_get_args();
49        // We don't need the parser.
50        array_shift( $params );
51
52        $wikiPageFactory = MediaWikiServices::getInstance()->getWikiPageFactory();
53
54        foreach ( $params as $param ) {
55            $elements = explode( '=', $param, 2 );
56
57            $key = trim( $elements[ 0 ] );
58            $value = ( count( $elements ) > 1 ) ? trim( $elements[ 1 ] ) : '';
59
60            switch ( $key ) {
61                case 'link text':
62                    $linkString = $parser->recursiveTagParse( $value );
63                    break;
64                case 'link type':
65                    $linkType = $parser->recursiveTagParse( $value );
66                    break;
67                case 'reload':
68                    $classString .= ' reload';
69                    break;
70                case 'summary':
71                    $summary = $parser->recursiveTagParse( $value );
72                    break;
73                case 'minor':
74                    $minorEdit = true;
75                    break;
76                case 'confirm':
77                    $confirmEdit = true;
78                    break;
79                case 'confirm text':
80                    $confirmEdit = true;
81                    $value = $parser->recursiveTagParse( $value );
82                    $arr = [ 'confirmtext' => $value ];
83                    $inQueryArr = PFUtils::arrayMergeRecursiveDistinct( $inQueryArr, $arr );
84                    break;
85                case 'query string':
86                    $inQueryArr = self::convertQueryString( $value, $inQueryArr );
87                    break;
88                case 'tooltip':
89                    $inTooltip = Sanitizer::decodeCharReferences( $value );
90                    break;
91
92                case 'target':
93                case 'title':
94                    $value = $parser->recursiveTagParse( $value );
95                    $arr = [ $key => $value ];
96                    $inQueryArr = PFUtils::arrayMergeRecursiveDistinct( $inQueryArr, $arr );
97
98                    $targetTitle = Title::newFromText( $value );
99
100                    if ( $targetTitle !== null ) {
101                        $allowedNamespaces = array_merge(
102                            $wgPageFormsAutoeditNamespaces,
103                            [ NS_CATEGORY ]
104                        );
105                        if ( !in_array( $targetTitle->getNamespace(), $allowedNamespaces ) ) {
106                            $errorMsg = wfMessage( 'pf-autoedit-invalidnamespace', $targetTitle->getNsText() )->parse();
107                            return Html::element( 'div', [ 'class' => 'error' ], $errorMsg );
108                        }
109                        $targetWikiPage = $wikiPageFactory->newFromTitle( $targetTitle );
110                        $targetWikiPage->clear();
111                        $editTime = $targetWikiPage->getTimestamp();
112                        $latestRevId = $targetWikiPage->getLatest();
113                    }
114                    break;
115                case 'redirect':
116                    $redirect = $value;
117                    break;
118                case 'bring to page':
119                    $bringToPage = true;
120                    break;
121
122                default:
123                    $value = $parser->recursiveTagParse( $value );
124                    $arr = [ $key => $value ];
125                    $inQueryArr = PFUtils::arrayMergeRecursiveDistinct( $inQueryArr, $arr );
126            }
127        }
128
129        if ( $redirect != '' && $bringToPage ) {
130            $errorMsg = wfMessage( 'pf_autoedit_notsettogether', 'redirect', 'bring to page' )->parse();
131            return Html::element( 'div', [ 'class' => 'error' ], $errorMsg );
132        }
133
134        if ( $redirect != '' ) {
135            $linkToPage = Skin::makeInternalOrExternalUrl( $redirect );
136        } elseif ( $bringToPage ) {
137            $linkToPage = $targetTitle->getFullURL();
138        }
139
140        // query string has to be turned into hidden inputs.
141        if ( !empty( $inQueryArr ) ) {
142            $query_components = explode( '&', http_build_query( $inQueryArr, '', '&' ) );
143
144            foreach ( $query_components as $query_component ) {
145                $var_and_val = explode( '=', $query_component, 2 );
146                if ( count( $var_and_val ) == 2 ) {
147                    $formcontent .= Html::hidden( urldecode( $var_and_val[0] ), urldecode( $var_and_val[1] ) );
148                }
149            }
150        }
151
152        if ( $linkString == null ) {
153            return null;
154        }
155
156        if ( $linkType == 'button' ) {
157            $attrs = [
158                'flags' => 'progressive',
159                'label' => $linkString,
160                'classes' => [ $classString ],
161                'href' => $linkToPage
162            ];
163            if ( $inTooltip != null ) {
164                $attrs['title'] = $inTooltip;
165            }
166            $parser->getOutput()->setEnableOOUI( true );
167            OutputPage::setupOOUI();
168            $linkElement = new OOUI\ButtonWidget( $attrs );
169        } elseif ( $linkType == 'link' ) {
170            $attrs = [ 'class' => $classString, 'href' => $linkToPage ];
171            if ( $inTooltip != null ) {
172                $attrs['title'] = $inTooltip;
173            }
174            $linkElement = Html::rawElement( 'a', $attrs, $linkString );
175        } else {
176            $linkElement = Html::rawElement( 'span', [ 'class' => $classString ], $linkString );
177        }
178
179        if ( $summary == null ) {
180            $summary = wfMessage( 'pf_autoedit_summary', "[[{$parser->getTitle()}]]" )->text();
181        }
182
183        $formcontent .= Html::hidden( 'wpSummary', $summary );
184
185        if ( $minorEdit ) {
186            $formcontent .= Html::hidden( 'wpMinoredit', true );
187        }
188
189        if ( $editTime !== null ) {
190            $formcontent .= Html::hidden( 'wpEdittime', $editTime );
191        }
192        if ( $latestRevId !== null ) {
193            $formcontent .= Html::hidden( 'editRevId', $latestRevId );
194        }
195
196        if ( $confirmEdit ) {
197            $formAttrs = [ 'class' => [ 'autoedit-data', 'confirm-edit' ] ];
198        } else {
199            $formAttrs = [ 'class' => 'autoedit-data' ];
200        }
201
202        $form = Html::rawElement( 'form', $formAttrs, $formcontent );
203
204        $output = Html::rawElement( 'div', [ 'class' => 'autoedit' ],
205                $linkElement .
206                Html::rawElement( 'span', [ 'class' => "autoedit-result" ], '' ) .
207                $form
208        );
209
210        // Return output HTML.
211        return [ $output, 'noparse' => true, 'isHTML' => true ];
212    }
213
214    public static function convertQueryString( $queryString, $inQueryArr ) {
215        // Change HTML-encoded ampersands directly to URL-encoded
216        // ampersands, so that the string doesn't get split up on the '&'.
217        $queryString = str_replace( '&amp;', '%26', $queryString );
218        // "Decode" any other HTML tags.
219        $queryString = html_entity_decode( $queryString, ENT_QUOTES );
220        // next, replace  Foo[Bar] += Baz  with  Foo[Bar+] = Baz
221        // and do the same for -=
222        // This way, parse_str won't strip out the += and -=
223        $queryString = preg_replace( "/\[([^\]]+)\]\s*(\+|-)=/", "[$1$2]=", $queryString );
224        // Prevent "decoding" + into a space character
225        $queryString = str_replace( '+', '%2B', $queryString );
226
227        parse_str( $queryString, $arr );
228
229        return PFUtils::arrayMergeRecursiveDistinct( $inQueryArr, $arr );
230    }
231}