Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like BaseTemplate often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use BaseTemplate, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 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 = [] ) { |
||
| 508 | |||
| 509 | function makeSearchButton( $mode, $attrs = [] ) { |
||
| 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 ) { |
||
| 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 ) { |
||
| 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() { |
||
| 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() { |
||
| 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.