Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SkinComponentMenu
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 4
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 msg
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMenuLabel
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getTemplateData
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 1
132
1<?php
2
3namespace MediaWiki\Skin;
4
5use MediaWiki\Html\Html;
6use MediaWiki\Linker\Linker;
7use MediaWiki\Message\Message;
8use MediaWiki\Parser\Sanitizer;
9use MessageLocalizer;
10
11/**
12 * @internal for use inside Skin and SkinTemplate classes only
13 * @unstable
14 */
15class SkinComponentMenu implements SkinComponent {
16
17    /** @var string */
18    private $name;
19
20    /** @var array */
21    private $items;
22
23    /** @var MessageLocalizer */
24    private $localizer;
25
26    /** @var string */
27    private $content;
28
29    /** @var array */
30    private $linkOptions;
31
32    /** @var string */
33    private $htmlAfterContent;
34
35    /** @var string */
36    private $htmlBeforeContent;
37
38    /**
39     * @param string $name
40     * @param array $items
41     * @param MessageLocalizer $localizer
42     * @param string $content
43     * @param array $linkOptions
44     * @param string $htmlAfterContent
45     * @param string $htmlBeforeContent
46     */
47    public function __construct(
48        string $name,
49        array $items,
50        MessageLocalizer $localizer,
51        string $content = '',
52        array $linkOptions = [],
53        string $htmlAfterContent = '',
54        string $htmlBeforeContent = ''
55    ) {
56        $this->name = $name;
57        $this->items = $items;
58        $this->localizer = $localizer;
59        $this->content = $content;
60        $this->linkOptions = $linkOptions;
61        $this->htmlAfterContent = $htmlAfterContent;
62        $this->htmlBeforeContent = $htmlBeforeContent;
63    }
64
65    private function msg( string $key ): Message {
66        return $this->localizer->msg( $key );
67    }
68
69    /**
70     * @param string $name of the menu e.g. p-personal the name is personal.
71     *
72     * @return string that is human-readable corresponding to the menu.
73     */
74    private function getMenuLabel( $name ) {
75        // For historic reasons for some menu items, there is no language key corresponding
76        // with its menu key.
77        $mappings = [
78            'tb' => 'toolbox',
79            'personal' => 'personaltools',
80            'lang' => 'otherlanguages',
81        ];
82        $msgObj = $this->msg( $mappings[ $name ] ?? $name );
83        // If no message exists fallback to plain text (T252727)
84        return $msgObj->exists() ? $msgObj->text() : $name;
85    }
86
87    /**
88     * @inheritDoc
89     */
90    public function getTemplateData(): array {
91        $name = $this->name;
92        // Monobook and Vector historically render this portal as an element with ID p-cactions.
93        // To ensure compatibility with gadgets, it is renamed accordingly.
94        // @todo Port p-#cactions to #p-actions and drop these conditionals.
95        if ( $name === 'actions' ) {
96            $name = 'cactions';
97        }
98
99        // The new personal tools without the notifications is user-menu.
100        // A lot of user code and gadgets relies on it being named personal.
101        // This allows it to function as a drop-in replacement.
102        if ( $name === 'user-menu' ) {
103            $name = 'personal';
104        }
105
106        if ( strpos( $name, 'footer' ) === 0 ) {
107            // Retain footer IDs.
108            $id = $name;
109        } else {
110            $id = Sanitizer::escapeIdForAttribute( "p-$name" );
111        }
112
113        $isEmptyContent = !$this->content;
114        $isEmptyAfterContent = !$this->htmlAfterContent;
115        $isEmptyBeforeContent = !$this->htmlBeforeContent;
116        $isEmptyItems = count( $this->items ) === 0;
117        $isEmptyPortlet = ( $isEmptyContent && $isEmptyAfterContent && $isEmptyBeforeContent && $isEmptyItems );
118        $data = [
119            'id' => $id,
120            // Any changes to these classes should be synced with resources/src/mediawiki.util/util.js
121            'class' => 'mw-portlet ' . Sanitizer::escapeClass( "mw-portlet-$name" ),
122            'html-tooltip' => Linker::tooltip( $id ),
123            'html-items' => '',
124            // Will be populated by SkinAfterPortlet hook.
125            'html-after-portal' => '',
126            'html-before-portal' => '',
127            'is-empty' => $isEmptyPortlet,
128        ];
129
130        // for output. In production this currently supports the Wikibase 'edit' link.
131        if ( !$isEmptyAfterContent ) {
132            $data['html-after-portal'] = Html::rawElement(
133                'div',
134                [
135                    'class' => [
136                        'after-portlet',
137                        Sanitizer::escapeClass( "after-portlet-$name" ),
138                    ],
139                ],
140                $this->htmlAfterContent
141            );
142        }
143
144        if ( !$isEmptyBeforeContent ) {
145            $data['html-before-portal'] = Html::rawElement(
146                'div',
147                [
148                    'class' => [
149                        'before-portlet',
150                        Sanitizer::escapeClass( "before-portlet-$name" ),
151                    ],
152                ],
153                $this->htmlBeforeContent
154            );
155        }
156
157        $html = '';
158        $arrayItems = [];
159        foreach ( $this->items as $key => $item ) {
160            $item = new SkinComponentListItem( $key, $item, $this->localizer, [], $this->linkOptions );
161            $itemData = $item->getTemplateData();
162            $html .= $itemData['html-item'];
163            $arrayItems[] = $itemData;
164        }
165        $data['html-items'] = $html;
166        $data['array-items'] = $arrayItems;
167
168        $data['label'] = $this->getMenuLabel( $name );
169        $data['class'] .= $isEmptyPortlet ? ' emptyPortlet' : '';
170        return $data;
171    }
172}