Completed
Push — master ( 88c9eb...e2ac37 )
by Jeroen De
03:05
created

MapPrinter::addShapeData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 4
crap 2
1
<?php
2
3
namespace Maps\SemanticMW\ResultPrinters;
4
5
use FormatJson;
6
use Html;
7
use Linker;
8
use Maps\Elements\BaseElement;
9
use Maps\Elements\Location;
10
use Maps\MapsFunctions;
11
use Maps\MappingService;
12
use Maps\MediaWiki\ParserHooks\DisplayMapRenderer;
13
use Maps\Presentation\WikitextParsers\LocationParser;
14
use ParamProcessor\ParamDefinition;
15
use Parser;
16
use ParserOptions;
17
use SMW;
18
use SMWOutputs;
19
use SMWQueryResult;
20
use Title;
21
22
/**
23
 * @licence GNU GPL v2+
24
 * @author Jeroen De Dauw < [email protected] >
25
 * @author Peter Grassberger < [email protected] >
26
 */
27
class MapPrinter extends SMW\ResultPrinter {
28
29
	private static $services = [];
30
31
	/**
32
	 * @var LocationParser
33
	 */
34
	private $locationParser;
35
36
	/**
37
	 * @var MappingService
38
	 */
39
	private $service;
40
41
	/**
42
	 * @var string|boolean
43
	 */
44
	private $fatalErrorMsg = false;
45
46
	/**
47
	 * @param string $format
48
	 * @param bool $inline
49
	 */
50
	public function __construct( $format, $inline = true ) {
51
		$this->service = self::$services[$format];
52
53
		parent::__construct( $format, $inline );
54
	}
55
56
	/**
57
	 * @since 3.4
58
	 * FIXME: this is a temporary hack that should be replaced when SMW allows for dependency
59
	 * injection in query printers.
60
	 *
61
	 * @param MappingService $service
62
	 */
63
	public static function registerService( MappingService $service ) {
64
		self::$services[$service->getName()] = $service;
65
	}
66
67
	public static function registerDefaultService( $serviceName ) {
68
		self::$services['map'] = self::$services[$serviceName];
69
	}
70
71
	/**
72
	 * Builds up and returns the HTML for the map, with the queried coordinate data on it.
73
	 *
74
	 * @param SMWQueryResult $res
75
	 * @param int $outputMode
76
	 *
77
	 * @return string
78
	 */
79
	public final function getResultText( SMWQueryResult $res, $outputMode ) {
80
		if ( $this->fatalErrorMsg !== false ) {
81
			return $this->fatalErrorMsg;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->fatalErrorMsg; of type string|boolean adds the type boolean to the return on line 81 which is incompatible with the return type documented by Maps\SemanticMW\ResultPr...pPrinter::getResultText of type string.
Loading history...
82
		}
83
84
		$this->addTrackingCategoryIfNeeded();
85
86
		$params = $this->params;
87
88
		$this->initializeLocationParser();
89
90
		$queryHandler = new QueryHandler( $res, $outputMode );
91
		$queryHandler->setLinkStyle( $params['link'] );
92
		$queryHandler->setHeaderStyle( $params['headers'] );
93
		$queryHandler->setShowSubject( $params['showtitle'] );
94
		$queryHandler->setTemplate( $params['template'] );
95
		$queryHandler->setUserParam( $params['userparam'] );
96
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
97
		$queryHandler->setActiveIcon( $params['activeicon'] );
98
99
		$this->handleMarkerData( $params, $queryHandler );
100
101
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
102
103
		$this->service->addHtmlDependencies(
104
			DisplayMapRenderer::getLayerDependencies( $params['format'], $params )
105
		);
106
107
		$locationAmount = count( $params['locations'] );
108
109
		if ( $locationAmount > 0 ) {
110
			// We can only take care of the zoom defaulting here,
111
			// as not all locations are available in whats passed to Validator.
112
			if ( $this->fullParams['zoom']->wasSetToDefault() && $locationAmount > 1 ) {
113
				$params['zoom'] = false;
114
			}
115
116
			$mapName = $this->service->getMapId();
117
118
			SMWOutputs::requireHeadItem(
119
				$mapName,
120
				$this->service->getDependencyHtml()
121
			);
122
123
			foreach ( $this->service->getResourceModules() as $resourceModule ) {
124
				SMWOutputs::requireResource( $resourceModule );
125
			}
126
127
			if ( array_key_exists( 'source', $params ) ) {
128
				unset( $params['source'] );
129
			}
130
131
			return $this->getMapHTML( $params, $mapName );
132
		} else {
133
			return $params['default'];
134
		}
135
	}
136
137
	private function addTrackingCategoryIfNeeded() {
138
		/**
139
		 * @var Parser $wgParser
140
		 */
141
		global $wgParser;
142
143
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
144
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
145
		}
146
	}
