Completed
Push — move ( 875a6f )
by Jeroen De
04:17
created

MapPrinter::getResultText()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 57
ccs 0
cts 41
cp 0
rs 8.0048
c 0
b 0
f 0
cc 7
nc 10
nop 2
crap 56

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 FormatJson;
6
use Html;
7
use Linker;
8
use Maps\Element;
9
use Maps\Elements\BaseElement;
10
use Maps\Elements\Location;
11
use Maps\LocationParser;
12
use MapsDisplayMapRenderer;
13
use MapsMapper;
14
use MapsMappingService;
15
use ParamProcessor\ParamDefinition;
16
use Parser;
17
use ParserOptions;
18
use SMW;
19
use SMWOutputs;
20
use SMWQueryResult;
21
use Title;
22
23
/**
24
 * Query printer for maps. Is invoked via SMMapper.
25
 * Can be overridden per service to have custom output.
26
 *
27
 * @ingroup SemanticMaps
28
 *
29
 * @licence GNU GPL v2+
30
 * @author Jeroen De Dauw < [email protected] >
31
 * @author Peter Grassberger < [email protected] >
32
 */
33
class MapPrinter extends SMW\ResultPrinter {
34
35
	private static $services = [];
36
37
	/**
38
	 * @var LocationParser
39
	 */
40
	private $locationParser;
41
	/**
42
	 * @var MapsMappingService
43
	 */
44
	private $service;
45
	/**
46
	 * @var string|boolean
47
	 */
48
	private $fatalErrorMsg = false;
49
50
	/**
51
	 * @param string $format
52
	 * @param bool $inline
53
	 */
54
	public function __construct( $format, $inline = true ) {
55
		$this->service = self::$services[$format];
56
57
		parent::__construct( $format, $inline );
58
	}
59
60
	/**
61
	 * @since 3.4
62
	 * FIXME: this is a temporary hack that should be replaced when SMW allows for dependency
63
	 * injection in query printers.
64
	 *
65
	 * @param MapsMappingService $service
66
	 */
67
	public static function registerService( MapsMappingService $service ) {
68
		self::$services[$service->getName()] = $service;
69
	}
70
71
	public static function registerDefaultService( $serviceName ) {
72
		self::$services['map'] = self::$services[$serviceName];
73
	}
74
75
	/**
76
	 * Builds up and returns the HTML for the map, with the queried coordinate data on it.
77
	 *
78
	 * @param SMWQueryResult $res
79
	 * @param $outputmode
80
	 *
81
	 * @return string
82
	 */
83
	public final function getResultText( SMWQueryResult $res, $outputmode ) {
84
		if ( $this->fatalErrorMsg !== false ) {
85
			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 85 which is incompatible with the return type documented by Maps\SemanticMW\ResultPr...pPrinter::getResultText of type string.
Loading history...
86
		}
87
88
		$this->addTrackingCategoryIfNeeded();
89
90
		$params = $this->params;
91
92
		$this->initializeLocationParser();
93
94
		$queryHandler = new QueryHandler( $res, $outputmode );
95
		$queryHandler->setLinkStyle( $params['link'] );
96
		$queryHandler->setHeaderStyle( $params['headers'] );
97
		$queryHandler->setShowSubject( $params['showtitle'] );
98
		$queryHandler->setTemplate( $params['template'] );
99
		$queryHandler->setUserParam( $params['userparam'] );
100
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
101
		$queryHandler->setActiveIcon( $params['activeicon'] );
102
103
		$this->handleMarkerData( $params, $queryHandler );
104
105
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
106
107
		$this->service->addHtmlDependencies(
108
			MapsDisplayMapRenderer::getLayerDependencies( $params['format'], $params )
109
		);
110
111
		$locationAmount = count( $params['locations'] );
112
113
		if ( $locationAmount > 0 ) {
114
			// We can only take care of the zoom defaulting here,
115
			// as not all locations are available in whats passed to Validator.
116
			if ( $this->fullParams['zoom']->wasSetToDefault() && $locationAmount > 1 ) {
117
				$params['zoom'] = false;
118
			}
119
120
			$mapName = $this->service->getMapId();
121
122
			SMWOutputs::requireHeadItem(
123
				$mapName,
124
				$this->service->getDependencyHtml()
125
			);
126
127
			foreach ( $this->service->getResourceModules() as $resourceModule ) {
128
				SMWOutputs::requireResource( $resourceModule );
129
			}
130
131
			if ( array_key_exists( 'source', $params ) ) {
132
				unset( $params['source'] );
133
			}
134
135
			return $this->getMapHTML( $params, $mapName );
136
		} else {
137
			return $params['default'];
138
		}
139
	}
140
141
	private function addTrackingCategoryIfNeeded() {
0 ignored issues
show
Coding Style introduced by
addTrackingCategoryIfNeeded uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
142
		/**
143
		 * @var Parser $wgParser
144
		 */
145
		global $wgParser;
146
147
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
148
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
149
		}
150
	}
