Completed
Push — master ( 4a4116...883737 )
by Jeroen De
15:46 queued 05:50
created

DisplayMapRenderer::getLocationJsonObject()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 23
cts 23
cp 1
rs 8.7697
c 0
b 0
f 0
cc 6
nc 32
nop 5
crap 6
1
<?php
2
3
namespace Maps\MediaWiki\ParserHooks;
4
5
use FormatJson;
6
use Html;
7
use Maps\Elements\Location;
8
use Maps\MapsFunctions;
9
use Maps\MappingService;
10
use Maps\Presentation\WikitextParsers\LocationParser;
11
use Parser;
12
use ParserOptions;
13
14
/**
15
 * Class handling the #display_map rendering.
16
 *
17
 * @licence GNU GPL v2+
18
 * @author Jeroen De Dauw < [email protected] >
19
 * @author Kim Eik
20
 */
21
class DisplayMapRenderer {
22
23
	public $service;
24
25
	/**
26
	 * @var LocationParser
27
	 */
28
	private $locationParser;
29
30 19
	public function __construct( MappingService $service = null ) {
31 19
		$this->service = $service;
32 19
	}
33
34
	/**
35
	 * Handles the request from the parser hook by doing the work that's common for all
36
	 * mapping services, calling the specific methods and finally returning the resulting output.
37
	 *
38
	 * @param array $params
39
	 * @param Parser $parser
40
	 *
41
	 * @return string
42
	 */
43 19
	public final function renderMap( array $params, Parser $parser ) {
44 19
		$this->initializeLocationParser();
45
46 19
		$this->handleMarkerData( $params, $parser );
47
48 19
		$mapName = $this->service->getMapId();
49
50 19
		$output = $this->getMapHTML( $params, $mapName );
51
52 19
		$this->service->addHtmlDependencies(
53 19
			self::getLayerDependencies( $params['mappingservice'], $params )
54
		);
55
56 19
		$this->service->addDependencies( $parser->getOutput() );
57
58 19
		return $output;
59
	}
60
61 19
	private function initializeLocationParser() {
62 19
		$this->locationParser = \Maps\MapsFactory::newDefault()->newLocationParser();
63 19
	}
64
65
	/**
66
	 * Converts the data in the coordinates parameter to JSON-ready objects.
67
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
68
	 */
69 19
	private function handleMarkerData( array &$params, Parser $parser ) {
70 19
		$params['centre'] = $this->getCenter( $params['centre'] );
71
72 19
		$parserClone = clone $parser;
73
74 19
		if ( is_object( $params['wmsoverlay'] ) ) {
75
			$params['wmsoverlay'] = $params['wmsoverlay']->getJSONObject();
76
		}
77
78 19
		$params['locations'] = $this->getLocationJson( $params, $parserClone );
79
80 19
		unset( $params['coordinates'] );
81
82 19
		$this->handleShapeData( $params, $parserClone );
83
84 19
		if ( $params['mappingservice'] === 'openlayers' ) {
85
			$params['layers'] = self::evilOpenLayersHack( $params['layers'] );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MediaWiki\ParserHoo...r::evilOpenLayersHack() has been deprecated.

This method has been deprecated.

Loading history...
86
		}
87 19
	}
88
89 19
	private function getCenter( $coordinatesOrAddress ) {
90 19
		if ( $coordinatesOrAddress === false ) {
91 19
			return false;
92
		}
93
94
		try {
95
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
96
			$location = $this->locationParser->parse( $coordinatesOrAddress );
97
		}
98
		catch ( \Exception $ex ) {
99
			// TODO: somehow report this to the user
100
			return false;
101
		}
102
103
		return $location->getJSONObject();
104
	}
105
106 19
	private function getLocationJson( array $params, $parserClone ) {
107 19
		$iconUrl = MapsFunctions::getFileUrl( $params['icon'] );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MapsFunctions::getFileUrl() has been deprecated.

This method has been deprecated.

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

This method has been deprecated.

Loading history...
109
110 19
		$locationJsonObjects = [];
111
112 19
		foreach ( $params['coordinates'] as $coordinatesOrAddress ) {
113
			try {
114 14
				$location = $this->locationParser->parse( $coordinatesOrAddress );
115
			}
116 1
			catch ( \Exception $ex ) {
117
				// TODO: somehow report this to the user
118 1
				continue;
119
			}
120
121 13
			$locationJsonObjects[] = $this->getLocationJsonObject(
122 13
				$location,
123 13
				$params,
124 13
				$iconUrl,
125 13
				$visitedIconUrl,
126 13
				$parserClone
127
			);
128
		}
129
130 19
		return $locationJsonObjects;
131
	}
132
133 13
	private function getLocationJsonObject( Location $location, array $params, $iconUrl, $visitedIconUrl, Parser $parserClone ) {
134 13
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
135
136 13
		if ( trim( $jsonObj['title'] ) !== '' ) {
137 13
			$jsonObj['title'] = $parserClone->parse(
138 13
				$jsonObj['title'],
139 13
				$parserClone->getTitle(),
140 13
				new ParserOptions()
141 13
			)->getText();
142 13
		}
143 13
144 13
		if ( trim( $jsonObj['text'] ) !== '' ) {
145 13
			$jsonObj['text'] = $parserClone->parse(
146
				$jsonObj['text'],
147 13
				$parserClone->getTitle(),
148 1
				new ParserOptions()
149 1
			)->getText();
150
		}
151 1
152
		if ( isset( $jsonObj['inlineLabel'] ) ) {
153
			$jsonObj['inlineLabel'] = strip_tags(
154
				$parserClone->parse( $jsonObj['inlineLabel'], $parserClone->getTitle(), new ParserOptions() )->getText(
155 13
				),
156 13
				'<a><img>'
157 13
			);
158
		}
159 13
160
		$hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== '';
161
		$jsonObj['text'] = ( $hasTitleAndtext ? '<b>' . $jsonObj['title'] . '</b><hr />' : $jsonObj['title'] ) . $jsonObj['text'];
162 19
		$jsonObj['title'] = strip_tags( $jsonObj['title'] );
163
164 19
		return $jsonObj;
165 19
	}
166 19
167 19
	private function handleShapeData( array &$params, Parser $parserClone ) {
168 19
		$textContainers = [
169
			&$params['lines'],
170
			&$params['polygons'],
171 19
			&$params['circles'],
172 19
			&$params['rectangles'],
173 19
			&$params['imageoverlays'], // FIXME: this is Google Maps specific!!
174 5
		];
175 5
176
		foreach ( $textContainers as &$textContainer ) {
177
			if ( is_array( $textContainer ) ) {
178 5
				foreach ( $textContainer as &$obj ) {
179 5
					if ( method_exists( $obj, 'getArrayValue' ) ) {
180 5
						$obj = $obj->getArrayValue();
181 5
					}
182 5
183 5
					$obj['title'] = $parserClone->parse(
184 5
						$obj['title'],
185 5
						$parserClone->getTitle(),
186 5
						new ParserOptions()
187 5
					)->getText();
188
					$obj['text'] = $parserClone->parse(
189 5
						$obj['text'],
190 5
						$parserClone->getTitle(),
191 19
						new ParserOptions()
192
					)->getText();
193
194
					$hasTitleAndtext = $obj['title'] !== '' && $obj['text'] !== '';
195 19
					$obj['text'] = ( $hasTitleAndtext ? '<b>' . $obj['title'] . '</b><hr />' : $obj['title'] ) . $obj['text'];
196
					$obj['title'] = strip_tags( $obj['title'] );
197
				}
198
			}
199
		}
200
	}
201
202
	/**
203
	 * FIXME
204
	 *
205
	 * Temporary hack until the mapping service handling gets a proper refactor
206
	 * This kind of JS construction is also rather evil and should not be done at this point
207
	 *
208
	 * @since 3.0
209
	 * @deprecated
210
	 *
211
	 * @param string[] $layers
212
	 *
213
	 * @return string[]
214
	 */
215
	public static function evilOpenLayersHack( $layers ) {
216
		global $egMapsOLLayerGroups, $egMapsOLAvailableLayers;
217
218
		$layerDefs = [];
219
		$layerNames = [];
220
221
		foreach ( $layers as $layerOrGroup ) {
222
			$lcLayerOrGroup = strtolower( $layerOrGroup );
223
224
			// Layer groups. Loop over all items and add them if not present yet:
225
			if ( array_key_exists( $lcLayerOrGroup, $egMapsOLLayerGroups ) ) {
226
				foreach ( $egMapsOLLayerGroups[$lcLayerOrGroup] as $layerName ) {
227
					if ( !in_array( $layerName, $layerNames ) ) {
228
						if ( is_array( $egMapsOLAvailableLayers[$layerName] ) ) {
229
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName][0];
230
						} else {
231
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName];
232
						}
233
						$layerNames[] = $layerName;
234
					}
235
				}
236
			} // Single layers. Add them if not present yet:
237
			elseif ( array_key_exists( $lcLayerOrGroup, $egMapsOLAvailableLayers ) ) {
238
				if ( !in_array( $lcLayerOrGroup, $layerNames ) ) {
239
					if ( is_array( $egMapsOLAvailableLayers[$lcLayerOrGroup] ) ) {
240
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup][0];
241
					} else {
242
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup];
243
					}
244
245
					$layerNames[] = $lcLayerOrGroup;
246
				}