147
148
	private function initializeLocationParser() {
149
		$this->locationParser = \Maps\MapsFactory::newDefault()->newLocationParser();
150
	}
151
152
	/**
153
	 * Converts the data in the coordinates parameter to JSON-ready objects.
154
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
155
	 *
156
	 * @param array &$params
157
	 * @param QueryHandler $queryHandler
158
	 */
159
	private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
160
		$params['centre'] = $this->getCenter( $params['centre'] );
161
162
		$iconUrl = MapsFunctions::getFileUrl( $params['icon'] );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MapsFunctions::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
163
		$visitedIconUrl = MapsFunctions::getFileUrl( $params['visitedicon'] );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MapsFunctions::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
164
165
		$params['locations'] = $this->getJsonForStaticLocations(
166
			$params['staticlocations'],
167
			$params,
168
			$iconUrl,
169
			$visitedIconUrl
170
		);
171
172
		unset( $params['staticlocations'] );
173
174
		$params['locations'] = array_merge(
175
			$params['locations'],
176
			$this->getJsonForLocations(
177
				$queryHandler->getLocations(),
178
				$params,
179
				$iconUrl,
180
				$visitedIconUrl
181
			)
182
		);
183
184
		if ( $params['format'] === 'openlayers' ) {
185
			$params['layers'] = DisplayMapRenderer::evilOpenLayersHack( $params['layers'] );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MediaWiki\ParserHoo...r::evilOpenLayersHack() has been deprecated.

This method has been deprecated.

Loading history...
186
		}
187
188
189
	}
190
191
	private function getCenter( $coordinatesOrAddress ) {
192
		if ( $coordinatesOrAddress === false ) {
193
			return false;
194
		}
195
196
		try {
197
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
198
			$location = $this->locationParser->parse( $coordinatesOrAddress );
199
		}
200
		catch ( \Exception $ex ) {
201
			// TODO: somehow report this to the user
202
			return false;
203
		}
204
205
		return $location->getJSONObject();
206
	}
207
208
	private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
209
		$locationsJson = [];
210
211
		foreach ( $staticLocations as $location ) {
212
			$locationsJson[] = $this->getJsonForStaticLocation(
213
				$location,
214
				$params,
215
				$iconUrl,
216
				$visitedIconUrl,
217
				clone $GLOBALS['wgParser']
218
			);
219
		}
220
221
		return $locationsJson;
222
	}
223
224
	private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl, Parser $parser ) {
225
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
226
227
		$jsonObj['title'] = $parser->parse( $jsonObj['title'], $parser->getTitle(), new ParserOptions() )->getText();
228
		$jsonObj['text'] = $parser->parse( $jsonObj['text'], $parser->getTitle(), new ParserOptions() )->getText();
229
230
		$hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== '';
231
		$jsonObj['text'] = ( $hasTitleAndtext ? '<b>' . $jsonObj['title'] . '</b><hr />' : $jsonObj['title'] ) . $jsonObj['text'];
232
		$jsonObj['title'] = strip_tags( $jsonObj['title'] );
233
234
		if ( $params['pagelabel'] ) {
235
			$jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
236
		}
237
238
		return $jsonObj;
239
	}
240
241
	/**
242
	 * @param Location[] $locations
243
	 * @param array $params
244
	 * @param string $iconUrl
245
	 * @param string $visitedIconUrl
246
	 *
247
	 * @return array
248
	 */
249
	private function getJsonForLocations( iterable $locations, array $params, string $iconUrl, string $visitedIconUrl ): array {
250
		$locationsJson = [];
251
252
		foreach ( $locations as $location ) {
253
			$jsonObj = $location->getJSONObject(
254
				$params['title'],
255
				$params['label'],
256
				$iconUrl,
257
				'',
258
				'',
259
				$visitedIconUrl
260
			);
261
262
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
263
264
			$locationsJson[] = $jsonObj;
265
		}
266
267
		return $locationsJson;
268
	}
