Completed
Push — mv ( 03c11a...584528 )
by Jeroen De
03:24 queued 44s
created

MapsDisplayMapRenderer   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 290
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 75.84%

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 5
dl 0
loc 290
ccs 113
cts 149
cp 0.7584
rs 8.72
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A renderMap() 0 17 1
A initializeLocationParser() 0 3 1
A handleMarkerData() 0 19 3
A getCenter() 0 16 3
A getLocationJson() 0 26 3
A getLocationJsonObject() 0 27 4
B handleShapeData() 0 34 7
B evilOpenLayersHack() 0 36 9
A getMapHTML() 0 16 1
C getLayerDependencies() 0 38 13

How to fix   Complexity   

Complex Class

Complex classes like MapsDisplayMapRenderer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MapsDisplayMapRenderer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
use Maps\Element;
4
use Maps\Elements\Location;
5
use Maps\LocationParser;
6
7
/**
8
 * Class handling the #display_map rendering.
9
 *
10
 * @licence GNU GPL v2+
11
 * @author Jeroen De Dauw < [email protected] >
12
 * @author Kim Eik
13
 */
14
class MapsDisplayMapRenderer {
15
16
	public $service;
17
18
	/**
19
	 * @var LocationParser
20
	 */
21
	private $locationParser;
22
23 19
	public function __construct( MapsMappingService $service = null ) {
24 19
		$this->service = $service;
25 19
	}
26
27
	/**
28
	 * Handles the request from the parser hook by doing the work that's common for all
29
	 * mapping services, calling the specific methods and finally returning the resulting output.
30
	 *
31
	 * @param array $params
32
	 * @param Parser $parser
33
	 *
34
	 * @return string
35
	 */
36 19
	public final function renderMap( array $params, Parser $parser ) {
37 19
		$this->initializeLocationParser();
38
39 19
		$this->handleMarkerData( $params, $parser );
40
41 19
		$mapName = $this->service->getMapId();
42
43 19
		$output = $this->getMapHTML( $params, $mapName );
44
45 19
		$this->service->addHtmlDependencies(
46 19
			self::getLayerDependencies( $params['mappingservice'], $params )
47
		);
48
49 19
		$this->service->addDependencies( $parser->getOutput() );
50
51 19
		return $output;
52
	}
53
54 19
	private function initializeLocationParser() {
55 19
		$this->locationParser = \Maps\MapsFactory::newDefault()->newLocationParser();
56 19
	}
57
58
	/**
59
	 * Converts the data in the coordinates parameter to JSON-ready objects.
60
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
61
	 */
62 19
	private function handleMarkerData( array &$params, Parser $parser ) {
63 19
		$params['centre'] = $this->getCenter( $params['centre'] );
64
65 19
		$parserClone = clone $parser;
66
67 19
		if ( is_object( $params['wmsoverlay'] ) ) {
68
			$params['wmsoverlay'] = $params['wmsoverlay']->getJSONObject();
69
		}
70
71 19
		$params['locations'] = $this->getLocationJson( $params, $parserClone );
72
73 19
		unset( $params['coordinates'] );
74
75 19
		$this->handleShapeData( $params, $parserClone );
76
77 19
		if ( $params['mappingservice'] === 'openlayers' ) {
78
			$params['layers'] = self::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...
79
		}
80 19
	}
81
82 19
	private function getCenter( $coordinatesOrAddress ) {
83 19
		if ( $coordinatesOrAddress === false ) {
84 19
			return false;
85
		}
86
87
		try {
88
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
89
			$location = $this->locationParser->parse( $coordinatesOrAddress );
90
		}
91
		catch ( \Exception $ex ) {
92
			// TODO: somehow report this to the user
93
			return false;
94
		}
95
96
		return $location->getJSONObject();
97
	}
98
99 19
	private function getLocationJson( array $params, $parserClone ) {
100 19
		$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...
101 19
		$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...
102
103 19
		$locationJsonObjects = [];
104
105 19
		foreach ( $params['coordinates'] as $coordinatesOrAddress ) {
106
			try {
107 14
				$location = $this->locationParser->parse( $coordinatesOrAddress );
108
			}
109 1
			catch ( \Exception $ex ) {
110
				// TODO: somehow report this to the user
111 1
				continue;
112
			}
113
114 13
			$locationJsonObjects[] = $this->getLocationJsonObject(
115 13
				$location,
116 13
				$params,
117 13
				$iconUrl,
118 13
				$visitedIconUrl,
119 13
				$parserClone
120
			);
121
		}
122
123 19
		return $locationJsonObjects;
124
	}
125
126 13
	private function getLocationJsonObject( Location $location, array $params, $iconUrl, $visitedIconUrl, Parser $parserClone ) {
127 13
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
128
129 13
		$jsonObj['title'] = $parserClone->parse(
130 13
			$jsonObj['title'],
131 13
			$parserClone->getTitle(),
132 13
			new ParserOptions()
133 13
		)->getText();
134 13
		$jsonObj['text'] = $parserClone->parse(
135 13
			$jsonObj['text'],
136 13
			$parserClone->getTitle(),
137 13
			new ParserOptions()
138 13
		)->getText();
139
140 13
		if ( isset( $jsonObj['inlineLabel'] ) ) {
141 1
			$jsonObj['inlineLabel'] = strip_tags(
142 1
				$parserClone->parse( $jsonObj['inlineLabel'], $parserClone->getTitle(), new ParserOptions() )->getText(),
143 1
				'<a><img>'
144
			);
145
		}
146
147 13
		$hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== '';
148 13
		$jsonObj['text'] = ( $hasTitleAndtext ? '<b>' . $jsonObj['title'] . '</b><hr />' : $jsonObj['title'] ) . $jsonObj['text'];
149 13
		$jsonObj['title'] = strip_tags( $jsonObj['title'] );
150
151 13
		return $jsonObj;
152
	}
153
154 19
	private function handleShapeData( array &$params, Parser $parserClone ) {
155
		$textContainers = [
156 19
			&$params['lines'],
157 19
			&$params['polygons'],
158 19
			&$params['circles'],
159 19
			&$params['rectangles'],
160 19
			&$params['imageoverlays'], // FIXME: this is Google Maps specific!!
161
		];
162
163 19
		foreach ( $textContainers as &$textContainer ) {
164 19
			if ( is_array( $textContainer ) ) {
165 19
				foreach ( $textContainer as &$obj ) {
166 5
					if ( $obj instanceof Element ) {
167 5
						$obj = $obj->getArrayValue();
168
					}
169
170 5
					$obj['title'] = $parserClone->parse(
171 5
						$obj['title'],
172 5
						$parserClone->getTitle(),
173 5
						new ParserOptions()
174 5
					)->getText();
175 5
					$obj['text'] = $parserClone->parse(
176 5
						$obj['text'],
177 5
						$parserClone->getTitle(),
178 5
						new ParserOptions()
179 5
					)->getText();
180
181 5
					$hasTitleAndtext = $obj['title'] !== '' && $obj['text'] !== '';
182 5
					$obj['text'] = ( $hasTitleAndtext ? '<b>' . $obj['title'] . '</b><hr />' : $obj['title'] ) . $obj['text'];
183 19
					$obj['title'] = strip_tags( $obj['title'] );
184
				}
185
			}
186
		}
187 19
	}
188
189
	/**
190
	 * FIXME
191
	 *
192
	 * Temporary hack until the mapping service handling gets a proper refactor
193
	 * This kind of JS construction is also rather evil and should not be done at this point
194
	 *
195
	 * @since 3.0
196
	 * @deprecated
197
	 *
198
	 * @param string[] $layers
199
	 *
200
	 * @return string[]
201
	 */
202
	public static function evilOpenLayersHack( $layers ) {
203
		global $egMapsOLLayerGroups, $egMapsOLAvailableLayers;
204
205
		$layerDefs = [];
206
		$layerNames = [];
207
208
		foreach ( $layers as $layerOrGroup ) {
209
			$lcLayerOrGroup = strtolower( $layerOrGroup );
210
211
			// Layer groups. Loop over all items and add them if not present yet:
212
			if ( array_key_exists( $lcLayerOrGroup, $egMapsOLLayerGroups ) ) {
213
				foreach ( $egMapsOLLayerGroups[$lcLayerOrGroup] as $layerName ) {
214
					if ( !in_array( $layerName, $layerNames ) ) {
215
						if ( is_array( $egMapsOLAvailableLayers[$layerName] ) ) {
216
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName][0];
217
						} else {
218
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName];
219
						}
220
						$layerNames[] = $layerName;
221
					}
222
				}
223
			} // Single layers. Add them if not present yet:
224
			elseif ( array_key_exists( $lcLayerOrGroup, $egMapsOLAvailableLayers ) ) {
225
				if ( !in_array( $lcLayerOrGroup, $layerNames ) ) {
226
					if ( is_array( $egMapsOLAvailableLayers[$lcLayerOrGroup] ) ) {
227
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup][0];
228
					} else {
229
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup];
230
					}
231
232
					$layerNames[] = $lcLayerOrGroup;
233
				}
