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