Completed
Push — master ( 2472c5...a814e8 )
by mw
35:03
created

src/DataValues/ReferenceValue.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SMW\DataValues;
4
5
use SMW\ApplicationFactory;
6
use SMW\DataValueFactory;
7
use SMW\DataValues\ValueFormatters\DataValueFormatter;
8
use SMW\DIProperty;
9
use SMW\DIWikiPage;
10
use SMW\Message;
11
use SMWContainerSemanticData as ContainerSemanticData;
12
use SMWDataItem as DataItem;
13
use SMWDataValue as DataValue;
14
use SMWDIContainer as DIContainer;
15
use SMWPropertyListValue as PropertyListValue;
16
17
/**
18
 * ReferenceValue allows to define additional DV to describe the state of a
19
 * SourceValue in terms of provenance or referential evidence. ReferenceValue is
20
 * stored as separate entity to the original subject in order to encapsulate the
21
 * SourceValue from the remaining annotations kept in reference to a subject.
22
 *
23
 * Defining which fields are required can vary and therefore is left to the user
24
 * to specify such requirements using the `'Has fields' property.
25
 *
26
 * For example, declaring `[[Has fields::SomeValue;Date;SomeUrl;...]]` on a
27
 * `SomeProperty` property page is to define:
28
 *
29
 * - a property called `SomeValue` with its own specification
30
 * - a date property with the Date type
31
 * - a property called `SomeUrl` with its own specification
32
 * - ... any other property the users expects to require when making a value
33
 *   annotation of this type
34
 *
35
 * An annotation `[[SomeProperty::Foo;12-12-1212;http://example.org]]` is expected
36
 * to be a concatenated string and be separated by ';' to indicate the next value
37
 * string which will corespondent to the index of the `Has fields` declaration.
38
 *
39
 * @license GNU GPL v2+
40
 * @since 2.5
41
 *
42
 * @author mwjames
43
 */
44
class ReferenceValue extends AbstractMultiValue {
45
46
	/**
47
	 * @var DIProperty[]|null
48
	 */
49
	private $properties = null;
50
51
	/**
52
	 * @param string $typeid
53
	 */
54 8
	public function __construct( $typeid = '' ) {
55 8
		parent::__construct( '_ref_rec' );
56 8
	}
57
58
	/**
59
	 * @since 2.5
60
	 *
61
	 * {@inheritDoc}
62
	 */
63
	public function setFieldProperties( array $properties ) {
64
		foreach ( $properties as $property ) {
65
			if ( $property instanceof DIProperty ) {
66
				$this->properties[] = $property;
67
			}
68
		}
69
	}
70
71
	/**
72
	 * @since 2.5
73
	 *
74
	 * {@inheritDoc}
75
	 */
76
	public function getProperties() {
77
		return $this->properties;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->properties; of type SMW\DIProperty[]|null adds the type SMW\DIProperty[] to the return on line 77 which is incompatible with the return type declared by the abstract method SMW\DataValues\AbstractMultiValue::getProperties of type SMW\DataValues\DIProperty[]|null.
Loading history...
78
	}
79
80
	/**
81
	 * @since 2.5
82
	 *
83
	 * {@inheritDoc}
84
	 */
85 5
	public function getValuesFromString( $value ) {
86
		// #664 / T17732
87 5
		$value = str_replace( "\;", "-3B", $value );
88
89
		// Bug 21926 / T23926
90
		// Values that use html entities are encoded with a semicolon
91 5
		$value = htmlspecialchars_decode( $value, ENT_QUOTES );
92 5
		$values = preg_split( '/[\s]*;[\s]*/u', trim( $value ) );
93
94 5
		return str_replace( "-3B", ";", $values );
95
	}
96
97
	/**
98
	 * @see DataValue::getShortWikiText
99
	 * @since 2.5
100
	 *
101
	 * {@inheritDoc}
102
	 */
103 2
	public function getShortWikiText( $linker = null ) {
104 2
		return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_SHORT, $linker );
105
	}
106
107
	/**
108
	 * @see DataValue::getShortHTMLText
109
	 * @since 2.5
110
	 *
111
	 * {@inheritDoc}
112
	 */
113
	public function getShortHTMLText( $linker = null ) {
114
		return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_SHORT, $linker );
115
	}
116
117
	/**
118
	 * @see DataValue::getLongWikiText
119
	 * @since 2.5
120
	 *
121
	 * {@inheritDoc}
122
	 */
123
	public function getLongWikiText( $linker = null ) {
124
		return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_LONG, $linker );
125
	}
126
127
	/**
128
	 * @see DataValue::getLongHTMLText
129
	 * @since 2.5
130
	 *
131
	 * {@inheritDoc}
132
	 */
133
	public function getLongHTMLText( $linker = null ) {
134
		return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_LONG, $linker );
135
	}
136
137
	/**
138
	 * @see DataValue::getWikiValue
139
	 * @since 2.5
140
	 *
141
	 * {@inheritDoc}
142
	 */
143 1
	public function getWikiValue() {
144 1
		return $this->getDataValueFormatter()->format( DataValueFormatter::VALUE );
145
	}
146
147
	/**
148
	 * @since 2.5
149
	 *
150
	 * {@inheritDoc}
151
	 */
