Completed
Push — master ( 88c9eb...e2ac37 )
by Jeroen De
03:05
created

DisplayMapRenderer::handleShapeData()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

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