151
152
	private function initializeLocationParser() {
153
		$this->locationParser = \Maps\MapsFactory::newDefault()->newLocationParser();
154
	}
155
156
	/**
157
	 * Converts the data in the coordinates parameter to JSON-ready objects.
158
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
159
	 *
160
	 * @param array &$params
161
	 * @param QueryHandler $queryHandler
162
	 */
163
	private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
164
		$params['centre'] = $this->getCenter( $params['centre'] );
165
166
		$iconUrl = MapsMapper::getFileUrl( $params['icon'] );
0 ignored issues
show
Deprecated Code introduced by
The method MapsMapper::getFileUrl() has been deprecated.

This method has been deprecated.

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

This method has been deprecated.

Loading history...
168
169
		$params['locations'] = $this->getJsonForStaticLocations(
170
			$params['staticlocations'],
171
			$params,
172
			$iconUrl,
173
			$visitedIconUrl
174
		);
175
176
		unset( $params['staticlocations'] );
177
178
		$this->addShapeData( $queryHandler->getShapes(), $params, $iconUrl, $visitedIconUrl );
179
180
		if ( $params['format'] === 'openlayers' ) {
181
			$params['layers'] = MapsDisplayMapRenderer::evilOpenLayersHack( $params['layers'] );
0 ignored issues
show
Deprecated Code introduced by
The method MapsDisplayMapRenderer::evilOpenLayersHack() has been deprecated.

This method has been deprecated.

Loading history...
182
		}
183
	}
184
185
	private function getCenter( $coordinatesOrAddress ) {
186
		if ( $coordinatesOrAddress === false ) {
187
			return false;
188
		}
189
190
		try {
191
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
192
			$location = $this->locationParser->parse( $coordinatesOrAddress );
193
		}
194
		catch ( \Exception $ex ) {
195
			// TODO: somehow report this to the user
196
			return false;
197
		}
198
199
		return $location->getJSONObject();
200
	}
201
202
	private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
0 ignored issues
show
Coding Style introduced by
getJsonForStaticLocations uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
203
		/**
204
		 * @var Parser $wgParser
205
		 */
206
		global $wgParser;
207
208
		$parser = version_compare( $GLOBALS['wgVersion'], '1.18', '<' ) ? $wgParser : clone $wgParser;
209
210
		$locationsJson = [];
211
212
		foreach ( $staticLocations as $location ) {
213
			$locationsJson[] = $this->getJsonForStaticLocation(
214
				$location,
215
				$params,
216
				$iconUrl,
217
				$visitedIconUrl,
218
				$parser
219
			);
220
		}
221
222
		return $locationsJson;
223
	}
224
225
	private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl, Parser $parser ) {
226
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
227
228
		$jsonObj['title'] = $parser->parse( $jsonObj['title'], $parser->getTitle(), new ParserOptions() )->getText();
229
		$jsonObj['text'] = $parser->parse( $jsonObj['text'], $parser->getTitle(), new ParserOptions() )->getText();
230
231
		$hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== '';
232
		$jsonObj['text'] = ( $hasTitleAndtext ? '<b>' . $jsonObj['title'] . '</b><hr />' : $jsonObj['title'] ) . $jsonObj['text'];
233
		$jsonObj['title'] = strip_tags( $jsonObj['title'] );
234
235
		if ( $params['pagelabel'] ) {
236
			$jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
237
		}
238
239
		return $jsonObj;
240
	}
241
242
	/**
243
	 * @param Element[] $queryShapes
244
	 * @param array $params
245
	 * @param string $iconUrl
246
	 * @param string $visitedIconUrl
247
	 */
248
	private function addShapeData( array $queryShapes, array &$params, $iconUrl, $visitedIconUrl ) {
249
		$params['locations'] = array_merge(
250
			$params['locations'],
251
			$this->getJsonForLocations(
252
				$queryShapes['locations'],
253
				$params,
254
				$iconUrl,
255
				$visitedIconUrl
256
			)
257
		);
258
259
		$params['lines'] = $this->getElementJsonArray( $queryShapes['lines'], $params );
260
		$params['polygons'] = $this->getElementJsonArray( $queryShapes['polygons'], $params );
261
	}
