Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.59% |
70 / 74 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
SkinComponentListItem | |
94.59% |
70 / 74 |
|
75.00% |
3 / 4 |
19.06 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
getMessageLocalizer | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
makeListItem | |
93.10% |
54 / 58 |
|
0.00% |
0 / 1 |
16.08 | |||
getTemplateData | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 |
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 | |
19 | namespace MediaWiki\Skin; |
20 | |
21 | use MediaWiki\Html\Html; |
22 | use MessageLocalizer; |
23 | |
24 | class SkinComponentListItem implements SkinComponent { |
25 | /** @var string */ |
26 | private $key; |
27 | /** @var array */ |
28 | private $item; |
29 | /** @var MessageLocalizer */ |
30 | private $localizer; |
31 | /** @var array */ |
32 | private $options; |
33 | /** @var array */ |
34 | private $defaultLinkOptions; |
35 | |
36 | /** |
37 | * @param string $key |
38 | * @param array $item |
39 | * @param MessageLocalizer $localizer |
40 | * @param array $options |
41 | * @param array $defaultLinkOptions |
42 | */ |
43 | public function __construct( |
44 | string $key, |
45 | array $item, |
46 | MessageLocalizer $localizer, |
47 | array $options = [], |
48 | array $defaultLinkOptions = [] |
49 | ) { |
50 | $this->key = $key; |
51 | $this->item = $item; |
52 | $this->localizer = $localizer; |
53 | $this->options = $options; |
54 | $this->defaultLinkOptions = $defaultLinkOptions; |
55 | } |
56 | |
57 | private function getMessageLocalizer(): MessageLocalizer { |
58 | return $this->localizer; |
59 | } |
60 | |
61 | /** |
62 | * Generates a list item for a navigation, portlet, portal, sidebar... list |
63 | * |
64 | * @param string $key Usually a key from the list you are generating this link from. |
65 | * @param array $item Array of list item data containing some of a specific set of keys. |
66 | * The "id", "class" and "itemtitle" keys will be used as attributes for the list item, |
67 | * if "active" contains a value of true a "active" class will also be appended to class. |
68 | * The "class" key currently accepts both a string and an array of classes, but this will be |
69 | * changed to only accept an array in the future. |
70 | * @phan-param array{id?:string,class?:string|string[],itemtitle?:string,active?:bool} $item |
71 | * |
72 | * @param array $options |
73 | * @phan-param array{tag?:string} $options |
74 | * |
75 | * If you want something other than a "<li>" you can pass a tag name such as |
76 | * "tag" => "span" in the $options array to change the tag used. |
77 | * link/content data for the list item may come in one of two forms |
78 | * A "links" key may be used, in which case it should contain an array with |
79 | * a list of links to include inside the list item, see makeLink for the |
80 | * format of individual links array items. |
81 | * |
82 | * Otherwise the relevant keys from the list item $item array will be passed |
83 | * to makeLink instead. Note however that "id" and "class" are used by the |
84 | * list item directly so they will not be passed to makeLink |
85 | * (however the link will still support a tooltip and accesskey from it) |
86 | * If you need an id or class on a single link you should include a "links" |
87 | * array with just one link item inside of it. You can also set "link-class" in |
88 | * $item to set a class on the link itself. If you want to add a title |
89 | * to the list item itself, you can set "itemtitle" to the value. |
90 | * $options is also passed on to makeLink calls |
91 | * |
92 | * @param array $linkOptions Can be used to affect the output of a link. |
93 | * Possible options are: |
94 | * - 'text-wrapper' key to specify a list of elements to wrap the text of |
95 | * a link in. This should be an array of arrays containing a 'tag' and |
96 | * optionally an 'attributes' key. If you only have one element you don't |
97 | * need to wrap it in another array. eg: To use <a><span>...</span></a> |
98 | * in all links use [ 'text-wrapper' => [ 'tag' => 'span' ] ] |
99 | * for your options. |
100 | * - 'link-class' key can be used to specify additional classes to apply |
101 | * to all links. |
102 | * - 'link-fallback' can be used to specify a tag to use instead of "<a>" |
103 | * if there is no link. eg: If you specify 'link-fallback' => 'span' than |
104 | * any non-link will output a "<span>" instead of just text. |
105 | * |
106 | * @return array List item data: |
107 | * - tag: String HTML tag name for the list item |
108 | * - attrs: Array of attributes for the list item |
109 | * - html: String HTML for the list item |
110 | * - array-links: Array of link template data |
111 | * @since 1.35 |
112 | */ |
113 | private function makeListItem( |
114 | string $key, |
115 | array $item, |
116 | array $options = [], |
117 | array $linkOptions = [] |
118 | ) { |
119 | // In case this is still set from SkinTemplate, we don't want it to appear in |
120 | // the HTML output (normally removed in SkinTemplate::buildContentActionUrls()) |
121 | unset( $item['redundant'] ); |
122 | $iconData = [ |
123 | 'icon' => $item['icon'] ?? null, |
124 | ]; |
125 | $linksArray = []; |
126 | if ( isset( $this->item['links'] ) ) { |
127 | $links = []; |
128 | /* @var array $link */ |
129 | foreach ( $this->item['links'] as $link ) { |
130 | // Note: links will have identical label unless 'msg' is set on $link |
131 | $linkComponent = new SkinComponentLink( |
132 | $key, |
133 | $link + $iconData, |
134 | $this->getMessageLocalizer(), |
135 | $options + $linkOptions |
136 | ); |
137 | $linkTemplateData = $linkComponent->getTemplateData(); |
138 | $links[] = $linkTemplateData['html']; |
139 | unset( $linkTemplateData['html'] ); |
140 | if ( $linkTemplateData ) { |
141 | $linksArray[] = $linkTemplateData; |
142 | } |
143 | } |
144 | $html = implode( ' ', $links ); |
145 | } else { |
146 | $link = $item; |
147 | // These keys are used by makeListItem and shouldn't be passed on to the link |
148 | foreach ( [ 'id', 'class', 'active', 'tag', 'itemtitle' ] as $k ) { |
149 | unset( $link[$k] ); |
150 | } |
151 | if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) { |
152 | // The id goes on the <li> not on the <a> for single links |
153 | // but makeSidebarLink still needs to know what id to use when |
154 | // generating tooltips and accesskeys. |
155 | $link['single-id'] = $item['id']; |
156 | } |
157 | if ( isset( $link['link-class'] ) ) { |
158 | // link-class should be set on the <a> itself, |
159 | // so pass it in as 'class' |
160 | $link['class'] = $link['link-class']; |
161 | unset( $link['link-class'] ); |
162 | } |
163 | $linkComponent = new SkinComponentLink( |
164 | $key, |
165 | $link, |
166 | $this->getMessageLocalizer(), |
167 | $options + $linkOptions |
168 | ); |
169 | $data = $linkComponent->getTemplateData(); |
170 | |
171 | $html = $data['html']; |
172 | unset( $data['html'] ); |
173 | // in the case of some links e.g. footer these may be HTML only so make sure not to add an empty object. |
174 | if ( $data ) { |
175 | $linksArray[] = $data; |
176 | } |
177 | } |
178 | |
179 | $attrs = []; |
180 | foreach ( [ 'id', 'class' ] as $attr ) { |
181 | if ( isset( $item[$attr] ) ) { |
182 | $attrs[$attr] = $item[$attr]; |
183 | } |
184 | } |
185 | $attrs['class'] = SkinComponentUtils::addClassToClassList( $attrs['class'] ?? [], 'mw-list-item' ); |
186 | |
187 | if ( isset( $item['active'] ) && $item['active'] ) { |
188 | // In the future, this should accept an array of classes, not a string |
189 | $attrs['class'] = SkinComponentUtils::addClassToClassList( $attrs['class'], 'active' ); |
190 | } |
191 | if ( isset( $item['itemtitle'] ) ) { |
192 | $attrs['title'] = $item['itemtitle']; |
193 | } |
194 | // Making sure we always have strings as class values |
195 | $classes = is_array( $attrs['class'] ) ? |
196 | implode( ' ', $attrs['class'] ) : |
197 | $attrs['class'] ?? null; |
198 | return [ |
199 | 'tag' => $options['tag'] ?? 'li', |
200 | 'attrs' => $attrs, |
201 | 'html' => $html, |
202 | 'class' => $classes, |
203 | 'array-links' => count( $linksArray ) > 0 ? $linksArray : null |
204 | ]; |
205 | } |
206 | |
207 | /** |
208 | * @inheritDoc |
209 | * @suppress SecurityCheck-DoubleEscaped |
210 | * |
211 | * @return array List item template data: |
212 | * - html-item: Full HTML for the list item with the content inside |
213 | * - name: Name/Key of the list item |
214 | * - html: String HTML for the list item content |
215 | * - id: ID of the list item |
216 | * - class: Classes for the list item |
217 | * - array-links: Array of link template data |
218 | */ |
219 | public function getTemplateData(): array { |
220 | $item = $this->makeListItem( $this->key, $this->item, $this->options, $this->defaultLinkOptions ); |
221 | $html = $item['html']; |
222 | return [ |
223 | 'html-item' => Html::rawElement( $item['tag'], $item['attrs'], $html ), |
224 | 'name' => $this->key, |
225 | 'html' => $html, |
226 | 'id' => $this->item['id'] ?? null, |
227 | 'class' => $item['class'], |
228 | 'array-links' => $item['array-links'] |
229 | ]; |
230 | } |
231 | } |