1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Class for the 'layer' tag for describing a layer. |
5
|
|
|
* Most code of 'Maps_LayerPage.php' is reused for this since in 3.0 layer |
6
|
|
|
* pages can contain wikitext and a layer definition must be made through |
7
|
|
|
* this tag extension. |
8
|
|
|
* |
9
|
|
|
* @since 3.0 |
10
|
|
|
* |
11
|
|
|
* @file Maps_Layer.php |
12
|
|
|
* @ingroup Maps |
13
|
|
|
* |
14
|
|
|
* @author Jeroen De Dauw |
15
|
|
|
* @author Daniel Werner |
16
|
|
|
*/ |
17
|
|
|
class MapsLayerDefinition extends ParserHook { |
|
|
|
|
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* The final layer representation of this tag. |
21
|
|
|
* |
22
|
|
|
* @since 3.0 |
23
|
|
|
* |
24
|
|
|
* @var MapsLayer instance |
25
|
|
|
*/ |
26
|
|
|
protected $layer; |
27
|
|
|
|
28
|
|
|
public function __construct() { |
29
|
|
|
/* |
30
|
|
|
* make this a tag extension only to avoid weird parser function |
31
|
|
|
* syntax as we have it in 'display_point' from the beginning! |
32
|
|
|
*/ |
33
|
|
|
parent::__construct( true, false ); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
public static function initialize() { |
37
|
|
|
|
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Gets the name of the parser hook. |
42
|
|
|
* @see ParserHook::getName |
43
|
|
|
* |
44
|
|
|
* @since 3.0 |
45
|
|
|
* |
46
|
|
|
* @return string |
47
|
|
|
*/ |
48
|
|
|
protected function getName() { |
49
|
|
|
return 'layer'; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Returns an array containing the parameter info. |
54
|
|
|
* @see ParserHook::getParameterInfo |
55
|
|
|
* |
56
|
|
|
* @since 3.0 |
57
|
|
|
* |
58
|
|
|
* @return array |
59
|
|
|
*/ |
60
|
|
|
protected function getParameterInfo( $type ) { |
61
|
|
|
$params = []; |
62
|
|
|
|
63
|
|
|
$params['type'] = [ |
64
|
|
|
'default' => false, |
65
|
|
|
'manipulatedefault' => false, |
66
|
|
|
'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message |
67
|
|
|
]; |
68
|
|
|
|
69
|
|
|
$params['name'] = [ |
70
|
|
|
'default' => false, |
71
|
|
|
'manipulatedefault' => false, |
72
|
|
|
// TODO-customMaps: addCriteria( new CriterionIsNonNumeric ); |
73
|
|
|
'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message |
74
|
|
|
]; |
75
|
|
|
|
76
|
|
|
$params['definition'] = [ |
77
|
|
|
'default' => false, |
78
|
|
|
'manipulatedefault' => false, |
79
|
|
|
'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message |
80
|
|
|
'post-format' => function( $value ) { |
81
|
|
|
return MapsLayers::parseLayerParameters( $value, "\n", '=' ); |
82
|
|
|
} |
83
|
|
|
]; |
84
|
|
|
|
85
|
|
|
return $params; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Returns the list of default parameters. |
90
|
|
|
* @see ParserHook::getDefaultParameters |
91
|
|
|
* |
92
|
|
|
* @since 3.0 |
93
|
|
|
* |
94
|
|
|
* @return array |
95
|
|
|
*/ |
96
|
|
|
protected function getDefaultParameters( $type ) { |
97
|
|
|
return [ 'definition' ]; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Returns the parser function options. |
102
|
|
|
* @see ParserHook::getFunctionOptions |
103
|
|
|
* |
104
|
|
|
* @since 3.0 |
105
|
|
|
* |
106
|
|
|
* @return array |
107
|
|
|
*/ |
108
|
|
|
protected function getFunctionOptions() { |
109
|
|
|
return [ |
110
|
|
|
'noparse' => true, |
111
|
|
|
'isHTML' => true |
112
|
|
|
]; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @see ParserHook::getMessage() |
117
|
|
|
* |
118
|
|
|
* @since 3.0 |
119
|
|
|
*/ |
120
|
|
|
public function getMessage() { |
121
|
|
|
return 'maps-layerdefinition-description'; |
|
|
|
|
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Returns the MapsLayerGroup with all layers of the same page which have been |
126
|
|
|
* processed already. If the store is not attached to the parser object yet, |
127
|
|
|
* an empty MapsLayerGroup will be attached as store after calling the function. |
128
|
|
|
* |
129
|
|
|
* @since 3.0 |
130
|
|
|
* |
131
|
|
|
* @return MapsLayerGroup |
132
|
|
|
*/ |
133
|
|
|
protected function getLayerStore() { |
134
|
|
|
$parserOutput = $this->parser->getOutput(); |
135
|
|
|
|
136
|
|
|
// make sure layers store in current parsers ParserOutput is initialized: |
137
|
|
|
if( ! isset( $parserOutput->mExtMapsLayers ) ) { |
138
|
|
|
$parserOutput->mExtMapsLayers = new MapsLayerGroup(); |
139
|
|
|
} |
140
|
|
|
return $parserOutput->mExtMapsLayers; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* This will attach a user defined layer to the parser output of the parser which has |
145
|
|
|
* started the <layer> rendering. All added layers will be stored in the database |
146
|
|
|
* after page parsing. |
147
|
|
|
* $layer will only be stored in case it is a subclass of MapsLayer and its definition |
148
|
|
|
* is at least considered 'ok'. |
149
|
|
|
* |
150
|
|
|
* @since 3.0 |
151
|
|
|
* |
152
|
|
|
* @param type $layer |
153
|
|
|
* |
154
|
|
|
* @return boolean whether $layer has been added to the store |
155
|
|
|
*/ |
156
|
|
|
protected function addLayerToStore( $layer ) { |
157
|
|
|
|
158
|
|
|
$store = $this->getLayerStore(); |
159
|
|
|
|
160
|
|
|
// check whether $layer is a layer worthy to end up in the database: |
161
|
|
|
if( $layer === null || $layer === false |
162
|
|
|
|| ! is_subclass_of( $layer, 'MapsLayer' ) |
163
|
|
|
|| ! $layer->isOk() |
164
|
|
|
|| $store->getLayerByName( $layer->getName() ) !== null // layer of same name in store already |
165
|
|
|
|
166
|
|
|
) { |
167
|
|
|
return false; |
168
|
|
|
} |
169
|
|
|
// add layer to store: |
170
|
|
|
$overwritten = $store->addLayer( $layer ); |
171
|
|
|
|
172
|
|
|
if( $overwritten ) { |
|
|
|
|
173
|
|
|
/** @ToDo: Message that a layer was defined twice on that site */ |
174
|
|
|
} |
175
|
|
|
return true; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Renders and returns the output. |
180
|
|
|
* @see ParserHook::renderTag |
181
|
|
|
* |
182
|
|
|
* @since 3.0 |
183
|
|
|
* |
184
|
|
|
* @param array $parameters |
185
|
|
|
* |
186
|
|
|
* @return string |
187
|
|
|
*/ |
188
|
|
|
public function render( array $parameters ) { |
189
|
|
|
global $wgLang; |
190
|
|
|
|
191
|
|
|
// Check whether parser tag is used in the right namespace context, abort if not |
192
|
|
|
if( $this->parser->getTitle()->getNamespace() !== Maps_NS_LAYER ) { |
193
|
|
|
global $wgContLang; |
194
|
|
|
return $this->rawErrorbox( |
195
|
|
|
wfMessage( |
196
|
|
|
'maps-layerdef-wrong-namespace', |
197
|
|
|
$wgContLang->getNsText( Maps_NS_LAYER ) |
198
|
|
|
)->inContentLanguage()->text() |
199
|
|
|
); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
$type = $parameters['type']; |
203
|
|
|
|
204
|
|
|
if( $type === false ) { |
205
|
|
|
// no layer type specified |
206
|
|
|
|
207
|
|
|
$availableLayerTypes = MapsLayerTypes::getAvailableTypes(); |
208
|
|
|
|
209
|
|
|
$out = $this->rawErrorbox( |
210
|
|
|
wfMessage( |
211
|
|
|
'maps-error-no-layertype', |
212
|
|
|
$wgLang->listToText( $availableLayerTypes ), |
213
|
|
|
count( $availableLayerTypes ) |
214
|
|
|
)->inContentLanguage()->text() |
215
|
|
|
); |
216
|
|
|
} |
217
|
|
|
elseif( MapsLayerTypes::hasType( $type ) ) { |
218
|
|
|
// get layer name if any: |
219
|
|
|
$name = $parameters['name'] !== false ? $parameters['name'] : null; |
220
|
|
|
|
221
|
|
|
// make sure the layer has a label, if no user data, make something up: |
222
|
|
|
if( empty( $parameters['definition']['label'] ) ) { |
223
|
|
|
if( $name !== null ) { |
224
|
|
|
$labelSuffix = "- $name"; |
225
|
|
|
} else { |
226
|
|
|
// label for unnamed layer: |
227
|
|
|
$labelSuffix = '#' . ( count( $this->getLayerStore()->getLayers( MapsLayerGroup::LAYERS_NUMERIC ) ) + 1 ); |
228
|
|
|
} |
229
|
|
|
$parameters['definition']['label'] = $this->parser->getTitle()->getText() . ' ' . $labelSuffix; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
// new layer from user input (could still be invalid): |
233
|
|
|
$layer = MapsLayers::newLayerFromDefinition( $type, $parameters['definition'], $name ); |
234
|
|
|
|
235
|
|
|
$out = $this->renderLayerInfo( $layer ); |
236
|
|
|
} |
237
|
|
|
else { |
238
|
|
|
// specified layer type is non-existant! |
239
|
|
|
|
240
|
|
|
$availableLayerTypes = MapsLayerTypes::getAvailableTypes(); |
241
|
|
|
|
242
|
|
|
$out = $this->rawErrorbox( |
243
|
|
|
wfMessage( |
244
|
|
|
'maps-error-invalid-layertype', |
245
|
|
|
$this->validator->getParameter('type')->getOriginalValue(), |
|
|
|
|
246
|
|
|
$wgLang->listToText( $availableLayerTypes ), |
247
|
|
|
count( $availableLayerTypes ) |
248
|
|
|
)->inContentLanguage()->text() |
249
|
|
|
); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
// add the layer to the store after all info has been rendered: |
253
|
|
|
$this->addLayerToStore( $layer ); |
|
|
|
|
254
|
|
|
|
255
|
|
|
return $out; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Responsible for actual output on the layer page which gives an overview of the layer definition. |
260
|
|
|
* |
261
|
|
|
* @since 3.0 |
262
|
|
|
* |
263
|
|
|
* @param MapsLayer |
264
|
|
|
* |
265
|
|
|
* @return string |
266
|
|
|
*/ |
267
|
|
|
public function renderLayerInfo( MapsLayer $layer ) { |
268
|
|
|
global $wgLang; |
269
|
|
|
|
270
|
|
|
// appropriate layer header: |
271
|
|
|
if( $layer->getName() !== null ) { |
272
|
|
|
|
273
|
|
|
// if layer with same name is defined on same page already: |
274
|
|
|
if( $this->getLayerStore()->getLayerByName( $layer->getName() ) !== null ) { |
275
|
|
|
return |
276
|
|
|
$this->errorbox( |
277
|
|
|
wfMessage( |
278
|
|
|
'maps-layerdef-equal-layer-name', |
279
|
|
|
$layer->getName() |
280
|
|
|
)->inContentLanguage()->text() |
281
|
|
|
); |
282
|
|
|
} |
283
|
|
|
$outHeader = wfMessage( |
284
|
|
|
'maps-layer-of-type-and-name', |
285
|
|
|
$layer->getType(), |
286
|
|
|
$layer->getName() |
287
|
|
|
)->inContentLanguage()->text(); |
288
|
|
|
} |
289
|
|
|
else { |
290
|
|
|
$outHeader = wfMessage( |
291
|
|
|
'maps-layer-of-type', |
292
|
|
|
$layer->getType() |
293
|
|
|
)->inContentLanguage()->text(); |
294
|
|
|
} |
295
|
|
|
$outHeader = "<span class=\"mapslayerhead\">$outHeader</span>"; |
296
|
|
|
|
297
|
|
|
// info message about which services are supporting the layer(-type): |
298
|
|
|
$supportedServices = MapsLayerTypes::getServicesForType( $layer->getType() ); |
299
|
|
|
$outServices = '<span class="mapslayersupports">' . |
300
|
|
|
wfMessage( |
301
|
|
|
'maps-layer-type-supported-by', |
302
|
|
|
$wgLang->listToText( $supportedServices ), |
303
|
|
|
count( $supportedServices ) |
304
|
|
|
)->inContentLanguage()->escaped() . '</span>'; |
305
|
|
|
|
306
|
|
|
$outTable = $this->getLayerDefinitionTable( $layer ); |
307
|
|
|
|
308
|
|
|
return |
309
|
|
|
Html::rawElement( |
310
|
|
|
'div', |
311
|
|
|
[ 'class' => 'mapslayer' . ( $layer->isOk() ? '' : ' mapslayererror' ) ], |
312
|
|
|
$outHeader . $outServices . $outTable |
313
|
|
|
); |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* Displays the layer definition as a table. |
318
|
|
|
* |
319
|
|
|
* @since 3.0 |
320
|
|
|
* |
321
|
|
|
* @param MapsLayer $layer |
322
|
|
|
* |
323
|
|
|
* @return string |
324
|
|
|
*/ |
325
|
|
|
protected function getLayerDefinitionTable( MapsLayer $layer ) { |
326
|
|
|
$out = ''; |
327
|
|
|
$outWarning = ''; |
328
|
|
|
|
329
|
|
|
// check whether any error occurred during parameter validaton: |
330
|
|
|
if ( ! $layer->isValid() ) { |
331
|
|
|
$messages = $layer->getErrorMessages(); |
332
|
|
|
$warnings = ''; |
|
|
|
|
333
|
|
|
|
334
|
|
|
if( count( $messages ) === 1 ) { |
335
|
|
|
$warnings = htmlspecialchars( $messages[0] ); |
336
|
|
|
} else { |
337
|
|
|
$warnings = '<ul><li>' . implode( '</li><li>', array_map( 'htmlspecialchars', $messages ) ) . '</li></ul>'; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
$warnings = |
341
|
|
|
'<tr><td class="mapslayerpropname">' . |
342
|
|
|
wfMessage( |
343
|
|
|
'maps-layerdef-invalid' . ( $layer->isOk() ? '' : '-fatal' ), |
344
|
|
|
count( $messages ) |
345
|
|
|
)->inContentLanguage()->escaped() . |
346
|
|
|
"</td><td class=\"mapslayerpropval\">{$warnings}</td></tr>"; |
347
|
|
|
|
348
|
|
|
$outWarning .= Html::rawElement( |
349
|
|
|
'table', |
350
|
|
|
[ 'width' => '100%', 'class' => ( $layer->isOk() ? 'mapslayerwarntable' : 'mapslayererrortable' ) ], |
351
|
|
|
$warnings |
352
|
|
|
); |
353
|
|
|
|
354
|
|
|
if( ! $layer->isOk() ) { |
355
|
|
|
// fatal error occurred, don't print definition table since this would be quite empty since |
356
|
|
|
// parameter validation aborted after fatal error parameter! |
357
|
|
|
return $outWarning; |
358
|
|
|
} |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
global $wgOut; |
362
|
|
|
$wgOut->addModules( 'ext.maps.layers' ); |
363
|
|
|
|
364
|
|
|
$rows = []; |
365
|
|
|
|
366
|
|
|
// rows with layer definition: |
367
|
|
|
$properties = $layer->getPropertiesHtmlRepresentation( $this->parser ); |
368
|
|
|
|
369
|
|
|
foreach ( $properties as $property => $value ) { |
370
|
|
|
$rows[] = Html::rawElement( |
371
|
|
|
'tr', |
372
|
|
|
[], |
373
|
|
|
Html::element( |
374
|
|
|
'td', |
375
|
|
|
[ 'class' => 'mapslayerpropname' ], |
376
|
|
|
$property |
377
|
|
|
) . |
378
|
|
|
Html::rawElement( |
379
|
|
|
'td', |
380
|
|
|
[ 'class' => 'mapslayerpropval' ], |
381
|
|
|
$value |
382
|
|
|
) |
383
|
|
|
); |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
$out .= Html::rawElement( |
387
|
|
|
'table', |
388
|
|
|
[ 'width' => '100%', 'class' => 'mapslayertable' ], |
389
|
|
|
implode( "\n", $rows ) |
390
|
|
|
); |
391
|
|
|
|
392
|
|
|
return ( $out . $outWarning ); |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* wraps text inside an error box. |
397
|
|
|
* |
398
|
|
|
* @since 3.0 |
399
|
|
|
* |
400
|
|
|
* @param string $text text of the error, html-escaped. |
401
|
|
|
* |
402
|
|
|
* @return string |
403
|
|
|
*/ |
404
|
|
|
protected function errorbox( $text, $raw = true ) { |
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* FIXME: using 'errorbox' isn't the best idea since it has |
407
|
|
|
* some weird css definition, better would be introducing a |
408
|
|
|
* own class and puttin the whole definition into a nicer box. |
409
|
|
|
*/ |
410
|
|
|
return '<div class="errorbox" style="margin:0;">' . $text .'</div><div style="clear:both"></div>'; |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* wraps text inside an error box. |
415
|
|
|
* |
416
|
|
|
* @since 3.0 |
417
|
|
|
* |
418
|
|
|
* @param string $text text of the error, NOT html-escaped |
419
|
|
|
* |
420
|
|
|
* @return string |
421
|
|
|
*/ |
422
|
|
|
protected function rawErrorbox( $text ) { |
423
|
|
|
$text = htmlspecialchars( $text ); |
424
|
|
|
return $this->errorbox( $text ); |
425
|
|
|
} |
426
|
|
|
} |
427
|
|
|
|
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.