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