Completed
Push — master ( af4e66...ea2b76 )
by Jeroen De
05:38
created

MapPrinter::getResultText()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 67

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 7.0061

Importance

Changes 0
Metric Value
dl 0
loc 67
ccs 38
cts 40
cp 0.95
rs 7.7866
c 0
b 0
f 0
cc 7
nc 10
nop 2
crap 7.0061

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 2
	public function __construct( $format, $inline = true ) {
64 2
		$this->service = self::$services[$format];
65
66 2
		parent::__construct( $format, $inline );
67 2
	}
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 2
	private function getParser(): Parser {
85 2
		$parser = $GLOBALS['wgParser'];
86
87 2
		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 2
			return $parser->_newObject();
89
		}
90
91
		return $parser;
92
	}
93
94 2
	private function getParserClone(): Parser {
95 2
		$parser = $this->getParser();
96 2
		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 2
	public final function getResultText( SMWQueryResult $res, $outputMode ) {
108 2
		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 2
		$this->isHTML = true;
113
114 2
		$factory = \Maps\MapsFactory::newDefault();
115 2
		$this->locationParser = $factory->newLocationParser();
116 2
		$this->fileUrlFinder = $factory->getFileUrlFinder();
117
118 2
		$this->wikitextParser = new WikitextParser( $this->getParserClone() );
119 2
		$this->elementSerializer = new ElementJsonSerializer( $this->wikitextParser );
120
121 2
		$this->addTrackingCategoryIfNeeded();
122
123 2
		$params = $this->params;
124
125 2
		$queryHandler = new QueryHandler( $res, $outputMode );
126 2
		$queryHandler->setLinkStyle( $params['link'] );
127 2
		$queryHandler->setHeaderStyle( $params['headers'] );
128 2
		$queryHandler->setShowSubject( $params['showtitle'] );
129 2
		$queryHandler->setTemplate( $params['template'] );
130 2
		$queryHandler->setUserParam( $params['userparam'] );
131 2
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
132 2
		$queryHandler->setActiveIcon( $params['activeicon'] );
133
134 2
		$this->handleMarkerData( $params, $queryHandler );
135
136 2
		$params['lines'] = $this->elementsToJson( $params['lines'] );
137 2
		$params['polygons'] = $this->elementsToJson( $params['polygons'] );
138 2
		$params['circles'] = $this->elementsToJson( $params['circles'] );
139 2
		$params['rectangles'] = $this->elementsToJson( $params['rectangles'] );
140
141 2
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
142
143 2
		if ( $params['locations'] === [] ) {
144
			return $params['default'];
145
		}
146
147
		// We can only take care of the zoom defaulting here,
148
		// as not all locations are available in whats passed to Validator.
149 2
		if ( $this->fullParams['zoom']->wasSetToDefault() && count( $params['locations'] ) > 1 ) {
150 2
			$params['zoom'] = false;
151
		}
152
153 2
		$mapId = $this->service->newMapId();
154
155 2
		SMWOutputs::requireHeadItem(
156 2
			$mapId,
157 2
			$this->service->getDependencyHtml( $params )
158
		);
159
160 2
		foreach ( $this->service->getResourceModules( $params ) as $resourceModule ) {
161 2
			SMWOutputs::requireResource( $resourceModule );
162
		}
163
164 2
		if ( array_key_exists( 'source', $params ) ) {
165 2
			unset( $params['source'] );
166
		}
167
168 2
		return ( new MapHtmlBuilder() )->getMapHTML(
169 2
			$this->service->processedParamsToMapParams( $params ),
170
			$mapId,
171 2
			$this->service->getName()
172
		);
173
	}
174
175 2
	private function elementsToJson( array $elements ) {
176 2
		return array_map(
177 2
			function( BaseElement $element ) {
178
				return $this->elementSerializer->elementToJson( $element );
179 2
			},
180
			$elements
181
		);
182
	}
183
184 2
	private function addTrackingCategoryIfNeeded() {
185
		/**
186
		 * @var Parser $wgParser
187
		 */
188 2
		global $wgParser;
189
190 2
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
191
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
192
		}
193 2
	}
194
195
	/**
196
	 * Converts the data in the coordinates parameter to JSON-ready objects.
197
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
198
	 *
199
	 * @param array &$params
200
	 * @param QueryHandler $queryHandler
201
	 */
202 2
	private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
203 2
		$params['centre'] = $this->getCenter( $params['centre'] );
204
205 2
		$iconUrl = $this->fileUrlFinder->getUrlForFileName( $params['icon'] );
206 2
		$visitedIconUrl = $this->fileUrlFinder->getUrlForFileName( $params['visitedicon'] ?? '' );
207
208 2
		$params['locations'] = $this->getJsonForStaticLocations(
209 2
			$params['staticlocations'],
210
			$params,
211
			$iconUrl,
212
			$visitedIconUrl
213
		);
