|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* This program is free software; you can redistribute it and/or modify |
|
4
|
|
|
* it under the terms of the GNU General Public License as published by |
|
5
|
|
|
* the Free Software Foundation; either version 2 of the License, or |
|
6
|
|
|
* (at your option) any later version. |
|
7
|
|
|
* |
|
8
|
|
|
* This program is distributed in the hope that it will be useful, |
|
9
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11
|
|
|
* GNU General Public License for more details. |
|
12
|
|
|
* |
|
13
|
|
|
* You should have received a copy of the GNU General Public License along |
|
14
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc., |
|
15
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16
|
|
|
* http://www.gnu.org/copyleft/gpl.html |
|
17
|
|
|
* |
|
18
|
|
|
* @file |
|
19
|
|
|
*/ |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* New base template for a skin's template extended from QuickTemplate |
|
23
|
|
|
* this class features helper methods that provide common ways of interacting |
|
24
|
|
|
* with the data stored in the QuickTemplate |
|
25
|
|
|
*/ |
|
26
|
|
|
abstract class BaseTemplate extends QuickTemplate { |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* Get a Message object with its context set |
|
30
|
|
|
* |
|
31
|
|
|
* @param string $name Message name |
|
32
|
|
|
* @param ... $params Message params |
|
|
|
|
|
|
33
|
|
|
* @return Message |
|
34
|
|
|
*/ |
|
35
|
|
|
public function getMsg( $name /* ... */ ) { |
|
36
|
|
|
return call_user_func_array( [ $this->getSkin(), 'msg' ], func_get_args() ); |
|
37
|
|
|
} |
|
38
|
|
|
|
|
39
|
|
|
function msg( $str ) { |
|
40
|
|
|
echo $this->getMsg( $str )->escaped(); |
|
41
|
|
|
} |
|
42
|
|
|
|
|
43
|
|
|
function msgHtml( $str ) { |
|
44
|
|
|
echo $this->getMsg( $str )->text(); |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
function msgWiki( $str ) { |
|
48
|
|
|
echo $this->getMsg( $str )->parseAsBlock(); |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Create an array of common toolbox items from the data in the quicktemplate |
|
53
|
|
|
* stored by SkinTemplate. |
|
54
|
|
|
* The resulting array is built according to a format intended to be passed |
|
55
|
|
|
* through makeListItem to generate the html. |
|
56
|
|
|
* @return array |
|
57
|
|
|
*/ |
|
58
|
|
|
function getToolbox() { |
|
59
|
|
|
$toolbox = []; |
|
60
|
|
View Code Duplication |
if ( isset( $this->data['nav_urls']['whatlinkshere'] ) |
|
61
|
|
|
&& $this->data['nav_urls']['whatlinkshere'] |
|
62
|
|
|
) { |
|
63
|
|
|
$toolbox['whatlinkshere'] = $this->data['nav_urls']['whatlinkshere']; |
|
|
|
|
|
|
64
|
|
|
$toolbox['whatlinkshere']['id'] = 't-whatlinkshere'; |
|
65
|
|
|
} |
|
66
|
|
|
if ( isset( $this->data['nav_urls']['recentchangeslinked'] ) |
|
67
|
|
|
&& $this->data['nav_urls']['recentchangeslinked'] |
|
68
|
|
|
) { |
|
69
|
|
|
$toolbox['recentchangeslinked'] = $this->data['nav_urls']['recentchangeslinked']; |
|
70
|
|
|
$toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox'; |
|
71
|
|
|
$toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked'; |
|
72
|
|
|
$toolbox['recentchangeslinked']['rel'] = 'nofollow'; |
|
73
|
|
|
} |
|
74
|
|
|
if ( isset( $this->data['feeds'] ) && $this->data['feeds'] ) { |
|
75
|
|
|
$toolbox['feeds']['id'] = 'feedlinks'; |
|
76
|
|
|
$toolbox['feeds']['links'] = []; |
|
77
|
|
|
foreach ( $this->data['feeds'] as $key => $feed ) { |
|
78
|
|
|
$toolbox['feeds']['links'][$key] = $feed; |
|
79
|
|
|
$toolbox['feeds']['links'][$key]['id'] = "feed-$key"; |
|
80
|
|
|
$toolbox['feeds']['links'][$key]['rel'] = 'alternate'; |
|
81
|
|
|
$toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml"; |
|
82
|
|
|
$toolbox['feeds']['links'][$key]['class'] = 'feedlink'; |
|
83
|
|
|
} |
|
84
|
|
|
} |
|
85
|
|
|
foreach ( [ 'contributions', 'log', 'blockip', 'emailuser', |
|
86
|
|
|
'userrights', 'upload', 'specialpages' ] as $special |
|
87
|
|
|
) { |
|
88
|
|
|
if ( isset( $this->data['nav_urls'][$special] ) && $this->data['nav_urls'][$special] ) { |
|
89
|
|
|
$toolbox[$special] = $this->data['nav_urls'][$special]; |
|
90
|
|
|
$toolbox[$special]['id'] = "t-$special"; |
|
91
|
|
|
} |
|
92
|
|
|
} |
|
93
|
|
|
if ( isset( $this->data['nav_urls']['print'] ) && $this->data['nav_urls']['print'] ) { |
|
94
|
|
|
$toolbox['print'] = $this->data['nav_urls']['print']; |
|
95
|
|
|
$toolbox['print']['id'] = 't-print'; |
|
96
|
|
|
$toolbox['print']['rel'] = 'alternate'; |
|
97
|
|
|
$toolbox['print']['msg'] = 'printableversion'; |
|
98
|
|
|
} |
|
99
|
|
|
if ( isset( $this->data['nav_urls']['permalink'] ) && $this->data['nav_urls']['permalink'] ) { |
|
100
|
|
|
$toolbox['permalink'] = $this->data['nav_urls']['permalink']; |
|
101
|
|
|
if ( $toolbox['permalink']['href'] === '' ) { |
|
102
|
|
|
unset( $toolbox['permalink']['href'] ); |
|
103
|
|
|
$toolbox['ispermalink']['tooltiponly'] = true; |
|
104
|
|
|
$toolbox['ispermalink']['id'] = 't-ispermalink'; |
|
105
|
|
|
$toolbox['ispermalink']['msg'] = 'permalink'; |
|
106
|
|
|
} else { |
|
107
|
|
|
$toolbox['permalink']['id'] = 't-permalink'; |
|
108
|
|
|
} |
|
109
|
|
|
} |
|
110
|
|
View Code Duplication |
if ( isset( $this->data['nav_urls']['info'] ) && $this->data['nav_urls']['info'] ) { |
|
111
|
|
|
$toolbox['info'] = $this->data['nav_urls']['info']; |
|
112
|
|
|
$toolbox['info']['id'] = 't-info'; |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
Hooks::run( 'BaseTemplateToolbox', [ &$this, &$toolbox ] ); |
|
116
|
|
|
return $toolbox; |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
/** |
|
120
|
|
|
* Create an array of personal tools items from the data in the quicktemplate |
|
121
|
|
|
* stored by SkinTemplate. |
|
122
|
|
|
* The resulting array is built according to a format intended to be passed |
|
123
|
|
|
* through makeListItem to generate the html. |
|
124
|
|
|
* This is in reality the same list as already stored in personal_urls |
|
125
|
|
|
* however it is reformatted so that you can just pass the individual items |
|
126
|
|
|
* to makeListItem instead of hardcoding the element creation boilerplate. |
|
127
|
|
|
* @return array |
|
128
|
|
|
*/ |
|
129
|
|
|
function getPersonalTools() { |
|
130
|
|
|
$personal_tools = []; |
|
131
|
|
|
foreach ( $this->get( 'personal_urls' ) as $key => $plink ) { |
|
132
|
|
|
# The class on a personal_urls item is meant to go on the <a> instead |
|
133
|
|
|
# of the <li> so we have to use a single item "links" array instead |
|
134
|
|
|
# of using most of the personal_url's keys directly. |
|
135
|
|
|
$ptool = [ |
|
136
|
|
|
'links' => [ |
|
137
|
|
|
[ 'single-id' => "pt-$key" ], |
|
138
|
|
|
], |
|
139
|
|
|
'id' => "pt-$key", |
|
140
|
|
|
]; |
|
141
|
|
|
if ( isset( $plink['active'] ) ) { |
|
142
|
|
|
$ptool['active'] = $plink['active']; |
|
143
|
|
|
} |
|
144
|
|
|
foreach ( [ 'href', 'class', 'text', 'dir', 'data' ] as $k ) { |
|
145
|
|
|
if ( isset( $plink[$k] ) ) { |
|
146
|
|
|
$ptool['links'][0][$k] = $plink[$k]; |
|
147
|
|
|
} |
|
148
|
|
|
} |
|
149
|
|
|
$personal_tools[$key] = $ptool; |
|
150
|
|
|
} |
|
151
|
|
|
return $personal_tools; |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
|
|
function getSidebar( $options = [] ) { |
|
155
|
|
|
// Force the rendering of the following portals |
|
156
|
|
|
$sidebar = $this->data['sidebar']; |
|
157
|
|
|
if ( !isset( $sidebar['SEARCH'] ) ) { |
|
158
|
|
|
$sidebar['SEARCH'] = true; |
|
159
|
|
|
} |
|
160
|
|
|
if ( !isset( $sidebar['TOOLBOX'] ) ) { |
|
161
|
|
|
$sidebar['TOOLBOX'] = true; |
|
162
|
|
|
} |
|
163
|
|
|
if ( !isset( $sidebar['LANGUAGES'] ) ) { |
|
164
|
|
|
$sidebar['LANGUAGES'] = true; |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
if ( !isset( $options['search'] ) || $options['search'] !== true ) { |
|
168
|
|
|
unset( $sidebar['SEARCH'] ); |
|
169
|
|
|
} |
|
170
|
|
|
if ( isset( $options['toolbox'] ) && $options['toolbox'] === false ) { |
|
171
|
|
|
unset( $sidebar['TOOLBOX'] ); |
|
172
|
|
|
} |
|
173
|
|
|
if ( isset( $options['languages'] ) && $options['languages'] === false ) { |
|
174
|
|
|
unset( $sidebar['LANGUAGES'] ); |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
$boxes = []; |
|
178
|
|
|
foreach ( $sidebar as $boxName => $content ) { |
|
179
|
|
|
if ( $content === false ) { |
|
180
|
|
|
continue; |
|
181
|
|
|
} |
|
182
|
|
|
switch ( $boxName ) { |
|
183
|
|
|
case 'SEARCH': |
|
184
|
|
|
// Search is a special case, skins should custom implement this |
|
185
|
|
|
$boxes[$boxName] = [ |
|
186
|
|
|
'id' => 'p-search', |
|
187
|
|
|
'header' => $this->getMsg( 'search' )->text(), |
|
188
|
|
|
'generated' => false, |
|
189
|
|
|
'content' => true, |
|
190
|
|
|
]; |
|
191
|
|
|
break; |
|
192
|
|
|
case 'TOOLBOX': |
|
193
|
|
|
$msgObj = $this->getMsg( 'toolbox' ); |
|
194
|
|
|
$boxes[$boxName] = [ |
|
195
|
|
|
'id' => 'p-tb', |
|
196
|
|
|
'header' => $msgObj->exists() ? $msgObj->text() : 'toolbox', |
|
197
|
|
|
'generated' => false, |
|
198
|
|
|
'content' => $this->getToolbox(), |
|
199
|
|
|
]; |
|
200
|
|
|
break; |
|
201
|
|
|
case 'LANGUAGES': |
|
202
|
|
|
if ( $this->data['language_urls'] ) { |
|
203
|
|
|
$msgObj = $this->getMsg( 'otherlanguages' ); |
|
204
|
|
|
$boxes[$boxName] = [ |
|
205
|
|
|
'id' => 'p-lang', |
|
206
|
|
|
'header' => $msgObj->exists() ? $msgObj->text() : 'otherlanguages', |
|
207
|
|
|
'generated' => false, |
|
208
|
|
|
'content' => $this->data['language_urls'], |
|
209
|
|
|
]; |
|
210
|
|
|
} |
|
211
|
|
|
break; |
|
212
|
|
|
default: |
|
213
|
|
|
$msgObj = $this->getMsg( $boxName ); |
|
214
|
|
|
$boxes[$boxName] = [ |
|
215
|
|
|
'id' => "p-$boxName", |
|
216
|
|
|
'header' => $msgObj->exists() ? $msgObj->text() : $boxName, |
|
217
|
|
|
'generated' => true, |
|
218
|
|
|
'content' => $content, |
|
219
|
|
|
]; |
|
220
|
|
|
break; |
|
221
|
|
|
} |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
// HACK: Compatibility with extensions still using SkinTemplateToolboxEnd |
|
225
|
|
|
$hookContents = null; |
|
226
|
|
|
if ( isset( $boxes['TOOLBOX'] ) ) { |
|
227
|
|
|
ob_start(); |
|
228
|
|
|
// We pass an extra 'true' at the end so extensions using BaseTemplateToolbox |
|
229
|
|
|
// can abort and avoid outputting double toolbox links |
|
230
|
|
|
Hooks::run( 'SkinTemplateToolboxEnd', [ &$this, true ] ); |
|
231
|
|
|
$hookContents = ob_get_contents(); |
|
232
|
|
|
ob_end_clean(); |
|
233
|
|
|
if ( !trim( $hookContents ) ) { |
|
234
|
|
|
$hookContents = null; |
|
235
|
|
|
} |
|
236
|
|
|
} |
|
237
|
|
|
// END hack |
|
238
|
|
|
|
|
239
|
|
|
if ( isset( $options['htmlOnly'] ) && $options['htmlOnly'] === true ) { |
|
240
|
|
|
foreach ( $boxes as $boxName => $box ) { |
|
241
|
|
|
if ( is_array( $box['content'] ) ) { |
|
242
|
|
|
$content = '<ul>'; |
|
243
|
|
|
foreach ( $box['content'] as $key => $val ) { |
|
244
|
|
|
$content .= "\n " . $this->makeListItem( $key, $val ); |
|
245
|
|
|
} |
|
246
|
|
|
// HACK, shove the toolbox end onto the toolbox if we're rendering itself |
|
247
|
|
|
if ( $hookContents ) { |
|
|
|
|
|
|
248
|
|
|
$content .= "\n $hookContents"; |
|
249
|
|
|
} |
|
250
|
|
|
// END hack |
|
251
|
|
|
$content .= "\n</ul>\n"; |
|
252
|
|
|
$boxes[$boxName]['content'] = $content; |
|
253
|
|
|
} |
|
254
|
|
|
} |
|
255
|
|
|
} else { |
|
256
|
|
|
if ( $hookContents ) { |
|
|
|
|
|
|
257
|
|
|
$boxes['TOOLBOXEND'] = [ |
|
258
|
|
|
'id' => 'p-toolboxend', |
|
259
|
|
|
'header' => $boxes['TOOLBOX']['header'], |
|
260
|
|
|
'generated' => false, |
|
261
|
|
|
'content' => "<ul>{$hookContents}</ul>", |
|
262
|
|
|
]; |
|
263
|
|
|
// HACK: Make sure that TOOLBOXEND is sorted next to TOOLBOX |
|
264
|
|
|
$boxes2 = []; |
|
265
|
|
|
foreach ( $boxes as $key => $box ) { |
|
266
|
|
|
if ( $key === 'TOOLBOXEND' ) { |
|
267
|
|
|
continue; |
|
268
|
|
|
} |
|
269
|
|
|
$boxes2[$key] = $box; |
|
270
|
|
|
if ( $key === 'TOOLBOX' ) { |
|
271
|
|
|
$boxes2['TOOLBOXEND'] = $boxes['TOOLBOXEND']; |
|
272
|
|
|
} |
|
273
|
|
|
} |
|
274
|
|
|
$boxes = $boxes2; |
|
275
|
|
|
// END hack |
|
276
|
|
|
} |
|
277
|
|
|
} |
|
278
|
|
|
|
|
279
|
|
|
return $boxes; |
|
280
|
|
|
} |
|
281
|
|
|
|
|
282
|
|
|
/** |
|
283
|
|
|
* @param string $name |
|
284
|
|
|
*/ |
|
285
|
|
|
protected function renderAfterPortlet( $name ) { |
|
286
|
|
|
$content = ''; |
|
287
|
|
|
Hooks::run( 'BaseTemplateAfterPortlet', [ $this, $name, &$content ] ); |
|
288
|
|
|
|
|
289
|
|
|
if ( $content !== '' ) { |
|
290
|
|
|
echo "<div class='after-portlet after-portlet-$name'>$content</div>"; |
|
291
|
|
|
} |
|
292
|
|
|
} |
|
293
|
|
|
|
|
294
|
|
|
/** |
|
295
|
|
|
* Makes a link, usually used by makeListItem to generate a link for an item |
|
296
|
|
|
* in a list used in navigation lists, portlets, portals, sidebars, etc... |
|
297
|
|
|
* |
|
298
|
|
|
* @param string $key Usually a key from the list you are generating this |
|
299
|
|
|
* link from. |
|
300
|
|
|
* @param array $item Contains some of a specific set of keys. |
|
301
|
|
|
* |
|
302
|
|
|
* The text of the link will be generated either from the contents of the |
|
303
|
|
|
* "text" key in the $item array, if a "msg" key is present a message by |
|
304
|
|
|
* that name will be used, and if neither of those are set the $key will be |
|
305
|
|
|
* used as a message name. |
|
306
|
|
|
* |
|
307
|
|
|
* If a "href" key is not present makeLink will just output htmlescaped text. |
|
308
|
|
|
* The "href", "id", "class", "rel", and "type" keys are used as attributes |
|
309
|
|
|
* for the link if present. |
|
310
|
|
|
* |
|
311
|
|
|
* If an "id" or "single-id" (if you don't want the actual id to be output |
|
312
|
|
|
* on the link) is present it will be used to generate a tooltip and |
|
313
|
|
|
* accesskey for the link. |
|
314
|
|
|
* |
|
315
|
|
|
* The keys "context" and "primary" are ignored; these keys are used |
|
316
|
|
|
* internally by skins and are not supposed to be included in the HTML |
|
317
|
|
|
* output. |
|
318
|
|
|
* |
|
319
|
|
|
* If you don't want an accesskey, set $item['tooltiponly'] = true; |
|
320
|
|
|
* |
|
321
|
|
|
* If a "data" key is present, it must be an array, where the keys represent |
|
322
|
|
|
* the data-xxx properties with their provided values. For example, |
|
323
|
|
|
* $item['data'] = [ |
|
324
|
|
|
* 'foo' => 1, |
|
325
|
|
|
* 'bar' => 'baz', |
|
326
|
|
|
* ]; |
|
327
|
|
|
* will render as element properties: |
|
328
|
|
|
* data-foo='1' data-bar='baz' |
|
329
|
|
|
* |
|
330
|
|
|
* @param array $options Can be used to affect the output of a link. |
|
331
|
|
|
* Possible options are: |
|
332
|
|
|
* - 'text-wrapper' key to specify a list of elements to wrap the text of |
|
333
|
|
|
* a link in. This should be an array of arrays containing a 'tag' and |
|
334
|
|
|
* optionally an 'attributes' key. If you only have one element you don't |
|
335
|
|
|
* need to wrap it in another array. eg: To use <a><span>...</span></a> |
|
336
|
|
|
* in all links use [ 'text-wrapper' => [ 'tag' => 'span' ] ] |
|
337
|
|
|
* for your options. |
|
338
|
|
|
* - 'link-class' key can be used to specify additional classes to apply |
|
339
|
|
|
* to all links. |
|
340
|
|
|
* - 'link-fallback' can be used to specify a tag to use instead of "<a>" |
|
341
|
|
|
* if there is no link. eg: If you specify 'link-fallback' => 'span' than |
|
342
|
|
|
* any non-link will output a "<span>" instead of just text. |
|
343
|
|
|
* |
|
344
|
|
|
* @return string |
|
345
|
|
|
*/ |
|
346
|
|
|
function makeLink( $key, $item, $options = [] ) { |
|
347
|
|
|
if ( isset( $item['text'] ) ) { |
|
348
|
|
|
$text = $item['text']; |
|
349
|
|
|
} else { |
|
350
|
|
|
$text = $this->translator->translate( isset( $item['msg'] ) ? $item['msg'] : $key ); |
|
|
|
|
|
|
351
|
|
|
} |
|
352
|
|
|
|
|
353
|
|
|
$html = htmlspecialchars( $text ); |
|
354
|
|
|
|
|
355
|
|
|
if ( isset( $options['text-wrapper'] ) ) { |
|
356
|
|
|
$wrapper = $options['text-wrapper']; |
|
357
|
|
|
if ( isset( $wrapper['tag'] ) ) { |
|
358
|
|
|
$wrapper = [ $wrapper ]; |
|
359
|
|
|
} |
|
360
|
|
|
while ( count( $wrapper ) > 0 ) { |
|
361
|
|
|
$element = array_pop( $wrapper ); |
|
362
|
|
|
$html = Html::rawElement( $element['tag'], isset( $element['attributes'] ) |
|
363
|
|
|
? $element['attributes'] |
|
364
|
|
|
: null, $html ); |
|
365
|
|
|
} |
|
366
|
|
|
} |
|
367
|
|
|
|
|
368
|
|
|
if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) { |
|
369
|
|
|
$attrs = $item; |
|
370
|
|
|
foreach ( [ 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary', |
|
371
|
|
|
'tooltip-params' ] as $k ) { |
|
372
|
|
|
unset( $attrs[$k] ); |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
if ( isset( $attrs['data'] ) ) { |
|
376
|
|
|
foreach ( $attrs['data'] as $key => $value ) { |
|
377
|
|
|
$attrs[ 'data-' . $key ] = $value; |
|
378
|
|
|
} |
|
379
|
|
|
unset( $attrs[ 'data' ] ); |
|
380
|
|
|
} |
|
381
|
|
|
|
|
382
|
|
View Code Duplication |
if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) { |
|
383
|
|
|
$item['single-id'] = $item['id']; |
|
384
|
|
|
} |
|
385
|
|
|
|
|
386
|
|
|
$tooltipParams = []; |
|
387
|
|
|
if ( isset( $item['tooltip-params'] ) ) { |
|
388
|
|
|
$tooltipParams = $item['tooltip-params']; |
|
389
|
|
|
} |
|
390
|
|
|
|
|
391
|
|
|
if ( isset( $item['single-id'] ) ) { |
|
392
|
|
|
if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) { |
|
393
|
|
|
$title = Linker::titleAttrib( $item['single-id'], null, $tooltipParams ); |
|
394
|
|
|
if ( $title !== false ) { |
|
395
|
|
|
$attrs['title'] = $title; |
|
396
|
|
|
} |
|
397
|
|
|
} else { |
|
398
|
|
|
$tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'], $tooltipParams ); |
|
399
|
|
|
if ( isset( $tip['title'] ) && $tip['title'] !== false ) { |
|
400
|
|
|
$attrs['title'] = $tip['title']; |
|
401
|
|
|
} |
|
402
|
|
|
if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) { |
|
403
|
|
|
$attrs['accesskey'] = $tip['accesskey']; |
|
404
|
|
|
} |
|
405
|
|
|
} |
|
406
|
|
|
} |
|
407
|
|
View Code Duplication |
if ( isset( $options['link-class'] ) ) { |
|
408
|
|
|
if ( isset( $attrs['class'] ) ) { |
|
409
|
|
|
$attrs['class'] .= " {$options['link-class']}"; |
|
410
|
|
|
} else { |
|
411
|
|
|
$attrs['class'] = $options['link-class']; |
|
412
|
|
|
} |
|
413
|
|
|
} |
|
414
|
|
|
$html = Html::rawElement( isset( $attrs['href'] ) |
|
415
|
|
|
? 'a' |
|
416
|
|
|
: $options['link-fallback'], $attrs, $html ); |
|
417
|
|
|
} |
|
418
|
|
|
|
|
419
|
|
|
return $html; |
|
420
|
|
|
} |
|
421
|
|
|
|
|
422
|
|
|
/** |
|
423
|
|
|
* Generates a list item for a navigation, portlet, portal, sidebar... list |
|
424
|
|
|
* |
|
425
|
|
|
* @param string $key Usually a key from the list you are generating this link from. |
|
426
|
|
|
* @param array $item Array of list item data containing some of a specific set of keys. |
|
427
|
|
|
* The "id", "class" and "itemtitle" keys will be used as attributes for the list item, |
|
428
|
|
|
* if "active" contains a value of true a "active" class will also be appended to class. |
|
429
|
|
|
* |
|
430
|
|
|
* @param array $options |
|
431
|
|
|
* |
|
432
|
|
|
* If you want something other than a "<li>" you can pass a tag name such as |
|
433
|
|
|
* "tag" => "span" in the $options array to change the tag used. |
|
434
|
|
|
* link/content data for the list item may come in one of two forms |
|
435
|
|
|
* A "links" key may be used, in which case it should contain an array with |
|
436
|
|
|
* a list of links to include inside the list item, see makeLink for the |
|
437
|
|
|
* format of individual links array items. |
|
438
|
|
|
* |
|
439
|
|
|
* Otherwise the relevant keys from the list item $item array will be passed |
|
440
|
|
|
* to makeLink instead. Note however that "id" and "class" are used by the |
|
441
|
|
|
* list item directly so they will not be passed to makeLink |
|
442
|
|
|
* (however the link will still support a tooltip and accesskey from it) |
|
443
|
|
|
* If you need an id or class on a single link you should include a "links" |
|
444
|
|
|
* array with just one link item inside of it. You can also set "link-class" in |
|
445
|
|
|
* $item to set a class on the link itself. If you want to add a title |
|
446
|
|
|
* to the list item itself, you can set "itemtitle" to the value. |
|
447
|
|
|
* $options is also passed on to makeLink calls |
|
448
|
|
|
* |
|
449
|
|
|
* @return string |
|
450
|
|
|
*/ |
|
451
|
|
|
function makeListItem( $key, $item, $options = [] ) { |
|
452
|
|
|
if ( isset( $item['links'] ) ) { |
|
453
|
|
|
$links = []; |
|
454
|
|
|
foreach ( $item['links'] as $linkKey => $link ) { |
|
455
|
|
|
$links[] = $this->makeLink( $linkKey, $link, $options ); |
|
456
|
|
|
} |
|
457
|
|
|
$html = implode( ' ', $links ); |
|
458
|
|
|
} else { |
|
459
|
|
|
$link = $item; |
|
460
|
|
|
// These keys are used by makeListItem and shouldn't be passed on to the link |
|
461
|
|
|
foreach ( [ 'id', 'class', 'active', 'tag', 'itemtitle' ] as $k ) { |
|
462
|
|
|
unset( $link[$k] ); |
|
463
|
|
|
} |
|
464
|
|
View Code Duplication |
if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) { |
|
465
|
|
|
// The id goes on the <li> not on the <a> for single links |
|
466
|
|
|
// but makeSidebarLink still needs to know what id to use when |
|
467
|
|
|
// generating tooltips and accesskeys. |
|
468
|
|
|
$link['single-id'] = $item['id']; |
|
469
|
|
|
} |
|
470
|
|
|
if ( isset( $link['link-class'] ) ) { |
|
471
|
|
|
// link-class should be set on the <a> itself, |
|
472
|
|
|
// so pass it in as 'class' |
|
473
|
|
|
$link['class'] = $link['link-class']; |
|
474
|
|
|
unset( $link['link-class'] ); |
|
475
|
|
|
} |
|
476
|
|
|
$html = $this->makeLink( $key, $link, $options ); |
|
477
|
|
|
} |
|
478
|
|
|
|
|
479
|
|
|
$attrs = []; |
|
480
|
|
|
foreach ( [ 'id', 'class' ] as $attr ) { |
|
481
|
|
|
if ( isset( $item[$attr] ) ) { |
|
482
|
|
|
$attrs[$attr] = $item[$attr]; |
|
483
|
|
|
} |
|
484
|
|
|
} |
|
485
|
|
|
if ( isset( $item['active'] ) && $item['active'] ) { |
|
486
|
|
|
if ( !isset( $attrs['class'] ) ) { |
|
487
|
|
|
$attrs['class'] = ''; |
|
488
|
|
|
} |
|
489
|
|
|
$attrs['class'] .= ' active'; |
|
490
|
|
|
$attrs['class'] = trim( $attrs['class'] ); |
|
491
|
|
|
} |
|
492
|
|
|
if ( isset( $item['itemtitle'] ) ) { |
|
493
|
|
|
$attrs['title'] = $item['itemtitle']; |
|
494
|
|
|
} |
|
495
|
|
|
return Html::rawElement( isset( $options['tag'] ) ? $options['tag'] : 'li', $attrs, $html ); |
|
496
|
|
|
} |
|
497
|
|
|
|
|
498
|
|
|
function makeSearchInput( $attrs = [] ) { |
|
499
|
|
|
$realAttrs = [ |
|
500
|
|
|
'type' => 'search', |
|
501
|
|
|
'name' => 'search', |
|
502
|
|
|
'placeholder' => wfMessage( 'searchsuggest-search' )->text(), |
|
503
|
|
|
'value' => $this->get( 'search', '' ), |
|
504
|
|
|
]; |
|
505
|
|
|
$realAttrs = array_merge( $realAttrs, Linker::tooltipAndAccesskeyAttribs( 'search' ), $attrs ); |
|
506
|
|
|
return Html::element( 'input', $realAttrs ); |
|
507
|
|
|
} |
|
508
|
|
|
|
|
509
|
|
|
function makeSearchButton( $mode, $attrs = [] ) { |
|
510
|
|
|
switch ( $mode ) { |
|
511
|
|
|
case 'go': |
|
512
|
|
|
case 'fulltext': |
|
513
|
|
|
$realAttrs = [ |
|
514
|
|
|
'type' => 'submit', |
|
515
|
|
|
'name' => $mode, |
|
516
|
|
|
'value' => $this->translator->translate( |
|
517
|
|
|
$mode == 'go' ? 'searcharticle' : 'searchbutton' ), |
|
518
|
|
|
]; |
|
519
|
|
|
$realAttrs = array_merge( |
|
520
|
|
|
$realAttrs, |
|
521
|
|
|
Linker::tooltipAndAccesskeyAttribs( "search-$mode" ), |
|
522
|
|
|
$attrs |
|
523
|
|
|
); |
|
524
|
|
|
return Html::element( 'input', $realAttrs ); |
|
525
|
|
|
case 'image': |
|
526
|
|
|
$buttonAttrs = [ |
|
527
|
|
|
'type' => 'submit', |
|
528
|
|
|
'name' => 'button', |
|
529
|
|
|
]; |
|
530
|
|
|
$buttonAttrs = array_merge( |
|
531
|
|
|
$buttonAttrs, |
|
532
|
|
|
Linker::tooltipAndAccesskeyAttribs( 'search-fulltext' ), |
|
533
|
|
|
$attrs |
|
534
|
|
|
); |
|
535
|
|
|
unset( $buttonAttrs['src'] ); |
|
536
|
|
|
unset( $buttonAttrs['alt'] ); |
|
537
|
|
|
unset( $buttonAttrs['width'] ); |
|
538
|
|
|
unset( $buttonAttrs['height'] ); |
|
539
|
|
|
$imgAttrs = [ |
|
540
|
|
|
'src' => $attrs['src'], |
|
541
|
|
|
'alt' => isset( $attrs['alt'] ) |
|
542
|
|
|
? $attrs['alt'] |
|
543
|
|
|
: $this->translator->translate( 'searchbutton' ), |
|
544
|
|
|
'width' => isset( $attrs['width'] ) ? $attrs['width'] : null, |
|
545
|
|
|
'height' => isset( $attrs['height'] ) ? $attrs['height'] : null, |
|
546
|
|
|
]; |
|
547
|
|
|
return Html::rawElement( 'button', $buttonAttrs, Html::element( 'img', $imgAttrs ) ); |
|
548
|
|
|
default: |
|
549
|
|
|
throw new MWException( 'Unknown mode passed to BaseTemplate::makeSearchButton' ); |
|
550
|
|
|
} |
|
551
|
|
|
} |
|
552
|
|
|
|
|
553
|
|
|
/** |
|
554
|
|
|
* Returns an array of footerlinks trimmed down to only those footer links that |
|
555
|
|
|
* are valid. |
|
556
|
|
|
* If you pass "flat" as an option then the returned array will be a flat array |
|
557
|
|
|
* of footer icons instead of a key/value array of footerlinks arrays broken |
|
558
|
|
|
* up into categories. |
|
559
|
|
|
* @param string $option |
|
560
|
|
|
* @return array|mixed |
|
561
|
|
|
*/ |
|
562
|
|
|
function getFooterLinks( $option = null ) { |
|
563
|
|
|
$footerlinks = $this->get( 'footerlinks' ); |
|
564
|
|
|
|
|
565
|
|
|
// Reduce footer links down to only those which are being used |
|
566
|
|
|
$validFooterLinks = []; |
|
567
|
|
|
foreach ( $footerlinks as $category => $links ) { |
|
568
|
|
|
$validFooterLinks[$category] = []; |
|
569
|
|
|
foreach ( $links as $link ) { |
|
570
|
|
|
if ( isset( $this->data[$link] ) && $this->data[$link] ) { |
|
571
|
|
|
$validFooterLinks[$category][] = $link; |
|
572
|
|
|
} |
|
573
|
|
|
} |
|
574
|
|
|
if ( count( $validFooterLinks[$category] ) <= 0 ) { |
|
575
|
|
|
unset( $validFooterLinks[$category] ); |
|
576
|
|
|
} |
|
577
|
|
|
} |
|
578
|
|
|
|
|
579
|
|
|
if ( $option == 'flat' ) { |
|
580
|
|
|
// fold footerlinks into a single array using a bit of trickery |
|
581
|
|
|
$validFooterLinks = call_user_func_array( |
|
582
|
|
|
'array_merge', |
|
583
|
|
|
array_values( $validFooterLinks ) |
|
584
|
|
|
); |
|
585
|
|
|
} |
|
586
|
|
|
|
|
587
|
|
|
return $validFooterLinks; |
|
588
|
|
|
} |
|
589
|
|
|
|
|
590
|
|
|
/** |
|
591
|
|
|
* Returns an array of footer icons filtered down by options relevant to how |
|
592
|
|
|
* the skin wishes to display them. |
|
593
|
|
|
* If you pass "icononly" as the option all footer icons which do not have an |
|
594
|
|
|
* image icon set will be filtered out. |
|
595
|
|
|
* If you pass "nocopyright" then MediaWiki's copyright icon will not be included |
|
596
|
|
|
* in the list of footer icons. This is mostly useful for skins which only |
|
597
|
|
|
* display the text from footericons instead of the images and don't want a |
|
598
|
|
|
* duplicate copyright statement because footerlinks already rendered one. |
|
599
|
|
|
* @param string $option |
|
600
|
|
|
* @return array |
|
601
|
|
|
*/ |
|
602
|
|
|
function getFooterIcons( $option = null ) { |
|
603
|
|
|
// Generate additional footer icons |
|
604
|
|
|
$footericons = $this->get( 'footericons' ); |
|
605
|
|
|
|
|
606
|
|
|
if ( $option == 'icononly' ) { |
|
607
|
|
|
// Unset any icons which don't have an image |
|
608
|
|
|
foreach ( $footericons as &$footerIconsBlock ) { |
|
609
|
|
|
foreach ( $footerIconsBlock as $footerIconKey => $footerIcon ) { |
|
610
|
|
|
if ( !is_string( $footerIcon ) && !isset( $footerIcon['src'] ) ) { |
|
611
|
|
|
unset( $footerIconsBlock[$footerIconKey] ); |
|
612
|
|
|
} |
|
613
|
|
|
} |
|
614
|
|
|
} |
|
615
|
|
|
// Redo removal of any empty blocks |
|
616
|
|
|
foreach ( $footericons as $footerIconsKey => &$footerIconsBlock ) { |
|
617
|
|
|
if ( count( $footerIconsBlock ) <= 0 ) { |
|
618
|
|
|
unset( $footericons[$footerIconsKey] ); |
|
619
|
|
|
} |
|
620
|
|
|
} |
|
621
|
|
|
} elseif ( $option == 'nocopyright' ) { |
|
622
|
|
|
unset( $footericons['copyright']['copyright'] ); |
|
623
|
|
|
if ( count( $footericons['copyright'] ) <= 0 ) { |
|
624
|
|
|
unset( $footericons['copyright'] ); |
|
625
|
|
|
} |
|
626
|
|
|
} |
|
627
|
|
|
|
|
628
|
|
|
return $footericons; |
|
629
|
|
|
} |
|
630
|
|
|
|
|
631
|
|
|
/** |
|
632
|
|
|
* Get the suggested HTML for page status indicators: icons (or short text snippets) usually |
|
633
|
|
|
* displayed in the top-right corner of the page, outside of the main content. |
|
634
|
|
|
* |
|
635
|
|
|
* Your skin may implement this differently, for example by handling some indicator names |
|
636
|
|
|
* specially with a different UI. However, it is recommended to use a `<div class="mw-indicator" |
|
637
|
|
|
* id="mw-indicator-<id>" />` as a wrapper element for each indicator, for better compatibility |
|
638
|
|
|
* with extensions and user scripts. |
|
639
|
|
|
* |
|
640
|
|
|
* The raw data is available in `$this->data['indicators']` as an associative array (keys: |
|
641
|
|
|
* identifiers, values: contents) internally ordered by keys. |
|
642
|
|
|
* |
|
643
|
|
|
* @return string HTML |
|
644
|
|
|
* @since 1.25 |
|
645
|
|
|
*/ |
|
646
|
|
|
public function getIndicators() { |
|
647
|
|
|
$out = "<div class=\"mw-indicators\">\n"; |
|
648
|
|
|
foreach ( $this->data['indicators'] as $id => $content ) { |
|
649
|
|
|
$out .= Html::rawElement( |
|
650
|
|
|
'div', |
|
651
|
|
|
[ |
|
652
|
|
|
'id' => Sanitizer::escapeId( "mw-indicator-$id" ), |
|
653
|
|
|
'class' => 'mw-indicator', |
|
654
|
|
|
], |
|
655
|
|
|
$content |
|
656
|
|
|
) . "\n"; |
|
657
|
|
|
} |
|
658
|
|
|
$out .= "</div>\n"; |
|
659
|
|
|
return $out; |
|
660
|
|
|
} |
|
661
|
|
|
|
|
662
|
|
|
/** |
|
663
|
|
|
* Output the basic end-page trail including bottomscripts, reporttime, and |
|
664
|
|
|
* debug stuff. This should be called right before outputting the closing |
|
665
|
|
|
* body and html tags. |
|
666
|
|
|
*/ |
|
667
|
|
|
function printTrail() { |
|
668
|
|
|
?> |
|
669
|
|
|
<?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() ); ?> |
|
670
|
|
|
<?php $this->html( 'bottomscripts' ); /* JS call to runBodyOnloadHook */ ?> |
|
671
|
|
|
<?php $this->html( 'reporttime' ) ?> |
|
672
|
|
|
<?php |
|
673
|
|
|
} |
|
674
|
|
|
} |
|
675
|
|
|
|
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.