Completed
Push — leaflet-attribution ( 0121c0 )
by Peter
04:35
created

MapsDisplayMapRenderer::getLocationJson()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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