234
			}
235
		}
236
		return $layerDefs;
237
	}
238
239
	/**
240
	 * Returns the HTML to display the map.
241
	 *
242
	 * @param array $params
243
	 * @param string $mapName
244
	 *
245
	 * @return string
246
	 */
247 19
	protected function getMapHTML( array $params, $mapName ) {
248 19
		return Html::rawElement(
249 19
			'div',
250
			[
251 19
				'id' => $mapName,
252 19
				'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
253 19
				'class' => 'maps-map maps-' . $this->service->getName()
254
			],
255 19
			wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
256 19
			Html::element(
257 19
				'div',
258 19
				[ 'style' => 'display:none', 'class' => 'mapdata' ],
259 19
				FormatJson::encode( $params )
260
			)
261
		);
262
	}
263
264 19
	public static function getLayerDependencies( $service, $params ) {
265 19
		global $egMapsOLLayerDependencies, $egMapsOLAvailableLayers,
266 19
			   $egMapsLeafletLayerDependencies, $egMapsLeafletAvailableLayers,
267 19
			   $egMapsLeafletLayersApiKeys;
268
269 19
		$layerDependencies = [];
270
271 19
		if ( $service === 'leaflet' ) {
272 12
			$layerNames = $params['layers'];
273 12
			foreach ( $layerNames as $layerName ) {
274 12
				if ( array_key_exists( $layerName, $egMapsLeafletAvailableLayers )
275 12
					&& $egMapsLeafletAvailableLayers[$layerName]
276 12
					&& array_key_exists( $layerName, $egMapsLeafletLayersApiKeys )
277 12
					&& array_key_exists( $layerName, $egMapsLeafletLayerDependencies ) ) {
278
					$layerDependencies[] = '<script src="' . $egMapsLeafletLayerDependencies[$layerName] .
279 12
						$egMapsLeafletLayersApiKeys[$layerName] . '"></script>';
280
				}
281
			}
282
		} else {
283 7
			if ( $service === 'openlayers' ) {
284
				$layerNames = $params['layers'];
285
				foreach ( $layerNames as $layerName ) {
286
					if ( array_key_exists( $layerName, $egMapsOLAvailableLayers ) // The layer must be defined in php
287
						&& is_array( $egMapsOLAvailableLayers[$layerName] ) // The layer must be an array...
288
						&& count( $egMapsOLAvailableLayers[$layerName] ) > 1 // ...with a second element...
289
						&& array_key_exists(
290
							$egMapsOLAvailableLayers[$layerName][1],
291
							$egMapsOLLayerDependencies
292
						) ) { //...that is a dependency.
293
						$layerDependencies[] = $egMapsOLLayerDependencies[$egMapsOLAvailableLayers[$layerName][1]];
294
					}
295
				}
296
297
			}
298
		}
299
300 19
		return array_unique( $layerDependencies );
301
	}
302
303
}
304