Completed
Push — move ( 889447...795827 )
by Jeroen De
05:20
created

DisplayMapRenderer::handleMarkerData()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.054

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 9
cts 11
cp 0.8182
rs 9.6333
c 0
b 0
f 0
cc 3
nc 4
nop 2
crap 3.054
1
<?php
2
3
namespace Maps\MediaWiki\ParserHooks;
4
5
use FormatJson;
6
use Html;
7
use Maps\Elements\Location;
8
use Maps\Presentation\WikitextParsers\LocationParser;
9
use MapsMapper;
10
use MapsMappingService;
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( MapsMappingService $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 = 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...
108 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...
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
		$jsonObj['title'] = $parserClone->parse(
137 13
			$jsonObj['title'],
138 13
			$parserClone->getTitle(),
139 13
			new ParserOptions()
140 13
		)->getText();
141 13
		$jsonObj['text'] = $parserClone->parse(
142 13
			$jsonObj['text'],
143 13
			$parserClone->getTitle(),
144 13
			new ParserOptions()
145 13
		)->getText();
146
147 13
		if ( isset( $jsonObj['inlineLabel'] ) ) {
148 1
			$jsonObj['inlineLabel'] = strip_tags(
149 1
				$parserClone->parse( $jsonObj['inlineLabel'], $parserClone->getTitle(), new ParserOptions() )->getText(
150
				),
151 1
				'<a><img>'
152
			);
153
		}
154
155 13
		$hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== '';
156 13
		$jsonObj['text'] = ( $hasTitleAndtext ? '<b>' . $jsonObj['title'] . '</b><hr />' : $jsonObj['title'] ) . $jsonObj['text'];
157 13
		$jsonObj['title'] = strip_tags( $jsonObj['title'] );
158
159 13
		return $jsonObj;
160
	}
161
162 19
	private function handleShapeData( array &$params, Parser $parserClone ) {
163
		$textContainers = [
164 19
			&$params['lines'],
165 19
			&$params['polygons'],
166 19
			&$params['circles'],
167 19
			&$params['rectangles'],
168 19
			&$params['imageoverlays'], // FIXME: this is Google Maps specific!!
169
		];
170
171 19
		foreach ( $textContainers as &$textContainer ) {
172 19
			if ( is_array( $textContainer ) ) {
173 19
				foreach ( $textContainer as &$obj ) {
174 5
					if ( method_exists( $obj, 'getArrayValue' ) ) {
175 5
						$obj = $obj->getArrayValue();
176
					}
177
178 5
					$obj['title'] = $parserClone->parse(
179 5
						$obj['title'],
180 5
						$parserClone->getTitle(),
181 5
						new ParserOptions()
182 5
					)->getText();
183 5
					$obj['text'] = $parserClone->parse(
184 5
						$obj['text'],
185 5
						$parserClone->getTitle(),
186 5
						new ParserOptions()
187 5
					)->getText();
188
189 5
					$hasTitleAndtext = $obj['title'] !== '' && $obj['text'] !== '';
190 5
					$obj['text'] = ( $hasTitleAndtext ? '<b>' . $obj['title'] . '</b><hr />' : $obj['title'] ) . $obj['text'];
191 19
					$obj['title'] = strip_tags( $obj['title'] );
192
				}
193
			}
194
		}
195 19
	}
196
197
	/**
198
	 * FIXME
199
	 *
200
	 * Temporary hack until the mapping service handling gets a proper refactor
201
	 * This kind of JS construction is also rather evil and should not be done at this point
202
	 *
203
	 * @since 3.0
204
	 * @deprecated
205
	 *
206
	 * @param string[] $layers
207
	 *
208
	 * @return string[]
209
	 */
