Completed
Push — master ( cc1fa7...46bd0e )
by Jeroen De
01:23
created

MapPrinter::elementsToJson()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1.008

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 5
cp 0.8
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1.008
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\FileUrlFinder;
11
use Maps\MappingService;
12
use Maps\MapsFunctions;
13
use Maps\Presentation\ElementJsonSerializer;
14
use Maps\Presentation\MapHtmlBuilder;
15
use Maps\Presentation\WikitextParser;
16
use Maps\Presentation\WikitextParsers\LocationParser;
17
use ParamProcessor\ParamDefinition;
18
use Parser;
19
use SMW\Query\ResultPrinters\ResultPrinter;
20
use SMWOutputs;
21
use SMWQueryResult;
22
use Title;
23
24
/**
25
 * @licence GNU GPL v2+
26
 * @author Jeroen De Dauw < [email protected] >
27
 * @author Peter Grassberger < [email protected] >
28
 */
29
class MapPrinter extends ResultPrinter {
30
31
	private static $services = [];
32
33
	/**
34
	 * @var LocationParser
35
	 */
36
	private $locationParser;
37
38
	/**
39
	 * @var FileUrlFinder
40
	 */
41
	private $fileUrlFinder;
42
43
	/**
44
	 * @var MappingService
45
	 */
46
	private $service;
47
48
	/**
49
	 * @var WikitextParser
50
	 */
51
	private $wikitextParser;
52
53
	/**
54
	 * @var ElementJsonSerializer
55
	 */
56
	private $elementSerializer;
57
58
	/**
59
	 * @var string|boolean
60
	 */
61
	private $fatalErrorMsg = false;
62
63
	/**
64
	 * @param string $format
65
	 * @param bool $inline
66
	 */
67 1
	public function __construct( $format, $inline = true ) {
68 1
		$this->service = self::$services[$format];
69
70 1
		parent::__construct( $format, $inline );
71 1
	}
72
73
	/**
74
	 * @since 3.4
75
	 * FIXME: this is a temporary hack that should be replaced when SMW allows for dependency
76
	 * injection in query printers.
77
	 *
78
	 * @param MappingService $service
79
	 */
80
	public static function registerService( MappingService $service ) {
81
		self::$services[$service->getName()] = $service;
82
	}
83
84
	public static function registerDefaultService( $serviceName ) {
85
		self::$services['map'] = self::$services[$serviceName];
86
	}
87
88 1
	private function getParser(): Parser {
89 1
		$parser = $GLOBALS['wgParser'];
90
91 1
		if ( $parser instanceof \StubObject ) {
0 ignored issues
show
Bug introduced by
The class StubObject 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...
92 1
			return $parser->_newObject();
93
		}
94
95
		return $parser;
96
	}
97
98 1
	private function getParserClone(): Parser {
99 1
		$parser = $this->getParser();
100 1
		return clone $parser;
101
	}
102
103
	/**
104
	 * Builds up and returns the HTML for the map, with the queried coordinate data on it.
105
	 *
106
	 * @param SMWQueryResult $res
107
	 * @param int $outputMode
108
	 *
109
	 * @return string
110
	 */
111 1
	public final function getResultText( SMWQueryResult $res, $outputMode ) {
112 1
		if ( $this->fatalErrorMsg !== false ) {
113
			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 113 which is incompatible with the return type documented by Maps\SemanticMW\ResultPr...pPrinter::getResultText of type string.
Loading history...
114
		}
115
116 1
		$factory = \Maps\MapsFactory::newDefault();
117 1
		$this->locationParser = $factory->newLocationParser();
118 1
		$this->fileUrlFinder = $factory->getFileUrlFinder();
119
120 1
		$this->wikitextParser = new WikitextParser( $this->getParserClone() );
121 1
		$this->elementSerializer = new ElementJsonSerializer( $this->wikitextParser );
122
123 1
		$this->addTrackingCategoryIfNeeded();
124
125 1
		$params = $this->params;
126
127 1
		$queryHandler = new QueryHandler( $res, $outputMode );
128 1
		$queryHandler->setLinkStyle( $params['link'] );
129 1
		$queryHandler->setHeaderStyle( $params['headers'] );
130 1
		$queryHandler->setShowSubject( $params['showtitle'] );
131 1
		$queryHandler->setTemplate( $params['template'] );
132 1
		$queryHandler->setUserParam( $params['userparam'] );
133 1
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
134 1
		$queryHandler->setActiveIcon( $params['activeicon'] );
135
136 1
		$this->handleMarkerData( $params, $queryHandler );
137
138 1
		$params['lines'] = $this->elementsToJson( $params['lines'] );
139 1
		$params['polygons'] = $this->elementsToJson( $params['polygons'] );
140 1
		$params['circles'] = $this->elementsToJson( $params['circles'] );
141 1
		$params['rectangles'] = $this->elementsToJson( $params['rectangles'] );
142
143 1
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
144
145 1
		if ( $params['locations'] === [] ) {
146
			return $params['default'];
147
		}
148
149
		// We can only take care of the zoom defaulting here,
150
		// as not all locations are available in whats passed to Validator.
151 1
		if ( $this->fullParams['zoom']->wasSetToDefault() && count( $params['locations'] ) > 1 ) {
152 1
			$params['zoom'] = false;
153
		}
154
155 1
		$mapId = $this->service->newMapId();
156
157 1
		SMWOutputs::requireHeadItem(
158 1
			$mapId,
159 1
			$this->service->getDependencyHtml( $params )
160
		);
161
162 1
		foreach ( $this->service->getResourceModules() as $resourceModule ) {
163 1
			SMWOutputs::requireResource( $resourceModule );
164
		}
165
166 1
		if ( array_key_exists( 'source', $params ) ) {
167 1
			unset( $params['source'] );
168
		}
169
170 1
		return ( new MapHtmlBuilder() )->getMapHTML(
171 1
			$params,
172
			$mapId,
173 1
			$this->service->getName()
174
		);
175
	}
176
177 1
	private function elementsToJson( array $elements ) {
178 1
		return array_map(
179 1
			function( BaseElement $element ) {
180
				return $this->elementSerializer->elementToJson( $element );
181 1
			},
182
			$elements
183
		);
184
	}
185
186 1
	private function addTrackingCategoryIfNeeded() {
187
		/**
188
		 * @var Parser $wgParser
189
		 */
190 1
		global $wgParser;
191
192 1
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
193
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
194
		}
195 1
	}
