Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
19.14% covered (danger)
19.14%
94 / 491
14.29% covered (danger)
14.29%
4 / 28
CRAP
0.00% covered (danger)
0.00%
0 / 1
PFPageSchemas
19.14% covered (danger)
19.14%
94 / 491
14.29% covered (danger)
14.29%
4 / 28
17885.28
0.00% covered (danger)
0.00%
0 / 1
 createPageSchemasObject
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
462
 createSchemaXMLFromForm
96.67% covered (success)
96.67%
29 / 30
0.00% covered (danger)
0.00%
0 / 1
16
 createTemplateXMLFromForm
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 createFieldXMLFromForm
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
12
 createPageSectionXMLFromForm
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
4
 createFormInputXMLFromForm
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
6
 getDisplayColor
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSchemaDisplayString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSchemaEditingHTML
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
42
 getTemplateEditingHTML
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
12
 getFieldEditingHTML
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 1
210
 getPageSectionEditingHTML
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
56
 getFormName
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getMainFormInfo
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
42
 getFormFieldInfo
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
90
 getPageSection
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
42
 getPagesToGenerate
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 getFieldsFromTemplateSchema
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
72
 generateForm
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
42
 generatePages
0.00% covered (danger)
0.00%
0 / 81
0.00% covered (danger)
0.00%
0 / 1
380
 getSchemaDisplayValues
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getTemplateValues
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
 getTemplateDisplayString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTemplateDisplayValues
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 getFieldDisplayString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPageSectionDisplayString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFieldDisplayValues
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 getPageSectionDisplayValues
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3use MediaWiki\Html\Html;
4use MediaWiki\MediaWikiServices;
5use MediaWiki\Title\Title;
6
7/**
8 * Static functions for Page Forms, for use by the Page Schemas
9 * extension.
10 *
11 * @author Yaron Koren
12 * @author Ankit Garg
13 * @file
14 * @ingroup PF
15 */
16
17class PFPageSchemas extends PSExtensionHandler {
18
19    /**
20     * Creates an object to hold form-wide information, based on an XML
21     * object from the Page Schemas extension.
22     * @param string $tagName
23     * @param SimpleXMLElement $xml
24     * @return string[]|null
25     */
26    public static function createPageSchemasObject( $tagName, $xml ) {
27        $pfarray = [];
28
29        if ( $tagName == "standardInputs" ) {
30            foreach ( $xml->children() as $_ => $child ) {
31                foreach ( $child->children() as $tag => $formelem ) {
32                    if ( $tag == $tagName ) {
33                        foreach ( $formelem->attributes() as $attr => $name ) {
34                            $pfarray[$attr] = (string)$formelem->attributes()->$attr;
35                        }
36                    }
37                }
38                return $pfarray;
39            }
40        }
41
42        if ( $tagName == "pageforms_Form" ) {
43            foreach ( $xml->children() as $tag => $child ) {
44                if ( $tag == $tagName ) {
45                    $formName = (string)$child->attributes()->name;
46                    $pfarray['name'] = $formName;
47                    foreach ( $child->children() as $childTag => $formelem ) {
48                        $pfarray[$childTag] = (string)$formelem;
49                    }
50                    return $pfarray;
51                }
52            }
53        }
54        if ( $tagName == "pageforms_TemplateDetails" ) {
55            foreach ( $xml->children() as $tag => $child ) {
56                if ( $tag == $tagName ) {
57                    foreach ( $child->children() as $childTag => $formelem ) {
58                        $pfarray[$childTag] = (string)$formelem;
59                    }
60                    return $pfarray;
61                }
62            }
63        }
64        if ( $tagName == "pageforms_FormInput" || $tagName == "pageforms_PageSection" ) {
65            foreach ( $xml->children() as $tag => $child ) {
66                if ( $tag == $tagName ) {
67                    foreach ( $child->children() as $prop ) {
68                        if ( $prop->getName() == 'InputType' ) {
69                            $pfarray[$prop->getName()] = (string)$prop;
70                        } else {
71                            if ( (string)$prop->attributes()->name == '' ) {
72                                $pfarray[$prop->getName()] = (string)$prop;
73                            } else {
74                                $pfarray[(string)$prop->attributes()->name] = (string)$prop;
75                            }
76                        }
77                    }
78                    return $pfarray;
79                }
80            }
81        }
82        return null;
83    }
84
85    /**
86     * Creates Page Schemas XML for form-wide information.
87     * @return string
88     */
89    public static function createSchemaXMLFromForm() {
90        global $wgRequest;
91
92        // Quick check: if the "form name" field hasn't been sent,
93        // it means the main "Form" checkbox wasn't selected; don't
94        // create any XML if so.
95        if ( !$wgRequest->getCheck( 'pf_form_name' ) ) {
96            return '';
97        }
98
99        $formName = null;
100        $xml = '';
101        $includeFreeText = false;
102        $freeTextLabel = null;
103        foreach ( $wgRequest->getValues() as $var => $val ) {
104            if ( $var == 'pf_form_name' ) {
105                $formName = $val;
106            } elseif ( $var == 'pf_page_name_formula' ) {
107                if ( !empty( $val ) ) {
108                    $xml .= Xml::element( 'PageNameFormula', null, $val );
109                }
110            } elseif ( $var == 'pf_create_title' ) {
111                if ( !empty( $val ) ) {
112                    $xml .= Xml::element( 'CreateTitle', null, $val );
113                }
114            } elseif ( $var == 'pf_edit_title' ) {
115                if ( !empty( $val ) ) {
116                    $xml .= Xml::element( 'EditTitle', null, $val );
117                }
118            } elseif ( $var == 'pf_fi_free_text' && !empty( $val ) ) {
119                $includeFreeText = true;
120            } elseif ( $var == 'pf_fi_free_text_label' ) {
121                if ( !empty( $val ) ) {
122                    $freeTextLabel = $val;
123                }
124            }
125        }
126
127        if ( $includeFreeText ) {
128            $standardInputsAttrs = [ 'inputFreeText' => '1' ];
129            if ( !empty( $freeTextLabel ) ) {
130                $standardInputsAttrs['freeTextLabel'] = $freeTextLabel;
131            }
132            $xml .= Xml::element( 'standardInputs', $standardInputsAttrs );
133        }
134
135        $formXml = Xml::openElement( 'pageforms_Form', [ 'name' => $formName ] ) . $xml . Xml::closeElement( 'pageforms_Form' );
136        return $formXml;
137    }
138
139    /**
140     * Creates Page Schemas XML from form information on templates.
141     * @return string[]
142     */
143    public static function createTemplateXMLFromForm() {
144        global $wgRequest;
145
146        $xmlPerTemplate = [];
147        $templateNum = -1;
148        $xml = '';
149        foreach ( $wgRequest->getValues() as $var => $val ) {
150            if ( substr( $var, 0, 18 ) === 'pf_template_label_' ) {
151                $templateNum = substr( $var, 18 );
152                $xml = Xml::openElement( 'pageforms_TemplateDetails' );
153                if ( !empty( $val ) ) {
154                    $xml .= Xml::element( 'Label', null, $val );
155                }
156            } elseif ( substr( $var, 0, 23 ) === 'pf_template_addanother_' ) {
157                if ( !empty( $val ) ) {
158                    $xml .= Xml::element( 'AddAnotherText', null, $val );
159                }
160                $xml .= Xml::closeElement( 'pageforms_TemplateDetails' );
161                $xmlPerTemplate[$templateNum] = $xml;
162            }
163        }
164        return $xmlPerTemplate;
165    }
166
167    /**
168     * Creates Page Schemas XML for form fields.
169     * @return string[]
170     */
171    public static function createFieldXMLFromForm() {
172        global $wgRequest;
173
174        $xmlPerField = [];
175        $fieldNum = -1;
176        $xml = '';
177        foreach ( $wgRequest->getValues() as $var => $val ) {
178            if ( substr( $var, 0, 14 ) === 'pf_input_type_' ) {
179                $fieldNum = substr( $var, 14 );
180                $xml = Xml::openElement( 'pageforms_FormInput' );
181                if ( !empty( $val ) ) {
182                    $xml .= Xml::element( 'InputType', null, $val );
183                }
184            } elseif ( substr( $var, 0, 14 ) === 'pf_key_values_' ) {
185                $xml .= self::createFormInputXMLFromForm( $val );
186            } elseif ( substr( $var, 0, 14 ) === 'pf_input_befo_' ) {
187                if ( $val !== '' ) {
188                    $xml .= Xml::element( 'TextBeforeField', null, $val );
189                }
190            } elseif ( substr( $var, 0, 14 ) === 'pf_input_desc_' ) {
191                if ( $val !== '' ) {
192                    $xml .= Xml::element( 'Description', null, $val );
193                }
194            } elseif ( substr( $var, 0, 18 ) === 'pf_input_desctool_' ) {
195                if ( $val !== '' ) {
196                    $xml .= Xml::element( 'DescriptionTooltipMode', null, $val );
197                }
198            } elseif ( substr( $var, 0, 16 ) === 'pf_input_finish_' ) {
199                // This is a hack.
200                $xml .= Xml::closeElement( 'pageforms_FormInput' );
201                $xmlPerField[$fieldNum] = $xml;
202            }
203        }
204        return $xmlPerField;
205    }
206
207    /**
208     * Creates Page Schemas XML for page sections
209     * @return string[]
210     */
211    public static function createPageSectionXMLFromForm() {
212        global $wgRequest;
213        $xmlPerPageSection = [];
214        $pageSectionNum = -1;
215
216        foreach ( $wgRequest->getValues() as $var => $val ) {
217            if ( substr( $var, 0, 26 ) == 'pf_pagesection_key_values_' ) {
218                $pageSectionNum = substr( $var, 26 );
219                $xml = "";
220                if ( $val != '' ) {
221                    $xml = Xml::tags(
222                        'pageforms_PageSection',
223                        [],
224                        self::createFormInputXMLFromForm( $val )
225                    );
226                }
227                $xmlPerPageSection[$pageSectionNum] = $xml;
228            }
229        }
230        return $xmlPerPageSection;
231    }
232
233    static function createFormInputXMLFromForm( $valueFromForm ) {
234        $xml = '';
235        if ( $valueFromForm !== '' ) {
236            // replace the comma substitution character that has no chance of
237            // being included in the values list - namely, the ASCII beep
238            $listSeparator = ',';
239            $key_values_str = str_replace( "\\$listSeparator", "\a", $valueFromForm );
240            $key_values_array = explode( $listSeparator, $key_values_str );
241            foreach ( $key_values_array as $value ) {
242                // replace beep back with comma, trim
243                $value = str_replace( "\a", $listSeparator, trim( $value ) );
244                $param_value = explode( "=", $value, 2 );
245                if ( count( $param_value ) == 2 && $param_value[1] !== null && $param_value[1] !== '' ) {
246                    // Handles <Parameter name="size">20</Parameter>
247                    $xml .= Xml::element( 'Parameter', [ 'name' => $param_value[0] ], $param_value[1] );
248                } else {
249                    // Handles <Parameter name="mandatory"/>
250                    $xml .= Xml::element( 'Parameter', [ 'name' => $param_value[0] ] );
251                }
252            }
253        }
254        return $xml;
255    }
256
257    public static function getDisplayColor() {
258        return '#CF9';
259    }
260
261    public static function getSchemaDisplayString() {
262        return wfMessage( 'pf-pageschemas-header' )->escaped();
263    }
264
265    public static function getSchemaEditingHTML( $pageSchemaObj ) {
266        $form_array = [];
267        $hasExistingValues = false;
268        if ( $pageSchemaObj !== null ) {
269            $form_array = $pageSchemaObj->getObject( 'pageforms_Form' );
270            if ( $form_array !== null ) {
271                $hasExistingValues = true;
272            }
273        }
274
275        // Get all the values from the page schema.
276        $formName = PageSchemas::getValueFromObject( $form_array, 'name' );
277        $pageNameFormula = PageSchemas::getValueFromObject( $form_array, 'PageNameFormula' );
278        $createTitle = PageSchemas::getValueFromObject( $form_array, 'CreateTitle' );
279        $editTitle = PageSchemas::getValueFromObject( $form_array, 'EditTitle' );
280
281        // Inputs
282        if ( $pageSchemaObj !== null ) {
283            $standardInputs = $pageSchemaObj->getObject( 'standardInputs' );
284            $includeFreeText = $standardInputs['inputFreeText'] ?? false;
285        } else {
286            $includeFreeText = true;
287        }
288
289        $freeTextLabel = html_entity_decode( PageSchemas::getValueFromObject( $form_array, 'freeTextLabel' ) ?? '' );
290
291        $text = "\t<p>" . wfMessage( 'ps-namelabel' )->escaped() . ' ' . Html::input( 'pf_form_name', $formName, 'text', [ 'size' => 15 ] ) . "</p>\n";
292        // The checkbox isn't actually a field in the page schema -
293        // we set it based on whether or not a page formula has been
294        // specified.
295        $twoStepProcessAttrs = [ 'id' => 'pf-two-step-process' ];
296        if ( $pageNameFormula === null ) {
297            $twoStepProcessAttrs['checked'] = true;
298        }
299        $text .= '<p>' . Html::input( 'pf_two_step_process', '', 'checkbox', $twoStepProcessAttrs );
300        $text .= ' Users must enter the page name before getting to the form (default)';
301        $text .= "</p>\n";
302        $text .= '<div class="editSchemaMinorFields">';
303        $text .= "\t<p id=\"pf-page-name-formula\">" . wfMessage( 'pf-pageschemas-pagenameformula' )->escaped() . ' ' .
304            Html::input( 'pf_page_name_formula', $pageNameFormula, 'text', [ 'size' => 30 ] ) . "</p>\n";
305        $text .= "\t<p>" . wfMessage( 'pf-pageschemas-createtitle' )->escaped() . ' ' .
306            Html::input( 'pf_create_title', $createTitle, 'text', [ 'size' => 25 ] ) . "</p>\n";
307        $text .= "\t<p id=\"pf-edit-title\">" . wfMessage( 'pf-pageschemas-edittitle' )->escaped() . ' ' .
308            Html::input( 'pf_edit_title', $editTitle, 'text', [ 'size' => 25 ] ) . "</p>\n";
309
310        // This checkbox went from a default of false to true in PF 5.2.
311        $text .= '<p>';
312        $text .= Html::input( 'pf_fi_free_text', '1', 'checkbox', [ 'id' => 'pf_fi_free_text', 'checked' => $includeFreeText ] ) . ' ';
313        $text .= Html::rawElement( 'label', [ 'for' => 'pf_fi_free_text' ], wfMessage( 'pf-pageschemas-includefreetextinput' )->escaped() );
314        $text .= "</p>";
315
316        if ( empty( $freeTextLabel ) ) {
317            $freeTextLabel = wfMessage( 'pf_form_freetextlabel' )->inContentLanguage()->text();
318        }
319        $text .= '<p>' . wfMessage( 'pf-pageschemas-freetextlabel' )->escaped() . ' ' .
320            Html::input( 'pf_fi_free_text_label', $freeTextLabel, 'text' ) . "</p>";
321
322        $text .= "</div>\n";
323
324        global $wgOut;
325        // Separately, add Javascript for getting the checkbox to
326        // hide certain fields.
327        $wgOut->addModules( [ 'ext.pageforms.PF_PageSchemas' ] );
328
329        return [ $text, $hasExistingValues ];
330    }
331
332    public static function getTemplateEditingHTML( $psTemplate ) {
333        $hasExistingValues = false;
334        $templateLabel = null;
335        $addAnotherText = null;
336        if ( $psTemplate !== null ) {
337            $form_array = $psTemplate->getObject( 'pageforms_TemplateDetails' );
338            if ( $form_array !== null ) {
339                $hasExistingValues = true;
340                $templateLabel = PageSchemas::getValueFromObject( $form_array, 'Label' );
341                $addAnotherText = PageSchemas::getValueFromObject( $form_array, 'AddAnotherText' );
342            }
343        }
344
345        $text = "\t<p>" . wfMessage( 'pf-pageschemas-templatedetailslabel' )->escaped() . "</p>\n";
346        $text .= "\t<p>" . wfMessage( 'exif-label' )->escaped() . ': ' .
347            Html::input( 'pf_template_label_num', $templateLabel, 'text', [ 'size' => 15 ] ) . "</p>\n";
348        $text .= "\t<p>" . 'Text of button to add another instance (default is "Add another"):' . ' ' .
349            Html::input( 'pf_template_addanother_num', $addAnotherText, 'text', [ 'size' => 25 ] ) . "</p>\n";
350
351        return [ $text, $hasExistingValues ];
352    }
353
354    /**
355     * Returns the HTML for inputs to define a single form field,
356     * within the Page Schemas 'edit schema' page.
357     * @param PSTemplateField $psField
358     * @return array
359     */
360    public static function getFieldEditingHTML( $psField ) {
361        $fieldValues = [];
362        $hasExistingValues = false;
363        $inputType = null;
364        $inputDesc = null;
365        $inputDescTooltipMode = null;
366        $inputBeforeText = null;
367        if ( $psField !== null ) {
368            $fieldValues = $psField->getObject( 'pageforms_FormInput' );
369            if ( $fieldValues !== null ) {
370                $hasExistingValues = true;
371                $inputType = PageSchemas::getValueFromObject( $fieldValues, 'InputType' );
372                $inputDesc = PageSchemas::getValueFromObject( $fieldValues, 'Description' );
373                $inputDescTooltipMode = PageSchemas::getValueFromObject( $fieldValues, 'DescriptionTooltipMode' );
374                $inputBeforeText = PageSchemas::getValueFromObject( $fieldValues, 'TextBeforeField' );
375            } else {
376                $fieldValues = [];
377            }
378        }
379
380        global $wgPageFormsFormPrinter;
381        $possibleInputTypes = $wgPageFormsFormPrinter->getAllInputTypes();
382        $inputTypeDropdownHTML = Html::element( 'option', [], '' );
383        foreach ( $possibleInputTypes as $possibleInputType ) {
384            $inputTypeOptionAttrs = [];
385            if ( $possibleInputType == $inputType ) {
386                $inputTypeOptionAttrs['selected'] = true;
387            }
388            $inputTypeDropdownHTML .= Html::element( 'option', $inputTypeOptionAttrs, $possibleInputType ) . "\n";
389        }
390        $inputTypeDropdown = Html::rawElement( 'select', [ 'name' => 'pf_input_type_num' ], $inputTypeDropdownHTML );
391        $text = '<p>' . wfMessage( 'pf-pageschemas-inputtype' )->escaped() . ' ' . $inputTypeDropdown . '</p>';
392
393        $text .= "\t" . '<p>' . wfMessage( 'pf-pageschemas-otherparams', 'size=20, mandatory' )->escaped() . '</p>' . "\n";
394        $paramValues = [];
395        foreach ( $fieldValues as $param => $value ) {
396            if ( !empty( $param ) && $param != 'InputType' && $param != 'Description' && $param != 'DescriptionTooltipMode' && $param != 'TextBeforeField' ) {
397                if ( !empty( $value ) ) {
398                    $paramValues[] = $param . '=' . $value;
399                } else {
400                    $paramValues[] = $param;
401                }
402            }
403        }
404        foreach ( $paramValues as $i => $paramAndVal ) {
405            $paramValues[$i] = str_replace( ',', '\,', $paramAndVal );
406        }
407        $param_value_str = implode( ', ', $paramValues );
408        $inputParamsAttrs = [ 'size' => 80 ];
409        $inputParamsInput = Html::input( 'pf_key_values_num', $param_value_str, 'text', $inputParamsAttrs );
410        $text .= "\t<p>$inputParamsInput</p>\n";
411
412        $text .= '<div class="editSchemaMinorFields">' . "\n";
413        $inputBeforeTextPrint = Html::input( 'pf_input_befo_num', $inputBeforeText, 'text', [ 'size' => 80 ] );
414        $text .= "\t<p>Text that will be printed before the field: $inputBeforeTextPrint</p>\n";
415
416        $inputDescriptionLabel = wfMessage( 'pf-pageschemas-inputdescription' )->parse();
417        $inputDescription = Html::input( 'pf_input_desc_num', $inputDesc, 'text', [ 'size' => 80 ] );
418        $inputDescriptionTooltipMode = Html::input( 'pf_input_desctool_num', $inputDescTooltipMode, 'checkbox', [ 'checked' => ( $inputDescTooltipMode ) ? 'checked' : null ] );
419        $useTooltipLabel = wfMessage( 'pf-pageschemas-usetooltip' )->escaped();
420        $text .= "\t<p>$inputDescriptionLabel $inputDescription<br>$inputDescriptionTooltipMode $useTooltipLabel</p>\n";
421
422        // @HACK to make input parsing easier.
423        $text .= Html::hidden( 'pf_input_finish_num', 1 );
424
425        $text .= "</div>\n";
426
427        return [ $text, $hasExistingValues ];
428    }
429
430    public static function getPageSectionEditingHTML( $psPageSection ) {
431        $otherParams = [];
432
433        if ( $psPageSection !== null ) {
434            $otherParams = $psPageSection->getObject( 'pageforms_PageSection' );
435        }
436        $paramValues = [];
437        if ( $otherParams !== null ) {
438            foreach ( $otherParams as $param => $value ) {
439                if ( !empty( $param ) ) {
440                    if ( !empty( $value ) ) {
441                        $paramValues[] = $param . '=' . $value;
442                    } else {
443                        $paramValues[] = $param;
444                    }
445                }
446            }
447        }
448
449        foreach ( $paramValues as $i => $paramAndVal ) {
450            $paramValues[$i] = str_replace( ',', '\,', $paramAndVal );
451        }
452        $param_value_str = implode( ', ', $paramValues );
453        $text = "\t" . '<p>' . wfMessage( 'pf-pageschemas-otherparams', 'rows=10, mandatory' )->escaped() . '</p>' . "\n";
454        $inputParamsInput = Html::input( 'pf_pagesection_key_values_num', $param_value_str, 'text', [ 'size' => 80 ] );
455        $text .= "\t<p>$inputParamsInput</p>\n";
456
457        return $text;
458    }
459
460    public static function getFormName( $pageSchemaObj ) {
461        $mainFormInfo = self::getMainFormInfo( $pageSchemaObj );
462        if ( $mainFormInfo === null || !array_key_exists( 'name', $mainFormInfo ) ) {
463            return null;
464        }
465        return $mainFormInfo['name'];
466    }
467
468    public static function getMainFormInfo( $pageSchemaObj ) {
469        // return $pageSchemaObj->getObject( 'pageforms_Form' );
470        // We don't just call getObject() here, because sometimes, for
471        // some reason, this gets called before PF registers itself
472        // with Page Schemas, which means that getObject() would return
473        // null. Instead, we directly call the code that would have
474        // been called.
475        $xml = $pageSchemaObj->getXML();
476        foreach ( $xml->children() as $tag => $child ) {
477            if ( $tag == "pageforms_Form" ) {
478                $pfarray = [];
479                $formName = (string)$child->attributes()->name;
480                $pfarray['name'] = $formName;
481                foreach ( $child->children() as $childTag => $formelem ) {
482                    if ( $childTag == "standardInputs" ) {
483                        foreach ( $formelem->attributes() as $attr => $value ) {
484                            $pfarray[$attr] = (string)$formelem->attributes()->$attr;
485                        }
486                    } else {
487                        $pfarray[$childTag] = (string)$formelem;
488                    }
489                }
490                return $pfarray;
491            }
492        }
493        return [];
494    }
495
496    public static function getFormFieldInfo( $psTemplate, $template_fields ) {
497        $form_fields = [];
498        $fieldsInfo = $psTemplate->getFields();
499        foreach ( $fieldsInfo as $i => $psField ) {
500            $fieldFormArray = $psField->getObject( 'pageforms_FormInput' );
501            if ( $fieldFormArray === null ) {
502                continue;
503            }
504            $formField = PFFormField::create( $template_fields[$i] );
505            foreach ( $fieldFormArray as $var => $val ) {
506                if ( $var == 'InputType' ) {
507                    $formField->setInputType( $val );
508                } elseif ( $var == 'mandatory' ) {
509                    $formField->setIsMandatory( true );
510                } elseif ( $var == 'hidden' ) {
511                    $formField->setIsHidden( true );
512                } elseif ( $var == 'restricted' ) {
513                    $formField->setIsRestricted( true );
514                } elseif ( in_array( $var, [ 'Description', 'DescriptionTooltipMode', 'TextBeforeField' ] ) ) {
515                    $formField->setDescriptionArg( $var, $val );
516                } else {
517                    $formField->setFieldArg( $var, $val );
518                }
519            }
520            $form_fields[] = $formField;
521        }
522        return $form_fields;
523    }
524
525    public static function getPageSection( $psPageSection ) {
526        $pageSection = PFPageSection::create( $psPageSection->getSectionName() );
527        $pageSectionArray = $psPageSection->getObject( 'pageforms_PageSection' );
528        if ( $pageSectionArray == null ) {
529            return null;
530        }
531
532        foreach ( $pageSectionArray as $var => $val ) {
533            if ( $var == 'mandatory' ) {
534                $pageSection->setIsMandatory( true );
535            } elseif ( $var == 'hidden' ) {
536                $pageSection->setIsHidden( true );
537            } elseif ( $var == 'restricted' ) {
538                $pageSection->setIsRestricted( true );
539            } else {
540                $pageSection->setSectionArgs( $var, $val );
541            }
542        }
543
544        return $pageSection;
545    }
546
547    /**
548     * Return the list of pages that Page Forms could generate from
549     * the current Page Schemas schema.
550     * @param PFPageSchemas $pageSchemaObj
551     * @return Title[]
552     */
553    public static function getPagesToGenerate( $pageSchemaObj ) {
554        $genPageList = [];
555        $psTemplates = $pageSchemaObj->getTemplates();
556        foreach ( $psTemplates as $psTemplate ) {
557            $title = Title::makeTitleSafe( NS_TEMPLATE, $psTemplate->getName() );
558            $genPageList[] = $title;
559        }
560        $form_name = self::getFormName( $pageSchemaObj );
561        if ( $form_name != null ) {
562            $title = Title::makeTitleSafe( PF_NS_FORM, $form_name );
563            $genPageList[] = $title;
564        }
565
566        return $genPageList;
567    }
568
569    /**
570     * Returns an array of PFTemplateField objects, representing the fields
571     * of a template, based on the contents of a <PageSchema> tag.
572     * @param PFTemplate $psTemplate
573     * @return PFTemplateField[]
574     */
575    public static function getFieldsFromTemplateSchema( $psTemplate ) {
576        $psFields = $psTemplate->getFields();
577        $templateFields = [];
578        foreach ( $psFields as $psField ) {
579            if ( defined( 'SMW_VERSION' ) ) {
580                $prop_array = $psField->getObject( 'semanticmediawiki_Property' );
581                $propertyName = PageSchemas::getValueFromObject( $prop_array, 'name' );
582                if ( $prop_array !== null && empty( $propertyName ) ) {
583                    $propertyName = $psField->getName();
584                }
585            } else {
586                $propertyName = null;
587            }
588
589            if ( $psField->getLabel() === '' ) {
590                $fieldLabel = $psField->getName();
591            } else {
592                $fieldLabel = $psField->getLabel();
593            }
594            $templateField = PFTemplateField::create(
595                $psField->getName(),
596                $fieldLabel,
597                $propertyName,
598                $psField->isList(),
599                $psField->getDelimiter(),
600                $psField->getDisplay()
601            );
602            $templateField->setNSText( $psField->getNamespace() );
603            if ( defined( 'CARGO_VERSION' ) ) {
604                $cargoFieldArray = $psField->getObject( 'cargo_Field' );
605                $fieldType = PageSchemas::getValueFromObject( $cargoFieldArray, 'Type' );
606                $allowedValues = PageSchemas::getValueFromObject( $cargoFieldArray, 'AllowedValues' );
607                if ( $fieldType != '' ) {
608                    $templateField->setFieldType( $fieldType );
609                    $templateField->setPossibleValues( $allowedValues );
610                }
611            }
612
613            $templateFields[] = $templateField;
614        }
615        return $templateFields;
616    }
617
618    /**
619     * Creates a form page, when called from the 'generatepages' page
620     * of Page Schemas.
621     * @param string $formName
622     * @param Title $formTitle
623     * @param array $formItems
624     * @param array $formDataFromSchema
625     * @param string $categoryName
626     */
627    public static function generateForm( $formName, $formTitle,
628        $formItems, $formDataFromSchema, $categoryName ) {
629        $includeFreeText = array_key_exists( 'inputFreeText', $formDataFromSchema );
630        $freeTextLabel = null;
631        if ( $includeFreeText && array_key_exists( 'freeTextLabel', $formDataFromSchema ) ) {
632            $freeTextLabel = $formDataFromSchema['freeTextLabel'];
633        }
634
635        $form = PFForm::create( $formName, $formItems );
636        $form->setAssociatedCategory( $categoryName );
637        if ( array_key_exists( 'PageNameFormula', $formDataFromSchema ) ) {
638            $form->setPageNameFormula( $formDataFromSchema['PageNameFormula'] );
639        }
640        if ( array_key_exists( 'CreateTitle', $formDataFromSchema ) ) {
641            $form->setCreateTitle( $formDataFromSchema['CreateTitle'] );
642        }
643        if ( array_key_exists( 'EditTitle', $formDataFromSchema ) ) {
644            $form->setEditTitle( $formDataFromSchema['EditTitle'] );
645        }
646
647        $user = RequestContext::getMain()->getUser();
648
649        $formContents = $form->createMarkup( $includeFreeText, $freeTextLabel );
650        $params = [];
651        $params['user_id'] = $user->getId();
652        $params['page_text'] = $formContents;
653        $job = new PSCreatePageJob( $formTitle, $params );
654        MediaWikiServices::getInstance()->getJobQueueGroup()->push( $job );
655    }
656
657    /**
658     * Generate pages (form and templates) specified in the list.
659     * @param PageSchemas $pageSchemaObj
660     * @param array $selectedPages
661     */
662    public static function generatePages( $pageSchemaObj, $selectedPages ) {
663        if ( $selectedPages == null ) {
664            return;
665        }
666
667        $user = RequestContext::getMain()->getUser();
668
669        $psFormItems = $pageSchemaObj->getFormItemsList();
670        $form_items = [];
671        $jobs = [];
672        $templateHackUsed = false;
673        $isCategoryNameSet = false;
674
675        // Generate every specified template
676        foreach ( $psFormItems as $psFormItem ) {
677            if ( $psFormItem['type'] == 'Template' ) {
678                $psTemplate = $psFormItem['item'];
679                $templateName = $psTemplate->getName();
680                $templateTitle = Title::makeTitleSafe( NS_TEMPLATE, $templateName );
681                $fullTemplateName = $templateTitle->getPrefixedText();
682                $template_fields = self::getFieldsFromTemplateSchema( $psTemplate );
683                // Get property for use in either #set_internal
684                // or #subobject, defined by either SIO's or
685                // SMW's Page Schemas portion. We don't need
686                // to record which one it came from, because
687                // PF's code to generate the template runs its
688                // own, similar check.
689                // @TODO - $internalObjProperty should probably
690                // have a more generic name.
691                if ( class_exists( 'SIOPageSchemas' ) ) {
692                    $internalObjProperty = SIOPageSchemas::getInternalObjectPropertyName( $psTemplate );
693                } elseif ( method_exists( 'SMWPageSchemas', 'getConnectingPropertyName' ) ) {
694                    $internalObjProperty = SMWPageSchemas::getConnectingPropertyName( $psTemplate );
695                } else {
696                    $internalObjProperty = null;
697                }
698                // TODO - actually, the category-setting should be
699                // smarter than this: if there's more than one
700                // template in the schema, it should probably be only
701                // the first non-multiple template that includes the
702                // category tag.
703                if ( $psTemplate->isMultiple() ) {
704                    $categoryName = null;
705                } else {
706                    if ( $isCategoryNameSet == false ) {
707                        $categoryName = $pageSchemaObj->getCategoryName();
708                        $isCategoryNameSet = true;
709                    } else {
710                        $categoryName = null;
711                    }
712
713                }
714                if ( method_exists( $psTemplate, 'getFormat' ) ) {
715                    $templateFormat = $psTemplate->getFormat();
716                } else {
717                    $templateFormat = null;
718                }
719
720                $pfTemplate = new PFTemplate( $templateName, $template_fields );
721                $pfTemplate->setConnectingProperty( $internalObjProperty );
722                $pfTemplate->setCategoryName( $categoryName );
723                $pfTemplate->setFormat( $templateFormat );
724
725                // Set Cargo table, if one was set in the schema.
726                $cargoArray = $psTemplate->getObject( 'cargo_TemplateDetails' );
727                if ( $cargoArray !== null ) {
728                    $cargoTable = PageSchemas::getValueFromObject( $cargoArray, 'Table' );
729                    $pfTemplate->setCargoTable( $cargoTable );
730                }
731
732                $templateText = $pfTemplate->createText();
733
734                if ( in_array( $fullTemplateName, $selectedPages ) ) {
735                    $params = [];
736                    $params['user_id'] = $user->getId();
737                    $params['page_text'] = $templateText;
738                    $jobs[] = new PSCreatePageJob( $templateTitle, $params );
739                    if ( strpos( $templateText, '{{!}}' ) > 0 ) {
740                        $templateHackUsed = true;
741                    }
742                }
743
744                $templateValues = self::getTemplateValues( $psTemplate );
745                if ( array_key_exists( 'Label', $templateValues ) ) {
746                    $templateLabel = $templateValues['Label'];
747                } else {
748                    $templateLabel = null;
749                }
750                $form_fields = self::getFormFieldInfo( $psTemplate, $template_fields );
751                // Create template info for form, for use in generating
752                // the form (if it will be generated).
753                $form_template = PFTemplateInForm::create(
754                    $templateName,
755                    $templateLabel,
756                    $psTemplate->isMultiple(),
757                    null,
758                    $form_fields
759                );
760                $form_items[] = [ 'type' => 'template', 'name' => $form_template->getTemplateName(), 'item' => $form_template ];
761            } elseif ( $psFormItem['type'] == 'Section' ) {
762                $psPageSection = $psFormItem['item'];
763                $form_section = self::getPageSection( $psPageSection );
764                if ( $form_section !== null ) {
765                    $form_section->setSectionLevel( $psPageSection->getSectionLevel() );
766                    $form_items[] = [ 'type' => 'section', 'name' => $form_section->getSectionName(), 'item' => $form_section ];
767                }
768            }
769
770        }
771
772        // Create the "!" hack template, if it's necessary
773        if ( $templateHackUsed ) {
774            $templateTitle = Title::makeTitleSafe( NS_TEMPLATE, '!' );
775            if ( !$templateTitle->exists() ) {
776                $params = [];
777                $params['user_id'] = $user->getId();
778                $params['page_text'] = '|';
779                $jobs[] = new PSCreatePageJob( $templateTitle, $params );
780            }
781        }
782
783        MediaWikiServices::getInstance()->getJobQueueGroup()->push( $jobs );
784
785        // Create form, if it's specified.
786        $formName = self::getFormName( $pageSchemaObj );
787        $categoryName = $pageSchemaObj->getCategoryName();
788        if ( !empty( $formName ) ) {
789            $formInfo = self::getMainFormInfo( $pageSchemaObj );
790            $formTitle = Title::makeTitleSafe( PF_NS_FORM, $formName );
791            $fullFormName = $formTitle->getPrefixedText();
792            if ( in_array( $fullFormName, $selectedPages ) ) {
793                self::generateForm( $formName, $formTitle,
794                    $form_items, $formInfo, $categoryName );
795            }
796        }
797    }
798
799    public static function getSchemaDisplayValues( $schemaXML ) {
800        foreach ( $schemaXML->children() as $tag => $child ) {
801            if ( $tag == "pageforms_Form" ) {
802                $formName = $child->attributes()->name;
803                $values = [];
804                foreach ( $child->children() as $tagName => $prop ) {
805                    $values[$tagName] = (string)$prop;
806                }
807                return [ $formName, $values ];
808            }
809        }
810        return null;
811    }
812
813    public static function getTemplateValues( $psTemplate ) {
814        // TODO - fix this.
815        $values = [];
816        if ( $psTemplate instanceof PSTemplate ) {
817            $psTemplate = $psTemplate->getXML();
818        }
819        // @phan-suppress-next-line PhanNonClassMethodCall
820        foreach ( $psTemplate->children() as $tag => $child ) {
821            if ( $tag == "pageforms_TemplateDetails" ) {
822                foreach ( $child->children() as $prop ) {
823                    $values[$prop->getName()] = (string)$prop;
824                }
825            }
826        }
827        return $values;
828    }
829
830    public static function getTemplateDisplayString() {
831        return wfMessage( 'pf-pageschemas-templatedetails' )->escaped();
832    }
833
834    /**
835     * Displays form details for one template in the Page Schemas XML.
836     * @param string $templateXML
837     * @return null|array
838     */
839    public static function getTemplateDisplayValues( $templateXML ) {
840        $templateValues = self::getTemplateValues( $templateXML );
841        if ( count( $templateValues ) == 0 ) {
842            return null;
843        }
844
845        $displayValues = [];
846        foreach ( $templateValues as $key => $value ) {
847            if ( $key == 'Label' ) {
848                $propName = wfMessage( 'exif-label' )->escaped();
849            } elseif ( $key == 'AddAnotherText' ) {
850                $propName = "'Add another' button";
851            }
852            $displayValues[$propName] = $value;
853        }
854        return [ null, $displayValues ];
855    }
856
857    public static function getFieldDisplayString() {
858        return wfMessage( 'pf-pageschemas-forminput' )->parse();
859    }
860
861    public static function getPageSectionDisplayString() {
862        return wfMessage( 'ps-otherparams' )->text();
863    }
864
865    /**
866     * Displays data on a single form input in the Page Schemas XML.
867     * @param Node $fieldXML
868     * @return array|null
869     * @suppress PhanUndeclaredTypeParameter
870     */
871    public static function getFieldDisplayValues( $fieldXML ) {
872        foreach ( $fieldXML->children() as $tag => $child ) {
873            if ( $tag == "pageforms_FormInput" ) {
874                $inputName = $child->attributes()->name;
875                $values = [];
876                foreach ( $child->children() as $prop ) {
877                    if ( $prop->getName() == 'InputType' ) {
878                        $propName = 'Input type';
879                    } else {
880                        $propName = (string)$prop->attributes()->name;
881                    }
882                    $values[$propName] = (string)$prop;
883                }
884                return [ $inputName, $values ];
885            }
886        }
887        return null;
888    }
889
890    public static function getPageSectionDisplayValues( $pageSectionXML ) {
891        foreach ( $pageSectionXML->children() as $tag => $child ) {
892            if ( $tag == "pageforms_PageSection" ) {
893                $inputName = $child->attributes()->name;
894                $values = [];
895                foreach ( $child->children() as $prop ) {
896                    $propName = (string)$prop->attributes()->name;
897                    $values[$propName] = (string)$prop;
898                }
899                return [ $inputName, $values ];
900            }
901        }
902        return null;
903    }
904}