214
215 2
		unset( $params['staticlocations'] );
216
217 2
		$params['locations'] = array_merge(
218 2
			$params['locations'],
219 2
			$this->getJsonForLocations(
220 2
				$queryHandler->getLocations(),
221
				$params,
222
				$iconUrl,
223
				$visitedIconUrl
224
			)
225
		);
226 2
	}
227
228 2
	private function getCenter( $coordinatesOrAddress ) {
229 2
		if ( $coordinatesOrAddress === false ) {
230 2
			return false;
231
		}
232
233
		try {
234
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
235
			$location = $this->locationParser->parse( $coordinatesOrAddress );
236
		}
237
		catch ( \Exception $ex ) {
238
			// TODO: somehow report this to the user
239
			return false;
240
		}
241
242
		return $location->getJSONObject();
243
	}
244
245 2
	private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
246 2
		$locationsJson = [];
247
248 2
		foreach ( $staticLocations as $location ) {
249
			$locationsJson[] = $this->getJsonForStaticLocation(
250
				$location,
251
				$params,
252
				$iconUrl,
253
				$visitedIconUrl
254
			);
255
		}
256
257 2
		return $locationsJson;
258
	}
259
260
	private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl ) {
261
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
262
263
		$this->elementSerializer->titleAndText( $jsonObj );
264
265
		if ( $params['pagelabel'] ) {
266
			$jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
267
		}
268
269
		return $jsonObj;
270
	}
271
272
	/**
273
	 * @param Location[] $locations
274
	 * @param array $params
275
	 * @param string $iconUrl
276
	 * @param string $visitedIconUrl
277
	 *
278
	 * @return array
279
	 */
280 2
	private function getJsonForLocations( iterable $locations, array $params, string $iconUrl, string $visitedIconUrl ): array {
281 2
		$locationsJson = [];
282
283 2
		foreach ( $locations as $location ) {
284 2
			$jsonObj = $location->getJSONObject(
285 2
				$params['title'],
286 2
				$params['label'],
287
				$iconUrl,
288 2
				'',
289 2
				'',
290
				$visitedIconUrl
291
			);
292
293 2
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
294
295 2
			$locationsJson[] = $jsonObj;
296
		}
297
298 2
		return $locationsJson;
299
	}
300
301
	/**
302
	 * Returns the internationalized name of the mapping service.
303
	 *
304
	 * @return string
305
	 */
306
	public final function getName() {
307
		return wfMessage( 'maps_' . $this->service->getName() )->text();
308
	}
309
310
	/**
311
	 * Returns a list of parameter information, for usage by Special:Ask and others.
312
	 *
313
	 * @return array
314
	 */
315 2
	public function getParameters() {
316 2
		$params = parent::getParameters();
317 2
		$paramInfo = $this->getParameterInfo();
318
319 2
		$params = array_merge( $params, $paramInfo );
320
321 2
		return $params;
322
	}
323
324
	/**
325
	 * Returns an array containing the parameter info.
326
	 *
327
	 * @return array
328
	 */
329 2
	private function getParameterInfo() {
330 2
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
331
332 2
		$params = $this->service->getParameterInfo();
333
334 2
		$params['staticlocations'] = [
335
			'type' => 'mapslocation',
336
			'aliases' => [ 'locations', 'points' ],
337
			'default' => [],
338
			'islist' => true,
339
			'delimiter' => ';',
340
			'message' => 'semanticmaps-par-staticlocations',
341
		];
342
343 2
		$params['showtitle'] = [
344 2
			'type' => 'boolean',
345 2
			'aliases' => 'show title',
346 2
			'default' => $smgQPShowTitle,
347
		];
348
349 2
		$params['hidenamespace'] = [
350 2
			'type' => 'boolean',
351 2
			'aliases' => 'hide namespace',
352 2
			'default' => $smgQPHideNamespace,
353
		];
354
355 2
		$params['template'] = [
356 2
			'default' => $smgQPTemplate,
357
		];
358
359 2
		$params['userparam'] = [
360
			'default' => '',
361
		];
362
363 2
		$params['activeicon'] = [
364
			'type' => 'string',
365
			'default' => '',
366
		];
367
368 2
		$params['pagelabel'] = [
369
			'type' => 'boolean',
370
			'default' => false,
371
		];
372
373 2
		$params['ajaxcoordproperty'] = [
374
			'default' => '',
375
		];
376
377 2
		$params['ajaxquery'] = [
378
			'default' => '',
379
			'type' => 'string'
380
		];
381
382
		// Messages:
383
		// semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
384
		// semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
385
		// semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
386 2
		foreach ( $params as $name => &$data ) {
387 2
			if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
388 2
				$data['message'] = 'semanticmaps-par-' . $name;
389
			}
390
		}
391
392 2
		return $params;
393
	}
394
}
395