196
197
	/**
198
	 * Converts the data in the coordinates parameter to JSON-ready objects.
199
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
200
	 *
201
	 * @param array &$params
202
	 * @param QueryHandler $queryHandler
203
	 */
204 1
	private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
205 1
		$params['centre'] = $this->getCenter( $params['centre'] );
206
207 1
		$iconUrl = $this->fileUrlFinder->getUrlForFileName( $params['icon'] );
208 1
		$visitedIconUrl = $this->fileUrlFinder->getUrlForFileName( $params['visitedicon'] );
209
210 1
		$params['locations'] = $this->getJsonForStaticLocations(
211 1
			$params['staticlocations'],
212
			$params,
213
			$iconUrl,
214
			$visitedIconUrl
215
		);
216
217 1
		unset( $params['staticlocations'] );
218
219 1
		$params['locations'] = array_merge(
220 1
			$params['locations'],
221 1
			$this->getJsonForLocations(
222 1
				$queryHandler->getLocations(),
223
				$params,
224
				$iconUrl,
225
				$visitedIconUrl
226
			)
227
		);
228 1
	}
229
230 1
	private function getCenter( $coordinatesOrAddress ) {
231 1
		if ( $coordinatesOrAddress === false ) {
232 1
			return false;
233
		}
234
235
		try {
236
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
237
			$location = $this->locationParser->parse( $coordinatesOrAddress );
238
		}
239
		catch ( \Exception $ex ) {
240
			// TODO: somehow report this to the user
241
			return false;
242
		}
243
244
		return $location->getJSONObject();
245
	}
