Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 199
0.00% covered (danger)
0.00%
0 / 35
CRAP
0.00% covered (danger)
0.00%
0 / 1
PFTemplateField
0.00% covered (danger)
0.00%
0 / 199
0.00% covered (danger)
0.00%
0 / 35
11130
0.00% covered (danger)
0.00%
0 / 1
 create
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
30
 toWikitext
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
210
 newFromParams
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
210
 setTypeAndPossibleValues
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
72
 setSemanticProperty
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 setCargoFieldData
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
72
 getFieldName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getValueLabels
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLabel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSemanticProperty
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPropertyType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExpectedCargoField
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getFullCargoField
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 getFieldType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRealFieldType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPossibleValues
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getHierarchyStructure
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isList
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDelimiter
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDisplay
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNSText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNamespace
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCategory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getForm
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 isMandatory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isUnique
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRegex
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHoldsTemplate
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLabel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setNamespace
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setNSText
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 setFieldType
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 setPossibleValues
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setHierarchyStructure
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 createText
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
342
1<?php
2
3use MediaWiki\Title\Title;
4
5/**
6 * Defines a class, PFTemplateField, that represents a field in a template,
7 * including any possible Cargo or SMW storage it may have. Used in both
8 * creating templates and displaying user-created forms.
9 *
10 * @author Yaron Koren
11 * @file
12 * @ingroup PF
13 */
14
15class PFTemplateField {
16    private $mFieldName;
17    private $mValueLabels;
18    private $mLabel;
19
20    /**
21     * SMW-specific fields
22     */
23    private $mSemanticProperty;
24    private $mPropertyType;
25
26    /**
27     * Cargo-specific fields
28     */
29    private $mCargoTable;
30    private $mCargoField;
31    private $mFieldType;
32    private $mRealFieldType = null;
33    private $mHierarchyStructure;
34
35    private $mPossibleValues;
36    private $mIsList;
37    private $mDelimiter;
38    private $mDisplay;
39    private $mNSText = null;
40    private $mNamespace = 0;
41    private $mCategory = null;
42    private $mForm = null;
43    private $mIsMandatory = false;
44    private $mIsUnique = false;
45    private $mRegex = null;
46    private $mHoldsTemplate = null;
47
48    static function create( $name, $label, $semanticProperty = null, $isList = null, $delimiter = null, $display = null ) {
49        $f = new PFTemplateField();
50        $f->mFieldName = trim( str_replace( '\\', '', $name ) );
51        if ( $label !== null ) {
52            // Keep this field null if no value was set.
53            $f->mLabel = trim( str_replace( '\\', '', $label ) );
54        }
55        if ( $semanticProperty !== null ) {
56            $f->setSemanticProperty( $semanticProperty );
57        }
58        $f->mIsList = $isList;
59        $f->mDelimiter = $delimiter;
60        $f->mDisplay = $display;
61        // Delimiter should default to ','.
62        if ( $isList && !$delimiter ) {
63            $f->mDelimiter = ',';
64        }
65        return $f;
66    }
67
68    public function toWikitext() {
69        $attribsStrings = [];
70        // Only include the label if it's different from the field name.
71        if ( $this->mLabel != '' &&
72            ( $this->mLabel !== $this->mFieldName ) ) {
73            $attribsStrings['label'] = $this->mLabel;
74        }
75        if ( $this->mCargoField != '' && $this->mCargoField !== str_replace( ' ', '_', $this->mFieldName ) ) {
76            $attribsStrings['cargo field'] = $this->mCargoField;
77        }
78        // Only set list and delimiter information if there's no Cargo
79        // field - if there is, they will be set in #cargo_declare.
80        if ( $this->mCargoField == '' ) {
81            if ( $this->mIsList == true ) {
82                $attribsStrings['list'] = true;
83                if ( $this->mDelimiter != ',' ) {
84                    $attribsStrings['delimiter'] = $this->mDelimiter;
85                }
86            }
87        }
88        if ( $this->mSemanticProperty != '' ) {
89            $attribsStrings['property'] = $this->mSemanticProperty;
90        }
91        if ( $this->mNSText != null ) {
92            $attribsStrings['namespace'] = $this->mNSText;
93        }
94        if ( $this->mDisplay != '' ) {
95            $attribsStrings['display'] = $this->mDisplay;
96        }
97        $text = $this->mFieldName;
98        if ( count( $attribsStrings ) > 0 ) {
99            $attribsFullStrings = [];
100            foreach ( $attribsStrings as $key => $value ) {
101                if ( $value === true ) {
102                    $attribsFullStrings[] = $key;
103                } else {
104                    $attribsFullStrings[] = "$key=$value";
105                }
106            }
107            $text .= ' (' . implode( ';', $attribsFullStrings ) . ')';
108        }
109        return $text;
110    }
111
112    static function newFromParams( $fieldName, $fieldParams ) {
113        $f = new PFTemplateField();
114        $f->mFieldName = $fieldName;
115        foreach ( $fieldParams as $key => $value ) {
116            if ( $key == 'label' ) {
117                $f->mLabel = $value;
118            } elseif ( $key == 'cargo field' ) {
119                $f->mCargoField = $value;
120            } elseif ( $key == 'property' ) {
121                $f->setSemanticProperty( $value );
122            } elseif ( $key == 'list' ) {
123                $f->mIsList = true;
124            } elseif ( $key == 'delimiter' ) {
125                $f->mDelimiter = $value;
126            } elseif ( $key == 'namespace' ) {
127                $f->setNSText( $value );
128            } elseif ( $key == 'display' ) {
129                $f->mDisplay = $value;
130            } elseif ( $key == 'holds template' ) {
131                $f->mHoldsTemplate = $value;
132            } elseif ( $key == 'category' ) {
133                $f->mCategory = $value;
134            } elseif ( $key == 'form' ) {
135                $f->mForm = $value;
136            }
137        }
138        // Delimiter should default to ','.
139        if ( $f->mIsList && !$f->mDelimiter ) {
140            $f->mDelimiter = ',';
141        }
142        return $f;
143    }
144
145    function setTypeAndPossibleValues() {
146        if ( !defined( 'SMW_NS_PROPERTY' ) ) {
147            return;
148        }
149
150        // The presence of "-" at the beginning of a property name
151        // (which happens if PF tries to parse an inverse query)
152        // leads to an error in SMW - just exit if that's the case.
153        if ( strpos( $this->mSemanticProperty, '-' ) === 0 ) {
154            return;
155        }
156
157        $proptitle = Title::makeTitleSafe( SMW_NS_PROPERTY, $this->mSemanticProperty );
158        if ( $proptitle === null ) {
159            return;
160        }
161
162        $store = PFUtils::getSMWStore();
163        // this returns an array of objects
164        $allowed_values = PFValuesUtils::getSMWPropertyValues( $store, $proptitle, "Allows value" );
165        if ( empty( $allowed_values ) ) {
166            $allowed_values = PFValuesUtils::getSMWPropertyValues( $store, $proptitle, "Allows value list" );
167        }
168        $label_formats = PFValuesUtils::getSMWPropertyValues( $store, $proptitle, "Has field label format" );
169        $propValue = \SMW\DIProperty::newFromUserLabel( $this->mSemanticProperty );
170        $this->mPropertyType = $propValue->findPropertyTypeID();
171
172        foreach ( $allowed_values as $allowed_value ) {
173            // HTML-unencode each value
174            $wiki_value = html_entity_decode( $allowed_value );
175            $this->mPossibleValues[] = $wiki_value;
176            if ( count( $label_formats ) > 0 ) {
177                $label_format = $label_formats[0];
178                $prop_instance = \SMW\DataValueFactory::findTypeID( $this->mPropertyType );
179                $label_value = \SMW\DataValueFactory::newTypeIDValue( $prop_instance, $wiki_value );
180                $label_value->setOutputFormat( $label_format );
181                $this->mValueLabels[$wiki_value] = html_entity_decode( $label_value->getWikiValue() );
182            }
183        }
184
185        // HACK - if there were any possible values, set the property
186        // type to be 'enumeration', regardless of what the actual type is
187        if ( count( $this->mPossibleValues ) > 0 ) {
188            $this->mPropertyType = 'enumeration';
189        }
190    }
191
192    /**
193     * Called if a matching property is found for a template field when
194     * a template is parsed during the creation of a form.
195     * @param string $semantic_property
196     */
197    function setSemanticProperty( $semantic_property ) {
198        $this->mSemanticProperty = str_replace( '\\', '', $semantic_property );
199        $this->mPossibleValues = [];
200        // set field type and possible values, if any
201        $this->setTypeAndPossibleValues();
202    }
203
204    /**
205     * Equivalent to setSemanticProperty(), but called when using Cargo
206     * instead of SMW.
207     * @param string $tableName
208     * @param string $fieldName
209     * @param CargoFieldDescription|null $fieldDescription
210     */
211    function setCargoFieldData( $tableName, $fieldName, $fieldDescription = null ) {
212        $this->mCargoTable = $tableName;
213        $this->mCargoField = $fieldName;
214
215        if ( $fieldDescription === null ) {
216            $fieldDescription = PFUtils::getCargoFieldDescription( $tableName, $fieldName );
217            if ( $fieldDescription == null ) {
218                return;
219            }
220        }
221
222        // We have some "pseudo-types", used for setting the correct
223        // form input.
224        if ( $fieldDescription->mAllowedValues != null ) {
225            if ( $fieldDescription->mIsHierarchy == true ) {
226                $this->mFieldType = 'Hierarchy';
227                $this->mHierarchyStructure = $fieldDescription->mHierarchyStructure;
228            } else {
229                $this->mFieldType = 'Enumeration';
230            }
231            $this->mRealFieldType = $fieldDescription->mType;
232        } elseif ( $fieldDescription->mType == 'Text' && $fieldDescription->mSize != '' && $fieldDescription->mSize <= 100 ) {
233            $this->mFieldType = 'String';
234        } else {
235            $this->mFieldType = $fieldDescription->mType;
236        }
237        $this->mIsList = $fieldDescription->mIsList;
238        $this->mDelimiter = $fieldDescription->getDelimiter();
239        $this->mPossibleValues = $fieldDescription->mAllowedValues;
240        $this->mIsMandatory = $fieldDescription->mIsMandatory;
241        $this->mIsUnique = $fieldDescription->mIsUnique;
242        $this->mRegex = $fieldDescription->mRegex;
243    }
244
245    function getFieldName() {
246        return $this->mFieldName;
247    }
248
249    function getValueLabels() {
250        return $this->mValueLabels;
251    }
252
253    function getLabel() {
254        return $this->mLabel;
255    }
256
257    function getSemanticProperty() {
258        return $this->mSemanticProperty;
259    }
260
261    function getPropertyType() {
262        return $this->mPropertyType;
263    }
264
265    function getExpectedCargoField() {
266        if ( $this->mCargoField != '' ) {
267            return $this->mCargoField;
268        } else {
269            return str_replace( ' ', '_', $this->mFieldName );
270        }
271    }
272
273    function getFullCargoField() {
274        if ( $this->mCargoTable == '' || $this->mCargoField == '' ) {
275            return null;
276        }
277        return $this->mCargoTable . '|' . $this->mCargoField;
278    }
279
280    function getFieldType() {
281        return $this->mFieldType;
282    }
283
284    function getRealFieldType() {
285        return $this->mRealFieldType;
286    }
287
288    function getPossibleValues() {
289        if ( $this->mPossibleValues == null ) {
290            return [];
291        }
292        return $this->mPossibleValues;
293    }
294
295    function getHierarchyStructure() {
296        return $this->mHierarchyStructure;
297    }
298
299    function isList() {
300        return $this->mIsList;
301    }
302
303    function getDelimiter() {
304        return $this->mDelimiter;
305    }
306
307    function getDisplay() {
308        return $this->mDisplay;
309    }
310
311    function getNSText() {
312        return $this->mNSText;
313    }
314
315    function getNamespace() {
316        return $this->mNamespace;
317    }
318
319    function getCategory() {
320        return $this->mCategory;
321    }
322
323    function getForm() {
324        // @todo - add alternate forms here too.
325        if ( $this->mForm !== null ) {
326            return $this->mForm;
327        }
328
329        $defaultFormForNamespace = PFFormLinker::getDefaultFormForNamespace( $this->mNamespace );
330        if ( $defaultFormForNamespace !== null ) {
331            $this->mForm = $defaultFormForNamespace;
332            return $defaultFormForNamespace;
333        }
334
335        if ( $this->mCategory != null ) {
336            $categoryPage = Title::makeTitleSafe( NS_CATEGORY, $this->mCategory );
337            $defaultFormForCategory = PFFormLinker::getDefaultForm( $categoryPage );
338            if ( $defaultFormForCategory !== null ) {
339                $this->mForm = $defaultFormForCategory;
340                return $defaultFormForCategory;
341            }
342        }
343
344        return null;
345    }
346
347    function isMandatory() {
348        return $this->mIsMandatory;
349    }
350
351    function isUnique() {
352        return $this->mIsUnique;
353    }
354
355    function getRegex() {
356        return $this->mRegex;
357    }
358
359    function getHoldsTemplate() {
360        return $this->mHoldsTemplate;
361    }
362
363    function setLabel( $label ) {
364        $this->mLabel = $label;
365    }
366
367    function setNamespace( $namespace ) {
368        $this->mNSText = PFUtils::getCanonicalName( $namespace );
369        $this->mNamespace = $namespace;
370    }
371
372    function setNSText( $nsText ) {
373        $this->mNSText = $nsText;
374        // Translate from text to ID.
375        $dummyTitle = Title::newFromText( $nsText . ':ABC' );
376        $this->mNamespace = $dummyTitle->getNamespace();
377    }
378
379    function setFieldType( $fieldType ) {
380        $this->mFieldType = $fieldType;
381
382        if ( $fieldType == 'File' ) {
383            $this->mNSText = PFUtils::getCanonicalName( NS_FILE );
384            $this->mNamespace = NS_FILE;
385        }
386    }
387
388    function setPossibleValues( $possibleValues ) {
389        $this->mPossibleValues = $possibleValues;
390    }
391
392    function setHierarchyStructure( $hierarchyStructure ) {
393        $this->mHierarchyStructure = $hierarchyStructure;
394    }
395
396    function createText( $cargoInUse ) {
397        $fieldProperty = $this->mSemanticProperty;
398        // If this field is meant to contain a list, and the field has
399        // an associated SMW property, add on an 'arraymap' function,
400        // which will call the property tag on every element in the
401        // list. If, on the other hand, it uses Cargo, use #arraymap
402        // just for the link - but only if it's of type "Page".
403        if ( $this->mIsList && ( $fieldProperty != '' ||
404            ( $cargoInUse && $this->mFieldType == 'Page' ) ) ) {
405            // Find a string that's not in the property
406            // name, to be used as the variable.
407            // Default is "x" - also use this if all the attempts fail.
408            $var = "x";
409            if ( strstr( $fieldProperty, $var ) ) {
410                $var_options = [ 'y', 'z', 'xx', 'yy', 'zz', 'aa', 'bb', 'cc' ];
411                foreach ( $var_options as $option ) {
412                    if ( !strstr( $fieldProperty, $option ) ) {
413                        $var = $option;
414                        break;
415                    }
416                }
417            }
418            $text = "{{#arraymap:{{{" . $this->mFieldName . '|}}}|' . $this->mDelimiter . "|$var|[[";
419            if ( $fieldProperty == '' ) {
420                $text .= "$var]]";
421            } elseif ( $this->mNamespace == 0 ) {
422                $text .= "$fieldProperty::$var]]";
423            } else {
424                $text .= $this->mNamespace . ":$var]] {{#set:" . $fieldProperty . "=$var}} ";
425            }
426            // If the delimiter isn't a comma, use that as the "new delimiter" as well.
427            if ( $this->mDelimiter !== ',' ) {
428                $text .= '|' . $this->mDelimiter . '\s';
429            }
430            // Close #arraymap call.
431            $text .= "}}\n";
432            return $text;
433        }
434
435        // Not a list.
436        $fieldParam = '{{{' . $this->mFieldName . '|}}}';
437        if ( $this->mNamespace == 0 ) {
438            $fieldString = $fieldParam;
439        } else {
440            $fieldString = $this->mNamespace . ':' . $fieldParam;
441        }
442
443        if ( $fieldProperty == '' ) {
444            if ( $cargoInUse && ( $this->mFieldType == 'Page' || $this->mFieldType == 'File' ) ) {
445                $fieldString = "[[$fieldString]]";
446                // Add an #if around the link, to prevent
447                // anything from getting displayed on the
448                // screen for blank values, if the
449                // ParserFunctions extension is installed.
450                if ( ExtensionRegistry::getInstance()->isLoaded( 'ParserFunctions' ) ) {
451                    $fieldString = "{{#if:$fieldParam|$fieldString}}";
452                }
453                return $fieldString;
454            }
455            return $fieldString;
456        } elseif ( $this->mNamespace == 0 ) {
457            return "[[$fieldProperty::$fieldString]]";
458        } else {
459            // Special handling is needed, for at
460            // least the File and Category namespaces.
461            return "[[$fieldString]] {{#set:$fieldProperty=$fieldString}}";
462        }
463    }
464
465}