Completed
Push — master ( e247ff...af1f28 )
by Jeroen De
10:41
created

QueryHandler::getValueLinker()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 6
1
<?php
2
3
namespace Maps\SemanticMW\ResultPrinters;
4
5
use Html;
6
use Linker;
7
use Maps\Elements\Location;
8
use Maps\MapsFunctions;
9
use SMWDataValue;
10
use SMWPrintRequest;
11
use SMWQueryResult;
12
use SMWResultArray;
13
use SMWWikiPageValue;
14
use Title;
15
16
/**
17
 * Class for handling geographical SMW queries.
18
 *
19
 * @licence GNU GPL v2+
20
 * @author Jeroen De Dauw < [email protected] >
21
 */
22
class QueryHandler {
23
24
	/**
25
	 * The global icon.
26
	 * @var string
27
	 */
28
	public $icon = '';
29
30
	/**
31
	 * The global text.
32
	 * @var string
33
	 */
34
	public $text = '';
35
36
	/**
37
	 * The global title.
38
	 * @var string
39
	 */
40
	public $title = '';
41
42
	/**
43
	 * Make a separate link to the title or not?
44
	 * @var boolean
45
	 */
46
	public $titleLinkSeparate = false;
47
48
	private $queryResult;
49
50
	private $outputMode;
51
52
	/**
53
	 * @var array
54
	 */
55
	private $geoShapes = [
56
		'lines' => [],
57
		'locations' => [],
58
		'polygons' => []
59
	];
60
61
	/**
62
	 * The template to use for the text, or false if there is none.
63
	 * @var string|boolean false
64
	 */
65
	private $template = false;
66
67
	/**
68
	 * Should link targets be made absolute (instead of relative)?
69
	 * @var boolean
70
	 */
71
	private $linkAbsolute;
72
73
	/**
74
	 * The text used for the link to the page (if it's created). $1 will be replaced by the page name.
75
	 * @var string
76
	 */
77
	private $pageLinkText = '$1';
78
79
	/**
80
	 * A separator to use between the subject and properties in the text field.
81
	 * @var string
82
	 */
83
	private $subjectSeparator = '<hr />';
84
85
	/**
86
	 * Show the subject in the text or not?
87
	 * @var boolean
88
	 */
89
	private $showSubject = true;
90
91
	/**
92
	 * Hide the namespace or not.
93
	 * @var boolean
94
	 */
95
	private $hideNamespace = false;
96
97
	/**
98
	 * Defines which article names in the result are hyperlinked, all normally is the default
99
	 * none, subject, all
100
	 */
101
	private $linkStyle = 'all';
102
103
	/*
104
	 * Show headers (with links), show headers (just text) or hide them. show is default
105
	 * show, plain, hide
106
	 */
107
	private $headerStyle = 'show';
108
109
	/**
110
	 * Marker icon to show when marker equals active page
111
	 * @var string|null
112
	 */
113
	private $activeIcon = null;
114
115
	/**
116
	 * @var string
117
	 */
118
	private $userParam = '';
119
120
	public function __construct( SMWQueryResult $queryResult, int $outputMode, bool $linkAbsolute = false ) {
121
		$this->queryResult = $queryResult;
122
		$this->outputMode = $outputMode;
123
		$this->linkAbsolute = $linkAbsolute;
124
	}
125
126
	public function setTemplate( string $template ) {
127
		$this->template = $template === '' ? false : $template;
128
	}
129
130
	public function setUserParam( string $userParam ) {
131
		$this->userParam = $userParam;
132
	}
133
134
	/**
135
	 * Sets the global icon.
136
	 */
137
	public function setIcon( string $icon ) {
138
		$this->icon = $icon;
139
	}
140
141
	/**
142
	 * Sets the global title.
143
	 */
144
	public function setTitle( string $title ) {
145
		$this->title = $title;
146
	}
147
148
	/**
149
	 * Sets the global text.
150
	 */
151
	public function setText( string $text ) {
152
		$this->text = $text;
153
	}
154
155
	public function setSubjectSeparator( string $subjectSeparator ) {
156
		$this->subjectSeparator = $subjectSeparator;
157
	}
158
159
	public function setShowSubject( bool $showSubject ) {
160
		$this->showSubject = $showSubject;
161
	}
162
163
	/**
164
	 * Sets the text for the link to the page when separate from the title.
165
	 */
166
	public function setPageLinkText( string $text ) {
167
		$this->pageLinkText = $text;
168
	}
169
170
	public function setLinkStyle( string $link ) {
171
		$this->linkStyle = $link;
172
	}
173
174
	public function setHeaderStyle( string $headers ) {
175
		$this->headerStyle = $headers;
176
	}
177
178
	/**
179
	 * @return array
180
	 */
181
	public function getShapes() {
182
		$this->findShapes();
183
		return $this->geoShapes;
184
	}
185
186
	/**
187
	 * @since 2.0
188
	 */
189
	private function findShapes() {
190
		while ( ( $row = $this->queryResult->getNext() ) !== false ) {
191
			$this->handleResultRow( $row );
192
		}
193
	}
194
195
	/**
196
	 * Returns the locations found in the provided result row.
197
	 *
198
	 * @param SMWResultArray[] $row
199
	 */
200
	private function handleResultRow( array $row ) {
201
		$locations = [];
202
		$properties = [];
203
204
		$title = '';
205
		$text = '';
206
207
		// Loop through all fields of the record.
208
		foreach ( $row as $i => $resultArray ) {
209
			$printRequest = $resultArray->getPrintRequest();
210
211
			// Loop through all the parts of the field value.
212
			while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {
213
				if ( $dataValue->getTypeID() == '_wpg' && $i == 0 ) {
214
					$title = $dataValue->getLongText( $this->outputMode, null );
215
					$text = $this->getResultSubjectText( $dataValue );
216
				} else {
217
					if ( $dataValue->getTypeID() == '_str' && $i == 0 ) {
218
						$title = $dataValue->getLongText( $this->outputMode, null );
219
						$text = $dataValue->getLongText( $this->outputMode, smwfGetLinker() );
220
					} else {
221
						if ( strpos( $dataValue->getTypeID(), '_rec' ) !== false ) {
222
							foreach ( $dataValue->getDataItems() as $dataItem ) {
223
								if ( $dataItem instanceof \SMWDIGeoCoord ) {
0 ignored issues
show
Bug introduced by
The class SMWDIGeoCoord does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
224
									$locations[] = Location::newFromLatLon(
225
										$dataItem->getLatitude(),
226
										$dataItem->getLongitude()
227
									);
228
								}
229
							}
230
						} else {
231
							if ( $i === 0 || $dataValue->getTypeID() === '_geo' ) {
232
								if ( $printRequest->getMode(
233
									) == SMWPrintRequest::PRINT_PROP && $printRequest->getTypeID(
234
									) == '_geo' || $dataValue->getTypeID() == '_geo' ) {
235
									$dataItem = $dataValue->getDataItem();
236
237
									$locations[] = Location::newFromLatLon(
238
										$dataItem->getLatitude(),
239
										$dataItem->getLongitude()
240
									);
241
								}
242
							}
243
							else {
244
								$properties[] = $this->handleResultProperty( $dataValue, $printRequest );
245
							}
246
						}
247
					}
248
				}
249
			}
250
		}
251
252
		if ( $properties !== [] && $text !== '' ) {
253
			$text .= $this->subjectSeparator;
254
		}
255
256
		$icon = $this->getLocationIcon( $row );
257
258
		$this->geoShapes['locations'] = array_merge(
259
			$this->geoShapes['locations'],
260
			$this->buildLocationsList(
261
				$locations,
262
				$text,
263
				$icon,
264
				$properties,
265
				Title::newFromText( $title )
266
			)
267
		);
268
	}
269
270
	/**
271
	 * Handles a SMWWikiPageValue subject value.
272
	 * Gets the plain text title and creates the HTML text with headers and the like.
273
	 *
274
	 * @param SMWWikiPageValue $object
275
	 *
276
	 * @return string
277
	 */
278
	private function getResultSubjectText( SMWWikiPageValue $object ): string {
279
		if ( !$this->showSubject ) {
280
			return '';
281
		}
282
283
		if ( $this->showArticleLink() ) {
284
			if ( !$this->titleLinkSeparate && $this->linkAbsolute ) {
285
				$text = Html::element(
286
					'a',
287
					[ 'href' => $object->getTitle()->getFullUrl() ],
288
					$this->hideNamespace ? $object->getText() : $object->getTitle()->getFullText()
289
				);
290
			} else {
291
				if ( $this->hideNamespace ) {
292
					$text = $object->getShortHTMLText( smwfGetLinker() );
293
				} else {
294
					$text = $object->getLongHTMLText( smwfGetLinker() );
295
				}
296
			}
297
		} else {
298
			$text = $this->hideNamespace ? $object->getText() : $object->getTitle()->getFullText();
299
		}
300
301
		$text = '<b>' . $text . '</b>';
302
303
		if ( !$this->titleLinkSeparate ) {
304
			return $text;
305
		}
306
307
		$txt = $object->getTitle()->getText();
308
309
		if ( $this->pageLinkText !== '' ) {
310
			$txt = str_replace( '$1', $txt, $this->pageLinkText );
311
		}
312
313
		return $text . Html::element(
314
			'a',
315
			[ 'href' => $object->getTitle()->getFullUrl() ],
316
			$txt
317
		);
318
	}
319
320
	private function showArticleLink() {
321
		return $this->linkStyle !== 'none';
322
	}
323
324
	private function isHeadersHide() {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
325
		return $this->headerStyle === 'hide';
326
	}
327
328
	/**
329
	 * Handles a single property (SMWPrintRequest) to be displayed for a record (SMWDataValue).
330
	 */
331
	private function handleResultProperty( SMWDataValue $object, SMWPrintRequest $printRequest ): string {
332
		if ( $this->hasTemplate() ) {
333
			if ( $object instanceof SMWWikiPageValue ) {
0 ignored issues
show
Bug introduced by
The class SMWWikiPageValue does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
334
				return $object->getTitle()->getPrefixedText();
335
			}
336
337
			return $object->getLongText( SMW_OUTPUT_WIKI, null );
338
		}
339
340
		$propertyName = $this->getPropertyName( $printRequest );
341
		return $propertyName . ( $propertyName === '' ? '' : ': ' ) . $this->getPropertyValue( $object );
342
	}
343
344
	private function getPropertyName( SMWPrintRequest $printRequest ): string {
345
		if ( $this->headerStyle === 'hide' ) {
346
			return '';
347
		}
348
349
		if ( $this->linkAbsolute ) {
350
			$titleText = $printRequest->getText( null );
351
			$t = Title::newFromText( $titleText, SMW_NS_PROPERTY );
352
353
			if ( $t instanceof Title && $t->exists() ) {
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
354
				return  Html::element(
355
					'a',
356
					[ 'href' => $t->getFullUrl() ],
357
					$printRequest->getHTMLText( null )
358
				);
359
			}
360
361
			return $titleText;
362
		}
363
364
		return $printRequest->getHTMLText( $this->getPropertyLinker() );
365
	}
366
367
	private function getPropertyLinker(): ?Linker {
368
		return $this->headerStyle === 'show' && $this->linkStyle !== 'none' ? smwfGetLinker() : null;
369
	}
370
371
	private function getValueLinker(): ?Linker {
372
		return $this->linkStyle === 'all' ? smwfGetLinker() : null;
373
	}
374
375
	private function getPropertyValue( SMWDataValue $object ): string {
376
		if ( !$this->linkAbsolute ) {
377
			return $object->getLongHTMLText(
378
				$this->getValueLinker()
379
			);
380
		}
381
382
		if ( $this->hasPage( $object ) ) {
383
			return Html::element(
384
				'a',
385
				[
386
					'href' => Title::newFromText(
387
						$object->getLongText( $this->outputMode, null ),
388
						NS_MAIN
389
					)->getFullUrl()
390
				],
391
				$object->getLongText( $this->outputMode, null )
392
			);
393
		}
394
395
		return $object->getLongText( $this->outputMode, null );
396
	}
397
398
	private function hasPage( SMWDataValue $object ): bool {
399
		$hasPage = $object->getTypeID() == '_wpg';
400
401
		if ( $hasPage ) {
402
			$t = Title::newFromText( $object->getLongText( $this->outputMode, null ), NS_MAIN );
403
			$hasPage = $t !== null && $t->exists();
404
		}
405
406
		return $hasPage;
407
	}
408
409
	private function hasTemplate() {
410
		return is_string( $this->template );
411
	}
412
413
	/**
414
	 * Get the icon for a row.
415
	 *
416
	 * @param array $row
417
	 *
418
	 * @return string
419
	 */
420
	private function getLocationIcon( array $row ) {
421
		$icon = '';
422
		$legendLabels = [];
423
424
		//Check for activeicon parameter
425
426
		if ( $this->shouldGetActiveIconUrlFor( $row[0]->getResultSubject()->getTitle() ) ) {
427
			$icon = MapsFunctions::getFileUrl( $this->activeIcon );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MapsFunctions::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
428
		}
429
430
		// Look for display_options field, which can be set by Semantic Compound Queries
431
		// the location of this field changed in SMW 1.5
432
		$display_location = method_exists( $row[0], 'getResultSubject' ) ? $row[0]->getResultSubject() : $row[0];
433
434
		if ( property_exists( $display_location, 'display_options' ) && is_array(
435
				$display_location->display_options
436
			) ) {
437
			$display_options = $display_location->display_options;
438
			if ( array_key_exists( 'icon', $display_options ) ) {
439
				$icon = $display_options['icon'];
440
441
				// This is somewhat of a hack - if a legend label has been set, we're getting it for every point, instead of just once per icon
442
				if ( array_key_exists( 'legend label', $display_options ) ) {
443
444
					$legend_label = $display_options['legend label'];
445
446
					if ( !array_key_exists( $icon, $legendLabels ) ) {
447
						$legendLabels[$icon] = $legend_label;
448
					}
449
				}
450
			}
451
		} // Icon can be set even for regular, non-compound queries If it is, though, we have to translate the name into a URL here
452
		elseif ( $this->icon !== '' ) {
453
			$icon = MapsFunctions::getFileUrl( $this->icon );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MapsFunctions::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
454
		}
455
456
		return $icon;
457
	}
458
459
	private function shouldGetActiveIconUrlFor( Title $title ) {
460
		global $wgTitle;
461
462
		return isset( $this->activeIcon ) && is_object( $wgTitle )
463
			&& $wgTitle->equals( $title );
464
	}
465
466
	/**
467
	 * Builds a set of locations with the provided title, text and icon.
468
	 *
469
	 * @param Location[] $locations
470
	 * @param string $text
471
	 * @param string $icon
472
	 * @param array $properties
473
	 * @param Title|null $title
474
	 *
475
	 * @return Location[]
476
	 */
477
	private function buildLocationsList( array $locations, $text, $icon, array $properties, Title $title = null ) {
478
		if ( !$this->hasTemplate() ) {
479
			$text .= implode( '<br />', $properties );
480
		}
481
482
		$titleOutput = $this->getTitleOutput( $title );
483
484
		foreach ( $locations as &$location ) {
485
			if ( $this->hasTemplate() ) {
486
				$segments = array_merge(
487
					[
488
						$this->template,
489
						'title=' . $titleOutput,
490
						'latitude=' . $location->getCoordinates()->getLatitude(),
491
						'longitude=' . $location->getCoordinates()->getLongitude(),
492
						'userparam=' . $this->userParam
493
					],
494
					$properties
495
				);
496
497
				$text .= $this->getParser()->recursiveTagParseFully(
498
					'{{' . implode( '|', $segments ) . '}}'
499
				);
500
			}
501
502
			$location->setTitle( $titleOutput );
503
			$location->setText( $text );
504
			$location->setIcon( trim( $icon ) );
505
		}
506
507
		return $locations;
508
	}
509
510
	private function getTitleOutput( Title $title = null ) {
511
		if ( $title === null ) {
512
			return '';
513
		}
514
515
		return $this->hideNamespace ? $title->getText() : $title->getFullText();
516
	}
517
518
	/**
519
	 * @return \Parser
520
	 */
521
	private function getParser() {
522
		return $GLOBALS['wgParser'];
523
	}
524
525
	/**
526
	 * @return boolean
527
	 */
528
	public function getHideNamespace() {
529
		return $this->hideNamespace;
530
	}
531
532
	/**
533
	 * @param boolean $hideNamespace
534
	 */
535
	public function setHideNamespace( $hideNamespace ) {
536
		$this->hideNamespace = $hideNamespace;
537
	}
538
539
	/**
540
	 * @return string
541
	 */
542
	public function getActiveIcon() {
543
		return $this->activeIcon;
544
	}
545
546
	/**
547
	 * @param string $activeIcon
548
	 */
549
	public function setActiveIcon( $activeIcon ) {
550
		$this->activeIcon = $activeIcon;
551
	}
552
553
}
554