152 5
	public function getPropertyDataItems() {
153
154 5
		if ( $this->properties === null ) {
155 5
			$this->properties = $this->findPropertyDataItems( $this->getProperty() );
156
157 5
			if ( count( $this->properties ) == 0 ) {
158
				$this->addErrorMsg( array( 'smw-datavalue-reference-invalid-fields-definition' ), Message::PARSE );
159
			}
160
		}
161
162 5
		return $this->properties;
163
	}
164
165
	/**
166
	 * @since 2.5
167
	 *
168
	 * {@inheritDoc}
169
	 */
170
	public function getDataItems() {
171
		return parent::getDataItems();
172
	}
173
174
	/**
175
	 * @note called by DataValue::setUserValue
176
	 * @see DataValue::parseUserValue
177
	 *
178
	 * {@inheritDoc}
179
	 */
180 5
	protected function parseUserValue( $value ) {
181
182 5
		if ( $value === '' ) {
183 1
			$this->addErrorMsg( array( 'smw_novalues' ) );
184 1
			return;
185
		}
186
187 4
		$containerSemanticData = $this->newContainerSemanticData( $value );
188 4
		$sortKeys = array();
189
190 4
		$values = $this->getValuesFromString( $value );
191 4
		$index = 0; // index in value array
192
193 4
		$propertyIndex = 0; // index in property list
194 4
		$empty = true;
195
196 4
		foreach ( $this->getPropertyDataItems() as $property ) {
197
198 4
			if ( !array_key_exists( $index, $values ) || $this->getErrors() !== array() ) {
199 1
				break; // stop if there are no values left
200
			}
201
202
			// generating the DVs:
203 4
			if ( ( $values[$index] === '' ) || ( $values[$index] == '?' ) ) { // explicit omission
204
				$index++;
205
			} else {
206 4
				$dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
207
					$property,
208 4
					$values[$index],
209 4
					false,
210 4
					$this->getContextPage()
211
				);
212
213 4
				if ( $dataValue->isValid() ) { // valid DV: keep
214 4
					$containerSemanticData->addPropertyObjectValue( $property, $dataValue->getDataItem() );
215 4
					$sortKeys[] = $dataValue->getDataItem()->getSortKey();
216
217 4
					$index++;
218 4
					$empty = false;
219 1
				} elseif ( ( count( $values ) - $index ) == ( count( $this->properties ) - $propertyIndex ) ) {
220 1
					$containerSemanticData->addPropertyObjectValue( $property, $dataValue->getDataItem() );
221 1
					$this->addError( $dataValue->getErrors() );
222 1
					++$index;
223
				}
224
			}
225 4
			++$propertyIndex;
226
		}
227
228 4
		if ( $empty && $this->getErrors() === array()  ) {
229
			$this->addErrorMsg( array( 'smw_novalues' ) );
230
		}
231
232 4
		$this->m_dataitem = new DIContainer( $containerSemanticData );
233
234
		// Composite sortkey is to ensure that Store::getPropertyValues can
235
		// apply sorting during value selection
236 4
		$this->m_dataitem->addCompositeSortKey( implode( ';', $sortKeys ) );
237 4
	}
238
239
	/**
240
	 * @see DataValue::loadDataItem
241
	 */
242 1
	protected function loadDataItem( DataItem $dataItem ) {
243
244 1
		if ( $dataItem->getDIType() === DataItem::TYPE_CONTAINER ) {
245
			$this->m_dataitem = $dataItem;
246
			return true;
247 1
		} elseif ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
248 1
			$semanticData = new ContainerSemanticData( $dataItem );
249 1
			$semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) );
250 1
			$this->m_dataitem = new DIContainer( $semanticData );
251 1
			return true;
252
		}
253
254 1
		return false;
255
	}
256
257 5
	private function findPropertyDataItems( DIProperty $property = null ) {
258
259 5
		if ( $property === null ) {
260
			return array();
261
		}
262
263 5
		$propertyDiWikiPage = $property->getDiWikiPage();
264
265 5
		if ( $propertyDiWikiPage === null ) {
266
			return array();
267
		}
268
269 5
		$listDiProperty = new DIProperty( '_LIST' );
270 5
		$dataItems = ApplicationFactory::getInstance()->getStore()->getPropertyValues( $propertyDiWikiPage, $listDiProperty );
271
272 5
		if ( count( $dataItems ) == 1 ) {
273 5
			$propertyListValue = new PropertyListValue( '__pls' );
274 5
			$propertyListValue->setDataItem( reset( $dataItems ) );
275
276 5
			if ( $propertyListValue->isValid() ) {
277 5
				return $propertyListValue->getPropertyDataItems();
278
			}
279
		}
280
281
		return array();
282
	}
283
284 4
	private function newContainerSemanticData( $value ) {
285
286 4
		if ( $this->m_contextPage === null ) {
287 2
			$containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
288 2
			$containerSemanticData->skipAnonymousCheck();
289
		} else {
290 2
			$subobjectName = '_REF' . md5( $value );
291
292 2
			$subject = new DIWikiPage(
293 2
				$this->m_contextPage->getDBkey(),
294 2
				$this->m_contextPage->getNamespace(),
295 2
				$this->m_contextPage->getInterwiki(),
296
				$subobjectName
297
			);
298
299 2
			$containerSemanticData = new ContainerSemanticData( $subject );
300
		}
301
302 4
		return $containerSemanticData;
303
	}
304
305
}
306