1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
use SMW\DataValueFactory; |
4
|
|
|
use SMW\DataValues\AbstractMultiValue; |
5
|
|
|
use SMW\ApplicationFactory; |
6
|
|
|
use SMW\DIProperty; |
7
|
|
|
use SMW\DIWikiPage; |
8
|
|
|
use SMWPropertyListValue as PropertyListValue; |
9
|
|
|
use SMWDataItem as DataItem; |
10
|
|
|
use SMWContainerSemanticData as ContainerSemanticData; |
11
|
|
|
use SMWDIContainer as DIContainer; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* SMWDataValue implements the handling of small sets of property-value pairs. |
15
|
|
|
* The declaration of Records in SMW uses the order of values to encode the |
16
|
|
|
* property that should be used, so the user only needs to enter a list of |
17
|
|
|
* values. Internally, however, the property-value assignments are not stored |
18
|
|
|
* with a particular order; they will only be ordered for display, following |
19
|
|
|
* the declaration. This is why it is not supported to have Records using the |
20
|
|
|
* same property for more than one value. |
21
|
|
|
* |
22
|
|
|
* The class uses DIContainer objects to return its inner state. See the |
23
|
|
|
* documentation for DIContainer for details on how this "pseudo" data |
24
|
|
|
* encapsulated many property assignments. Such data is stored internally |
25
|
|
|
* like a page with various property-value assignments. Indeed, record values |
26
|
|
|
* can be created from DIWikiPage objects (the missing information will |
27
|
|
|
* be fetched from the store). |
28
|
|
|
* |
29
|
|
|
* @todo Enforce limitation of maximal number of values. |
30
|
|
|
* @todo Enforce uniqueness of properties in declaration. |
31
|
|
|
* @todo Complete internationalisation. |
32
|
|
|
* |
33
|
|
|
* @author Markus Krötzsch |
34
|
|
|
* @ingroup SMWDataValues |
35
|
|
|
*/ |
36
|
|
|
class SMWRecordValue extends AbstractMultiValue { |
|
|
|
|
37
|
|
|
|
38
|
|
|
/// cache for properties for the fields of this data value |
39
|
|
|
protected $m_diProperties = null; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @param string $typeid |
43
|
|
|
*/ |
44
|
31 |
|
public function __construct( $typeid = '' ) { |
45
|
31 |
|
parent::__construct( '_rec' ); |
46
|
31 |
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @since 2.3 |
50
|
|
|
* |
51
|
|
|
* @return DIProperty[]|null |
52
|
|
|
*/ |
53
|
1 |
|
public function getProperties() { |
54
|
1 |
|
return $this->m_diProperties; |
|
|
|
|
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @since 2.3 |
59
|
|
|
* |
60
|
|
|
* @param string $value |
61
|
|
|
* |
62
|
|
|
* @return array |
63
|
|
|
*/ |
64
|
28 |
|
public function getValuesFromString( $value ) { |
65
|
|
|
// #664 / T17732 |
66
|
28 |
|
$value = str_replace( "\;", "-3B", $value ); |
67
|
|
|
|
68
|
|
|
// Bug 21926 / T23926 |
69
|
|
|
// Values that use html entities are encoded with a semicolon |
70
|
28 |
|
$value = htmlspecialchars_decode( $value, ENT_QUOTES ); |
71
|
28 |
|
$values = preg_split( '/[\s]*;[\s]*/u', trim( $value ) ); |
72
|
|
|
|
73
|
28 |
|
return str_replace( "-3B", ";", $values ); |
74
|
|
|
} |
75
|
|
|
|
76
|
24 |
|
protected function parseUserValue( $value ) { |
77
|
|
|
|
78
|
24 |
|
if ( $value === '' ) { |
79
|
1 |
|
$this->addErrorMsg( array( 'smw_novalues' ) ); |
80
|
1 |
|
return; |
81
|
|
|
} |
82
|
|
|
|
83
|
23 |
|
$containerSemanticData = $this->newContainerSemanticData( $value ); |
84
|
23 |
|
$sortKeys = array(); |
85
|
|
|
|
86
|
23 |
|
$values = $this->getValuesFromString( $value ); |
87
|
23 |
|
$valueIndex = 0; // index in value array |
88
|
23 |
|
$propertyIndex = 0; // index in property list |
89
|
23 |
|
$empty = true; |
90
|
|
|
|
91
|
23 |
|
foreach ( $this->getPropertyDataItems() as $diProperty ) { |
|
|
|
|
92
|
|
|
|
93
|
23 |
|
if ( !array_key_exists( $valueIndex, $values ) || $this->getErrors() !== array() ) { |
94
|
1 |
|
break; // stop if there are no values left |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
// generating the DVs: |
98
|
23 |
|
if ( ( $values[$valueIndex] === '' ) || ( $values[$valueIndex] == '?' ) ) { // explicit omission |
99
|
1 |
|
$valueIndex++; |
100
|
|
|
} else { |
101
|
23 |
|
$dataValue = DataValueFactory::getInstance()->newDataValueByProperty( |
102
|
|
|
$diProperty, |
103
|
23 |
|
$values[$valueIndex], |
|
|
|
|
104
|
23 |
|
false, |
105
|
23 |
|
$this->getContextPage() |
106
|
|
|
); |
107
|
|
|
|
108
|
23 |
|
if ( $dataValue->isValid() ) { // valid DV: keep |
109
|
23 |
|
$containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() ); |
110
|
23 |
|
$sortKeys[] = $dataValue->getDataItem()->getSortKey(); |
111
|
|
|
|
112
|
23 |
|
$valueIndex++; |
113
|
23 |
|
$empty = false; |
114
|
1 |
|
} elseif ( ( count( $values ) - $valueIndex ) == ( count( $this->m_diProperties ) - $propertyIndex ) ) { |
115
|
1 |
|
$containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() ); |
116
|
1 |
|
$this->addError( $dataValue->getErrors() ); |
117
|
1 |
|
++$valueIndex; |
118
|
|
|
} |
119
|
|
|
} |
120
|
23 |
|
++$propertyIndex; |
121
|
|
|
} |
122
|
|
|
|
123
|
23 |
|
if ( $empty && $this->getErrors() === array() ) { |
124
|
|
|
$this->addErrorMsg( array( 'smw_novalues' ) ); |
125
|
|
|
} |
126
|
|
|
|
127
|
23 |
|
$this->m_dataitem = new DIContainer( $containerSemanticData ); |
128
|
|
|
|
129
|
|
|
// Composite sortkey is to ensure that Store::getPropertyValues can |
130
|
|
|
// apply sorting during value selection |
131
|
23 |
|
$this->m_dataitem->addCompositeSortKey( implode( ';', $sortKeys ) ); |
132
|
23 |
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* @see SMWDataValue::loadDataItem() |
136
|
|
|
* @param $dataitem DataItem |
137
|
|
|
* @return boolean |
138
|
|
|
*/ |
139
|
7 |
|
protected function loadDataItem( DataItem $dataItem ) { |
140
|
7 |
|
if ( $dataItem->getDIType() == DataItem::TYPE_CONTAINER ) { |
141
|
|
|
$this->m_dataitem = $dataItem; |
142
|
|
|
return true; |
143
|
7 |
|
} elseif ( $dataItem->getDIType() == DataItem::TYPE_WIKIPAGE ) { |
144
|
7 |
|
$semanticData = new ContainerSemanticData( $dataItem ); |
|
|
|
|
145
|
7 |
|
$semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) ); |
|
|
|
|
146
|
7 |
|
$this->m_dataitem = new DIContainer( $semanticData ); |
147
|
7 |
|
return true; |
148
|
|
|
} else { |
149
|
2 |
|
return false; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
8 |
|
public function getShortWikiText( $linked = null ) { |
154
|
8 |
|
if ( $this->m_caption !== false ) { |
155
|
|
|
return $this->m_caption; |
156
|
|
|
} |
157
|
8 |
|
return $this->makeOutputText( 0, $linked ); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
public function getShortHTMLText( $linker = null ) { |
161
|
|
|
if ( $this->m_caption !== false ) { |
162
|
|
|
return $this->m_caption; |
163
|
|
|
} |
164
|
|
|
return $this->makeOutputText( 1, $linker ); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
public function getLongWikiText( $linked = null ) { |
168
|
|
|
return $this->makeOutputText( 2, $linked ); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
public function getLongHTMLText( $linker = null ) { |
172
|
|
|
return $this->makeOutputText( 3, $linker ); |
173
|
|
|
} |
174
|
|
|
|
175
|
9 |
|
public function getWikiValue() { |
176
|
9 |
|
return $this->makeOutputText( 4 ); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/// @todo Allowed values for multi-valued properties are not supported yet. |
180
|
22 |
|
protected function checkAllowedValues() { |
181
|
22 |
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Make sure that the content is reset in this case. |
185
|
|
|
* @todo This is not a full reset yet (the case that property is changed after a value |
186
|
|
|
* was set does not occur in the normal flow of things, hence this has low priority). |
187
|
|
|
*/ |
188
|
21 |
|
public function setProperty( DIProperty $property ) { |
189
|
21 |
|
parent::setProperty( $property ); |
|
|
|
|
190
|
21 |
|
$this->m_diProperties = null; |
191
|
21 |
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @since 2.1 |
195
|
|
|
* |
196
|
|
|
* @param DIProperty[] $properties |
197
|
|
|
*/ |
198
|
8 |
|
public function setFieldProperties( array $properties ) { |
199
|
8 |
|
foreach ( $properties as $property ) { |
200
|
8 |
|
if ( $property instanceof DIProperty ) { |
201
|
8 |
|
$this->m_diProperties[] = $property; |
202
|
|
|
} |
203
|
|
|
} |
204
|
8 |
|
} |
205
|
|
|
|
206
|
|
|
////// Additional API for value lists |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @deprecated as of 1.6, use getDataItems instead |
210
|
|
|
* |
211
|
|
|
* @return array of DataItem |
212
|
|
|
*/ |
213
|
|
|
public function getDVs() { |
214
|
|
|
return $this->getDataItems(); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* @since 1.6 |
219
|
|
|
* |
220
|
|
|
* {@inheritDoc} |
221
|
|
|
*/ |
222
|
8 |
|
public function getDataItems() { |
223
|
8 |
|
return parent::getDataItems(); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Return the array (list) of properties that the individual entries of |
228
|
|
|
* this datatype consist of. |
229
|
|
|
* |
230
|
|
|
* @since 1.6 |
231
|
|
|
* |
232
|
|
|
* @todo I18N for error message. |
233
|
|
|
* |
234
|
|
|
* @return array of DIProperty |
235
|
|
|
*/ |
236
|
28 |
|
public function getPropertyDataItems() { |
237
|
28 |
|
|
238
|
20 |
|
if ( $this->m_diProperties !== null ) { |
239
|
|
|
return $this->m_diProperties; |
240
|
20 |
|
} |
241
|
6 |
|
|
242
|
|
|
$this->m_diProperties = $this->getFieldProperties( $this->m_property ); |
243
|
|
|
|
244
|
|
|
if ( $this->m_diProperties === array() ) { // TODO internalionalize |
245
|
28 |
|
$this->addError( 'The list of properties to be used for the data fields has not been specified properly.' ); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
return $this->m_diProperties; |
|
|
|
|
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
////// Internal helper functions |
252
|
|
|
|
253
|
|
|
protected function makeOutputText( $type = 0, $linker = null ) { |
254
|
|
|
if ( !$this->isValid() ) { |
255
|
|
|
return ( ( $type == 0 ) || ( $type == 1 ) ) ? '' : $this->getErrorText(); |
256
|
|
|
} |
257
|
|
|
|
258
|
20 |
|
$result = ''; |
259
|
20 |
|
$i = 0; |
260
|
20 |
|
foreach ( $this->getPropertyDataItems() as $propertyDataItem ) { |
|
|
|
|
261
|
|
|
if ( $i == 1 ) { |
262
|
20 |
|
$result .= ( $type == 4 ) ? '; ' : ' ('; |
263
|
20 |
|
} elseif ( $i > 1 ) { |
264
|
20 |
|
$result .= ( $type == 4 ) ? '; ' : ', '; |
265
|
|
|
} |
266
|
20 |
|
++$i; |
267
|
20 |
|
$propertyValues = $this->m_dataitem->getSemanticData()->getPropertyValues( $propertyDataItem ); // combining this with next line violates PHP strict standards |
|
|
|
|
268
|
20 |
|
$dataItem = reset( $propertyValues ); |
269
|
|
|
if ( $dataItem !== false ) { |
270
|
20 |
|
$dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $propertyDataItem ); |
271
|
20 |
|
$result .= $this->makeValueOutputText( $type, $dataValue, $linker ); |
272
|
|
|
} else { |
273
|
|
|
$result .= '?'; |
274
|
|
|
} |
275
|
|
|
} |
276
|
|
|
if ( ( $i > 1 ) && ( $type != 4 ) ) { |
277
|
6 |
|
$result .= ')'; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
return $result; |
281
|
|
|
} |
282
|
13 |
|
|
283
|
13 |
|
protected function makeValueOutputText( $type, $dataValue, $linker ) { |
284
|
|
|
switch ( $type ) { |
285
|
|
|
case 0: |
286
|
|
|
return $dataValue->getShortWikiText( $linker ); |
287
|
13 |
|
case 1: |
288
|
13 |
|
return $dataValue->getShortHTMLText( $linker ); |
289
|
13 |
|
case 2: |
290
|
13 |
|
return $dataValue->getShortWikiText( $linker ); |
291
|
13 |
|
case 3: |
292
|
13 |
|
return $dataValue->getShortHTMLText( $linker ); |
293
|
1 |
|
case 4: |
294
|
|
|
return str_replace( ";", "\;", $dataValue->getWikiValue() ); |
295
|
13 |
|
} |
296
|
13 |
|
} |
297
|
13 |
|
|
298
|
13 |
|
private function newContainerSemanticData( $value ) { |
299
|
13 |
|
|
300
|
13 |
|
if ( $this->m_contextPage === null ) { |
301
|
|
|
$containerSemanticData = ContainerSemanticData::makeAnonymousContainer(); |
302
|
13 |
|
$containerSemanticData->skipAnonymousCheck(); |
303
|
|
|
} else { |
304
|
|
|
$subobjectName = '_' . hash( 'md4', $value, false ); // md4 is probably fastest of PHP's hashes |
305
|
13 |
|
|
306
|
8 |
|
$subject = new DIWikiPage( |
307
|
|
|
$this->m_contextPage->getDBkey(), |
308
|
|
|
$this->m_contextPage->getNamespace(), |
309
|
13 |
|
$this->m_contextPage->getInterwiki(), |
310
|
|
|
$subobjectName |
311
|
|
|
); |
312
|
13 |
|
|
313
|
|
|
$containerSemanticData = new ContainerSemanticData( $subject ); |
314
|
13 |
|
} |
315
|
8 |
|
|
316
|
9 |
|
return $containerSemanticData; |
317
|
|
|
} |
318
|
9 |
|
|
319
|
|
|
} |
320
|
|
|
|
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.