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