269
270
	/**
271
	 * @param BaseElement[] $elements
272
	 * @param array $params
273
	 *
274
	 * @return array
275
	 */
276
	private function getElementJsonArray( array $elements, array $params ): array {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
277
		$elementsJson = [];
278
279
		foreach ( $elements as $element ) {
280
			$jsonObj = $element->getJSONObject( $params['title'], $params['label'] );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\Elements\BaseElement::getJSONObject() has been deprecated.

This method has been deprecated.

Loading history...
281
			$elementsJson[] = $jsonObj;
282
		}
283
284
		return $elementsJson;
285
	}
286
287
	/**
288
	 * Returns the HTML to display the map.
289
	 *
290
	 * @param array $params
291
	 * @param string $mapName
292
	 *
293
	 * @return string
294
	 */
295
	private function getMapHTML( array $params, string $mapName ): string {
296
		return Html::rawElement(
297
			'div',
298
			[
299
				'id' => $mapName,
300
				'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
301
				'class' => 'maps-map maps-' . $this->service->getName()
302
			],
303
			wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
304
			Html::element(
305
				'div',
306
				[ 'style' => 'display:none', 'class' => 'mapdata' ],
307
				FormatJson::encode( $params )
308
			)
309
		);
310
	}
311
312
	/**
313
	 * Returns the internationalized name of the mapping service.
314
	 *
315
	 * @return string
316
	 */
317
	public final function getName() {
318
		return wfMessage( 'maps_' . $this->service->getName() )->text();
319
	}
320
321
	/**
322
	 * Returns a list of parameter information, for usage by Special:Ask and others.
323
	 *
324
	 * @return array
325
	 */
326
	public function getParameters() {
327
		$params = parent::getParameters();
328
		$paramInfo = $this->getParameterInfo();
329
330
		// Do not display this as an option, as the format already determines it
331
		// TODO: this can probably be done cleaner with some changes in Maps
332
		unset( $paramInfo['mappingservice'] );
333
334
		$params = array_merge( $params, $paramInfo );
335
336
		return $params;
337
	}
338
339
	/**
340
	 * Returns an array containing the parameter info.
341
	 *
342
	 * @return array
343
	 */
344
	private function getParameterInfo() {
345
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
346
347
		$params = ParamDefinition::getCleanDefinitions( MapsFunctions::getCommonParameters() );
0 ignored issues
show
Documentation introduced by
\Maps\MapsFunctions::getCommonParameters() is of type array<string,array<strin...efault\":\"false\"}>"}>, but the function expects a array<integer,object<Par...ssor\IParamDefinition>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
348
349
		$this->service->addParameterInfo( $params );
350
351
		$params['staticlocations'] = [
352
			'type' => 'mapslocation',
353
			'aliases' => [ 'locations', 'points' ],
354
			'default' => [],
355
			'islist' => true,
356
			'delimiter' => ';',
357
			'message' => 'semanticmaps-par-staticlocations',
358
		];
359
360
		$params['showtitle'] = [
361
			'type' => 'boolean',
362
			'aliases' => 'show title',
363
			'default' => $smgQPShowTitle,
364
		];
365
366
		$params['hidenamespace'] = [
367
			'type' => 'boolean',
368
			'aliases' => 'hide namespace',
369
			'default' => $smgQPHideNamespace,
370
		];
371
372
		$params['template'] = [
373
			'default' => $smgQPTemplate,
374
		];
375
376
		$params['userparam'] = [
377
			'default' => '',
378
		];
379
380
		$params['activeicon'] = [
381
			'type' => 'string',
382
			'default' => '',
383
		];
384
385
		$params['pagelabel'] = [
386
			'type' => 'boolean',
387
			'default' => false,
388
		];
389
390
		$params['ajaxcoordproperty'] = [
391
			'default' => '',
392
		];
393
394
		$params['ajaxquery'] = [
395
			'default' => '',
396
			'type' => 'string'
397
		];
398
399
		// Messages:
400
		// semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
401
		// semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
402
		// semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
403
		foreach ( $params as $name => &$data ) {
404
			if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
405
				$data['message'] = 'semanticmaps-par-' . $name;
406
			}
407
		}
408
409
		return $params;
410
	}
411
}
412