247
			}
248
		}
249
		return $layerDefs;
250
	}
251
252
	/**
253
	 * Returns the HTML to display the map.
254
	 *
255 19
	 * @param array $params
256 19
	 * @param string $mapName
257 19
	 *
258
	 * @return string
259 19
	 */
260 19
	protected function getMapHTML( array $params, $mapName ) {
261 19
		return Html::rawElement(
262
			'div',
263 19
			[
264 19
				'id' => $mapName,
265 19
				'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
266 19
				'class' => 'maps-map maps-' . $this->service->getName()
267 19
			],
268
			wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
269
			Html::element(
270
				'div',
271
				[ 'style' => 'display:none', 'class' => 'mapdata' ],
272 19
				FormatJson::encode( $params )
273 19
			)
274 19
		);
275 19
	}
276
277 19
	public static function getLayerDependencies( $service, $params ) {
278
		global $egMapsOLLayerDependencies, $egMapsOLAvailableLayers,
279 19
			   $egMapsLeafletLayerDependencies, $egMapsLeafletAvailableLayers,
280 12
			   $egMapsLeafletLayersApiKeys;
281 12
282 12
		$layerDependencies = [];
283 12
284 12
		if ( $service === 'leaflet' ) {
285 12
			$layerNames = $params['layers'];
286
			foreach ( $layerNames as $layerName ) {
287 12
				if ( array_key_exists( $layerName, $egMapsLeafletAvailableLayers )
288
					&& $egMapsLeafletAvailableLayers[$layerName]
289
					&& array_key_exists( $layerName, $egMapsLeafletLayersApiKeys )
290
					&& array_key_exists( $layerName, $egMapsLeafletLayerDependencies ) ) {
291 7
					$layerDependencies[] = '<script src="' . $egMapsLeafletLayerDependencies[$layerName] .
292
						$egMapsLeafletLayersApiKeys[$layerName] . '"></script>';
293
				}
294
			}
295
		} else {
296
			if ( $service === 'openlayers' ) {
297
				$layerNames = $params['layers'];
298
				foreach ( $layerNames as $layerName ) {
299
					if ( array_key_exists( $layerName, $egMapsOLAvailableLayers ) // The layer must be defined in php
300
						&& is_array( $egMapsOLAvailableLayers[$layerName] ) // The layer must be an array...
301
						&& count( $egMapsOLAvailableLayers[$layerName] ) > 1 // ...with a second element...
302
						&& array_key_exists(
303
							$egMapsOLAvailableLayers[$layerName][1],
304
							$egMapsOLLayerDependencies
305
						) ) { //...that is a dependency.
306
						$layerDependencies[] = $egMapsOLLayerDependencies[$egMapsOLAvailableLayers[$layerName][1]];
307
					}
308 19
				}
309
310
			}
311
		}
312
313
		return array_unique( $layerDependencies );
314
	}
315
316
}
317