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