Completed
Push — newparam ( 9f04a0...72433c )
by Jeroen De
04:14 queued 02:51
created

MapPrinter::getMapHTML()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 0
cts 16
cp 0
rs 9.7333
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 2

1 Method

Rating   Name   Duplication   Size   Complexity  
A MapPrinter::getName() 0 3 1
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
	public function __construct( $format, $inline = true ) {
68
		$this->service = self::$services[$format];
69
70
		parent::__construct( $format, $inline );
71
	}
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
	private function getParser(): Parser {
89
		$parser = $GLOBALS['wgParser'];
90
91
		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
			return $parser->_newObject();
93
		}
94
95
		return $parser;
96
	}
97
98
	private function getParserClone(): Parser {
99
		$parser = $this->getParser();
100
		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
	public final function getResultText( SMWQueryResult $res, $outputMode ) {
112
		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
		$factory = \Maps\MapsFactory::newDefault();
117
		$this->locationParser = $factory->newLocationParser();
118
		$this->fileUrlFinder = $factory->getFileUrlFinder();
119
120
		$this->wikitextParser = new WikitextParser( $this->getParserClone() );
121
		$this->elementSerializer = new ElementJsonSerializer( $this->wikitextParser );
122
123
		$this->addTrackingCategoryIfNeeded();
124
125
		$params = $this->params;
126
127
		$queryHandler = new QueryHandler( $res, $outputMode );
128
		$queryHandler->setLinkStyle( $params['link'] );
129
		$queryHandler->setHeaderStyle( $params['headers'] );
130
		$queryHandler->setShowSubject( $params['showtitle'] );
131
		$queryHandler->setTemplate( $params['template'] );
132
		$queryHandler->setUserParam( $params['userparam'] );
133
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
134
		$queryHandler->setActiveIcon( $params['activeicon'] );
135
136
		$this->handleMarkerData( $params, $queryHandler );
137
138
		$params['lines'] = $this->elementsToJson( $params['lines'] );
139
		$params['polygons'] = $this->elementsToJson( $params['polygons'] );
140
		$params['circles'] = $this->elementsToJson( $params['circles'] );
141
		$params['rectangles'] = $this->elementsToJson( $params['rectangles'] );
142
143
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
144
145
		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
		if ( $this->fullParams['zoom']->wasSetToDefault() && count( $params['locations'] ) > 1 ) {
152
			$params['zoom'] = false;
153
		}
154
155
		$mapId = $this->service->newMapId();
156
157
		SMWOutputs::requireHeadItem(
158
			$mapId,
159
			$this->service->getDependencyHtml( $params )
160
		);
161
162
		foreach ( $this->service->getResourceModules() as $resourceModule ) {
163
			SMWOutputs::requireResource( $resourceModule );
164
		}
165
166
		if ( array_key_exists( 'source', $params ) ) {
167
			unset( $params['source'] );
168
		}
169
170
		return ( new MapHtmlBuilder() )->getMapHTML(
171
			$params,
172
			$mapId,
173
			$this->service->getName()
174
		);
175
	}
176
177
	private function elementsToJson( array $elements ) {
178
		return array_map(
179
			function( BaseElement $element ) {
180
				return $this->elementSerializer->elementToJson( $element );
181
			},
182
			$elements
183
		);
184
	}
185
186
	private function addTrackingCategoryIfNeeded() {
187
		/**
188
		 * @var Parser $wgParser
189
		 */
190
		global $wgParser;
191
192
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
193
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
194
		}
195
	}
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
	private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
205
		$params['centre'] = $this->getCenter( $params['centre'] );
206
207
		$iconUrl = $this->fileUrlFinder->getUrlForFileName( $params['icon'] );
208
		$visitedIconUrl = $this->fileUrlFinder->getUrlForFileName( $params['visitedicon'] );
209
210
		$params['locations'] = $this->getJsonForStaticLocations(
211
			$params['staticlocations'],
212
			$params,
213
			$iconUrl,
214
			$visitedIconUrl
215
		);
216
217
		unset( $params['staticlocations'] );
218
219
		$params['locations'] = array_merge(
220
			$params['locations'],
221
			$this->getJsonForLocations(
222
				$queryHandler->getLocations(),
223
				$params,
224
				$iconUrl,
225
				$visitedIconUrl
226
			)
227
		);
228
	}
229
230
	private function getCenter( $coordinatesOrAddress ) {
231
		if ( $coordinatesOrAddress === false ) {
232
			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
	private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
248
		$locationsJson = [];
249
250
		foreach ( $staticLocations as $location ) {
251
			$locationsJson[] = $this->getJsonForStaticLocation(
252
				$location,
253
				$params,
254
				$iconUrl,
255
				$visitedIconUrl
256
			);
257
		}
258
259
		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
	private function getJsonForLocations( iterable $locations, array $params, string $iconUrl, string $visitedIconUrl ): array {
283
		$locationsJson = [];
284
285
		foreach ( $locations as $location ) {
286
			$jsonObj = $location->getJSONObject(
287
				$params['title'],
288
				$params['label'],
289
				$iconUrl,
290
				'',
291
				'',
292
				$visitedIconUrl
293
			);
294
295
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
296
297
			$locationsJson[] = $jsonObj;
298
		}
299
300
		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
	public function getParameters() {
318
		$params = parent::getParameters();
319
		$paramInfo = $this->getParameterInfo();
320
321
		// Do not display this as an option, as the format already determines it
322
		// TODO: this can probably be done cleaner with some changes in Maps
323
		unset( $paramInfo['mappingservice'] );
324
325
		$params = array_merge( $params, $paramInfo );
326
327
		return $params;
328
	}
329
330
	/**
331
	 * Returns an array containing the parameter info.
332
	 *
333
	 * @return array
334
	 */
335
	private function getParameterInfo() {
336
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
337
338
		$params = array_merge(
339
			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...essor\ParamDefinition>>.

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...
Deprecated Code introduced by
The method ParamProcessor\ParamDefi...::getCleanDefinitions() has been deprecated with message: since 1.7 - use ParamDefinitionFactory

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

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