246
247 1
	private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
248 1
		$locationsJson = [];
249
250 1
		foreach ( $staticLocations as $location ) {
251
			$locationsJson[] = $this->getJsonForStaticLocation(
252
				$location,
253
				$params,
254
				$iconUrl,
255
				$visitedIconUrl
256
			);
257
		}
258
259 1
		return $locationsJson;
260
	}
261
262
	private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl ) {
263
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
264
265
		$this->elementSerializer->titleAndText( $jsonObj );
266
267
		if ( $params['pagelabel'] ) {
268
			$jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
269
		}
270
271
		return $jsonObj;
272
	}
273
274
	/**
275
	 * @param Location[] $locations
276
	 * @param array $params
277
	 * @param string $iconUrl
278
	 * @param string $visitedIconUrl
279
	 *
280
	 * @return array
281
	 */
282 1
	private function getJsonForLocations( iterable $locations, array $params, string $iconUrl, string $visitedIconUrl ): array {
283 1
		$locationsJson = [];
284
285 1
		foreach ( $locations as $location ) {
286 1
			$jsonObj = $location->getJSONObject(
287 1
				$params['title'],
288 1
				$params['label'],
289
				$iconUrl,
290 1
				'',
291 1
				'',
292
				$visitedIconUrl
293
			);
294
295 1
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
296
297 1
			$locationsJson[] = $jsonObj;
298
		}
299
300 1
		return $locationsJson;
301
	}
302
303
	/**
304
	 * Returns the internationalized name of the mapping service.
305
	 *
306
	 * @return string
307
	 */
308
	public final function getName() {
309
		return wfMessage( 'maps_' . $this->service->getName() )->text();
310
	}
311
312
	/**
313
	 * Returns a list of parameter information, for usage by Special:Ask and others.
314
	 *
315
	 * @return array
316
	 */
317 1
	public function getParameters() {
318 1
		$params = parent::getParameters();
319 1
		$paramInfo = $this->getParameterInfo();
320
321 1
		$params = array_merge( $params, $paramInfo );
322
323 1
		return $params;
324
	}
325
326
	/**
327
	 * Returns an array containing the parameter info.
328
	 *
329
	 * @return array
330
	 */
331 1
	private function getParameterInfo() {
332 1
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
333
334 1
		$params = $this->service->getParameterInfo();
335
336 1
		$params['staticlocations'] = [
337
			'type' => 'mapslocation',
338
			'aliases' => [ 'locations', 'points' ],
339
			'default' => [],
340
			'islist' => true,
341
			'delimiter' => ';',
342
			'message' => 'semanticmaps-par-staticlocations',
343
		];
344
345 1
		$params['showtitle'] = [
346 1
			'type' => 'boolean',
347 1
			'aliases' => 'show title',
348 1
			'default' => $smgQPShowTitle,
349
		];
350
351 1
		$params['hidenamespace'] = [
352 1
			'type' => 'boolean',
353 1
			'aliases' => 'hide namespace',
354 1
			'default' => $smgQPHideNamespace,
355
		];
356
357 1
		$params['template'] = [
358 1
			'default' => $smgQPTemplate,
359
		];
360
361 1
		$params['userparam'] = [
362
			'default' => '',
363
		];
364
365 1
		$params['activeicon'] = [
366
			'type' => 'string',
367
			'default' => '',
368
		];
369
370 1
		$params['pagelabel'] = [
371
			'type' => 'boolean',
372
			'default' => false,
373
		];
374
375 1
		$params['ajaxcoordproperty'] = [
376
			'default' => '',
377
		];
378
379 1
		$params['ajaxquery'] = [
380
			'default' => '',
381
			'type' => 'string'
382
		];
383
384
		// Messages:
385
		// semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
386
		// semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
387
		// semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
388 1
		foreach ( $params as $name => &$data ) {
389 1
			if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
390 1
				$data['message'] = 'semanticmaps-par-' . $name;
391
			}
392
		}
393
394 1
		return $params;
395
	}
396
}
397