210
	public static function evilOpenLayersHack( $layers ) {
211
		global $egMapsOLLayerGroups, $egMapsOLAvailableLayers;
212
213
		$layerDefs = [];
214
		$layerNames = [];
215
216
		foreach ( $layers as $layerOrGroup ) {
217
			$lcLayerOrGroup = strtolower( $layerOrGroup );
218
219
			// Layer groups. Loop over all items and add them if not present yet:
220
			if ( array_key_exists( $lcLayerOrGroup, $egMapsOLLayerGroups ) ) {
221
				foreach ( $egMapsOLLayerGroups[$lcLayerOrGroup] as $layerName ) {
222
					if ( !in_array( $layerName, $layerNames ) ) {
223
						if ( is_array( $egMapsOLAvailableLayers[$layerName] ) ) {
224
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName][0];
225
						} else {
226
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName];
227
						}
228
						$layerNames[] = $layerName;
229
					}
230
				}
231
			} // Single layers. Add them if not present yet:
232
			elseif ( array_key_exists( $lcLayerOrGroup, $egMapsOLAvailableLayers ) ) {
233
				if ( !in_array( $lcLayerOrGroup, $layerNames ) ) {
234
					if ( is_array( $egMapsOLAvailableLayers[$lcLayerOrGroup] ) ) {
235
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup][0];
236
					} else {
237
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup];
238
					}
239
240
					$layerNames[] = $lcLayerOrGroup;
241
				}
242
			}
243
		}
244
		return $layerDefs;
245
	}
246
247
	/**
248
	 * Returns the HTML to display the map.
249
	 *
250
	 * @param array $params
251
	 * @param string $mapName
252
	 *
253
	 * @return string
254
	 */
255 19
	protected function getMapHTML( array $params, $mapName ) {
256 19
		return Html::rawElement(
257 19
			'div',
258
			[
259 19
				'id' => $mapName,
260 19
				'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
261 19
				'class' => 'maps-map maps-' . $this->service->getName()
262
			],
263 19
			wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
264 19
			Html::element(
265 19
				'div',
266 19
				[ 'style' => 'display:none', 'class' => 'mapdata' ],
267 19
				FormatJson::encode( $params )
268
			)
269
		);
270
	}
271
272 19
	public static function getLayerDependencies( $service, $params ) {
273 19
		global $egMapsOLLayerDependencies, $egMapsOLAvailableLayers,
274 19
			   $egMapsLeafletLayerDependencies, $egMapsLeafletAvailableLayers,
275 19
			   $egMapsLeafletLayersApiKeys;
276
277 19
		$layerDependencies = [];
278
279 19
		if ( $service === 'leaflet' ) {
280 12
			$layerNames = $params['layers'];
281 12
			foreach ( $layerNames as $layerName ) {
282 12
				if ( array_key_exists( $layerName, $egMapsLeafletAvailableLayers )
283 12
					&& $egMapsLeafletAvailableLayers[$layerName]
284 12
					&& array_key_exists( $layerName, $egMapsLeafletLayersApiKeys )
285 12
					&& array_key_exists( $layerName, $egMapsLeafletLayerDependencies ) ) {
286
					$layerDependencies[] = '<script src="' . $egMapsLeafletLayerDependencies[$layerName] .
287 12
						$egMapsLeafletLayersApiKeys[$layerName] . '"></script>';
288
				}
289
			}
290
		} else {
291 7
			if ( $service === 'openlayers' ) {
292
				$layerNames = $params['layers'];
293
				foreach ( $layerNames as $layerName ) {
294
					if ( array_key_exists( $layerName, $egMapsOLAvailableLayers ) // The layer must be defined in php
295
						&& is_array( $egMapsOLAvailableLayers[$layerName] ) // The layer must be an array...
296
						&& count( $egMapsOLAvailableLayers[$layerName] ) > 1 // ...with a second element...
297
						&& array_key_exists(
298
							$egMapsOLAvailableLayers[$layerName][1],
299
							$egMapsOLLayerDependencies
300
						) ) { //...that is a dependency.
301
						$layerDependencies[] = $egMapsOLLayerDependencies[$egMapsOLAvailableLayers[$layerName][1]];
302
					}
303
				}
304
305
			}
306
		}
307
308 19
		return array_unique( $layerDependencies );
309
	}
310
311
}
312