262
263
	/**
264
	 * @param Location[] $locations
265
	 * @param array $params
266
	 * @param string $iconUrl
267
	 * @param string $visitedIconUrl
268
	 *
269
	 * @return array
270
	 */
271
	private function getJsonForLocations( array $locations, array $params, $iconUrl, $visitedIconUrl ) {
272
		$locationsJson = [];
273
274
		foreach ( $locations as $location ) {
275
			$jsonObj = $location->getJSONObject(
276
				$params['title'],
277
				$params['label'],
278
				$iconUrl,
279
				'',
280
				'',
281
				$visitedIconUrl
282
			);
283
284
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
285
286
			$locationsJson[] = $jsonObj;
287
		}
288
289
		return $locationsJson;
290
	}
291
292
	/**
293
	 * @param BaseElement[] $elements
294
	 * @param array $params
295
	 *
296
	 * @return array
297
	 */
298
	private function getElementJsonArray( array $elements, array $params ) {
299
		$elementsJson = [];
300
301
		foreach ( $elements as $element ) {
302
			$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...
303
			$elementsJson[] = $jsonObj;
304
		}
305
306
		return $elementsJson;
307
	}
308
309
	/**
310
	 * Returns the HTML to display the map.
311
	 *
312
	 * @param array $params
313
	 * @param string $mapName
314
	 *
315
	 * @return string
316
	 */
317
	private function getMapHTML( array $params, $mapName ) {
318
		return Html::rawElement(
319
			'div',
320
			[
321
				'id' => $mapName,
322
				'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
323
				'class' => 'maps-map maps-' . $this->service->getName()
324
			],
325
			wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
326
			Html::element(
327
				'div',
328
				[ 'style' => 'display:none', 'class' => 'mapdata' ],
329
				FormatJson::encode( $params )
330
			)
331
		);
332
	}
333
334
	/**
335
	 * Returns the internationalized name of the mapping service.
336
	 *
337
	 * @return string
338
	 */
339
	public final function getName() {
340
		return wfMessage( 'maps_' . $this->service->getName() )->text();
341
	}
342
343
	/**
344
	 * Returns a list of parameter information, for usage by Special:Ask and others.
345
	 *
346
	 * @return array
347
	 */
348
	public function getParameters() {
349
		$params = parent::getParameters();
350
		$paramInfo = $this->getParameterInfo();
351
352
		// Do not display this as an option, as the format already determines it
353
		// TODO: this can probably be done cleaner with some changes in Maps
354
		unset( $paramInfo['mappingservice'] );
355
356
		$params = array_merge( $params, $paramInfo );
357
358
		return $params;
359
	}
360
361
	/**
362
	 * Returns an array containing the parameter info.
363
	 *
364
	 * @return array
365
	 */
366
	private function getParameterInfo() {
367
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
368
369
		$params = ParamDefinition::getCleanDefinitions( MapsMapper::getCommonParameters() );
0 ignored issues
show
Documentation introduced by
\MapsMapper::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...
370
371
		$this->service->addParameterInfo( $params );
372
373
		$params['staticlocations'] = [
374
			'type' => 'mapslocation', // FIXME: geoservice is not used
375
			'aliases' => [ 'locations', 'points' ],
376
			'default' => [],
377
			'islist' => true,
378
			'delimiter' => ';',
379
			'message' => 'semanticmaps-par-staticlocations',
380
		];
381
382
		$params['showtitle'] = [
383
			'type' => 'boolean',
384
			'aliases' => 'show title',
385
			'default' => $smgQPShowTitle,
386
		];
387
388
		$params['hidenamespace'] = [
389
			'type' => 'boolean',
390
			'aliases' => 'hide namespace',
391
			'default' => $smgQPHideNamespace,
392
		];
393
394
		$params['template'] = [
395
			'default' => $smgQPTemplate,
396
		];
397
398
		$params['userparam'] = [
399
			'default' => '',
400
		];
401
402
		$params['activeicon'] = [
403
			'type' => 'string',
404
			'default' => '',
405
		];
406
407
		$params['pagelabel'] = [
408
			'type' => 'boolean',
409
			'default' => false,
410
		];
411
412
		$params['ajaxcoordproperty'] = [
413
			'default' => '',
414
		];
415
416
		$params['ajaxquery'] = [
417
			'default' => '',
418
			'type' => 'string'
419
		];
420
421
		// Messages:
422
		// semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
423
		// semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
424
		// semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
425
		foreach ( $params as $name => &$data ) {
426
			if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
427
				$data['message'] = 'semanticmaps-par-' . $name;
428
			}
429
		}
430
431
		return $params;
432
	}
433
}
434