Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 90 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
SkinComponentLink | |
0.00% |
0 / 90 |
|
0.00% |
0 / 5 |
1482 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
msg | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
makeLink | |
0.00% |
0 / 66 |
|
0.00% |
0 / 1 |
600 | |||
applyLinkTitleAttribs | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
132 | |||
getTemplateData | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
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 MediaWiki\Linker\Linker; |
23 | use MediaWiki\Message\Message; |
24 | use MessageLocalizer; |
25 | |
26 | /** |
27 | * @internal for use inside Skin and SkinTemplate classes only |
28 | * @unstable |
29 | */ |
30 | class SkinComponentLink implements SkinComponent { |
31 | /** @var string */ |
32 | private $key; |
33 | /** @var array */ |
34 | private $item; |
35 | /** @var array */ |
36 | private $options; |
37 | /** @var MessageLocalizer */ |
38 | private $localizer; |
39 | |
40 | /** |
41 | * @param string $key |
42 | * @param array $item |
43 | * @param MessageLocalizer $localizer |
44 | * @param array $options |
45 | */ |
46 | public function __construct( string $key, array $item, MessageLocalizer $localizer, array $options = [] ) { |
47 | $this->key = $key; |
48 | $this->item = $item; |
49 | $this->localizer = $localizer; |
50 | $this->options = $options; |
51 | } |
52 | |
53 | /** |
54 | * @param string $key |
55 | * @return Message |
56 | */ |
57 | private function msg( string $key ): Message { |
58 | return $this->localizer->msg( $key ); |
59 | } |
60 | |
61 | /** |
62 | * Makes a link, usually used by makeListItem to generate a link for an item |
63 | * in a list used in navigation lists, portlets, portals, sidebars, etc... |
64 | * |
65 | * @param string $key Usually a key from the list you are generating this |
66 | * link from. |
67 | * @param array $item Contains some of a specific set of keys. |
68 | * |
69 | * The text of the link will be generated either from the contents of the |
70 | * "text" key in the $item array, if a "msg" key is present a message by |
71 | * that name will be used, and if neither of those are set the $key will be |
72 | * used as a message name. |
73 | * |
74 | * If a "href" key is not present makeLink will just output htmlescaped text. |
75 | * The "href", "id", "class", "rel", and "type" keys are used as attributes |
76 | * for the link if present. |
77 | * |
78 | * If an "id" or "single-id" (if you don't want the actual id to be output |
79 | * on the link) is present it will be used to generate a tooltip and |
80 | * accesskey for the link. |
81 | * |
82 | * The 'link-html' key can be used to prepend additional HTML inside the link HTML. |
83 | * For example to prepend an icon. |
84 | * |
85 | * The keys "context" and "primary" are ignored; these keys are used |
86 | * internally by skins and are not supposed to be included in the HTML |
87 | * output. |
88 | * |
89 | * If you don't want an accesskey, set $item['tooltiponly'] = true; |
90 | * |
91 | * If a "data" key is present, it must be an array, where the keys represent |
92 | * the data-xxx properties with their provided values. For example, |
93 | * $item['data'] = [ |
94 | * 'foo' => 1, |
95 | * 'bar' => 'baz', |
96 | * ]; |
97 | * will render as element properties: |
98 | * data-foo='1' data-bar='baz' |
99 | * |
100 | * The "class" key currently accepts both a string and an array of classes, but this will be |
101 | * changed to only accept an array in the future. |
102 | * |
103 | * @param array $options Can be used to affect the output of a link. |
104 | * Possible options are: |
105 | * - 'class-as-property' key to specify that class attribute should be |
106 | * not be included in array-attributes. |
107 | * - 'text-wrapper' key to specify a list of elements to wrap the text of |
108 | * a link in. This should be an array of arrays containing a 'tag' and |
109 | * optionally an 'attributes' key. If you only have one element you don't |
110 | * need to wrap it in another array. eg: To use <a><span>...</span></a> |
111 | * in all links use [ 'text-wrapper' => [ 'tag' => 'span' ] ] |
112 | * for your options. |
113 | * - 'link-class' key can be used to specify additional classes to apply |
114 | * to all links. |
115 | * - 'link-fallback' can be used to specify a tag to use instead of "<a>" |
116 | * if there is no link. eg: If you specify 'link-fallback' => 'span' than |
117 | * any non-link will output a "<span>" instead of just text. |
118 | * |
119 | * @return array Associated array with the following keys: |
120 | * - html: HTML string |
121 | * - array-attributes: HTML attributes as array of objects: |
122 | * - key: Attribute name ex: 'href', 'class', 'id', ... |
123 | * - value: Attribute value |
124 | * NOTE: if options['class-as-property'] is set, class will not be included in the list. |
125 | * - text: Text of the link |
126 | * - class: Class of the link |
127 | */ |
128 | private function makeLink( $key, $item, $options = [] ) { |
129 | $html = $item['html'] ?? null; |
130 | $icon = $item['icon'] ?? null; |
131 | if ( $html ) { |
132 | return [ |
133 | 'html' => $html |
134 | ]; |
135 | } |
136 | $text = $item['text'] ?? $this->msg( $item['msg'] ?? $key )->text(); |
137 | |
138 | $html = htmlspecialchars( $text ); |
139 | $isLink = isset( $item['href'] ) || isset( $options['link-fallback'] ); |
140 | |
141 | if ( $html !== '' && isset( $options['text-wrapper'] ) ) { |
142 | $wrapper = $options['text-wrapper']; |
143 | if ( isset( $wrapper['tag'] ) ) { |
144 | $wrapper = [ $wrapper ]; |
145 | } |
146 | while ( count( $wrapper ) > 0 ) { |
147 | $element = array_pop( $wrapper ); |
148 | '@phan-var array $element'; |
149 | |
150 | $attrs = $element['attributes'] ?? []; |
151 | // Apply title attribute to the outermost wrapper if there is |
152 | // no link wrapper. No need for an accesskey. |
153 | if ( count( $wrapper ) === 0 && !$isLink ) { |
154 | $this->applyLinkTitleAttribs( |
155 | $item, |
156 | false, |
157 | $attrs |
158 | ); |
159 | } |
160 | $html = Html::rawElement( $element['tag'], $attrs, $html ); |
161 | } |
162 | } |
163 | |
164 | $attrs = []; |
165 | $linkHtmlAttributes = []; |
166 | $classAsProperty = $options['class-as-property'] ?? false; |
167 | if ( $isLink ) { |
168 | $attrs = $item; |
169 | foreach ( [ |
170 | 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary', |
171 | // These fields provide context for skins to modify classes. |
172 | // They should not be outputted to skin. |
173 | 'icon', 'button', |
174 | 'tooltip-params', 'exists', 'link-html' ] as $k |
175 | ) { |
176 | unset( $attrs[$k] ); |
177 | } |
178 | |
179 | if ( isset( $attrs['data'] ) ) { |
180 | foreach ( $attrs['data'] as $key => $value ) { |
181 | if ( $value === null ) { |
182 | continue; |
183 | } |
184 | $attrs[ 'data-' . $key ] = $value; |
185 | } |
186 | unset( $attrs[ 'data' ] ); |
187 | } |
188 | $this->applyLinkTitleAttribs( $item, true, $attrs ); |
189 | $class = $attrs['class'] ?? []; |
190 | if ( isset( $options['link-class'] ) ) { |
191 | $class = SkinComponentUtils::addClassToClassList( |
192 | $class, $options['link-class'] |
193 | ); |
194 | } |
195 | $attrs['class'] = is_array( $class ) ? implode( ' ', $class ) : $class; |
196 | foreach ( $attrs as $key => $value ) { |
197 | if ( $value === null ) { |
198 | continue; |
199 | } |
200 | if ( $classAsProperty && $key === 'class' ) { |
201 | continue; |
202 | } |
203 | $linkHtmlAttributes[] = [ 'key' => $key, 'value' => $value ]; |
204 | } |
205 | |
206 | if ( isset( $item['link-html'] ) ) { |
207 | $html = $item['link-html'] . ' ' . $html; |
208 | } |
209 | |
210 | $html = Html::rawElement( isset( $attrs['href'] ) |
211 | ? 'a' |
212 | : $options['link-fallback'], $attrs, $html ); |
213 | } |
214 | $data = [ |
215 | 'html' => $html, |
216 | 'icon' => $icon, |
217 | 'array-attributes' => count( $linkHtmlAttributes ) > 0 ? $linkHtmlAttributes : null, |
218 | 'text' => trim( $text ), |
219 | ]; |
220 | if ( $classAsProperty ) { |
221 | $data['class'] = $attrs['class'] ?? ''; |
222 | } |
223 | return $data; |
224 | } |
225 | |
226 | /** |
227 | * Helper for makeLink(). Add tooltip and accesskey attributes to $attrs |
228 | * according to the input item array. |
229 | * |
230 | * @param array $item |
231 | * @param bool $allowAccessKey |
232 | * @param array &$attrs |
233 | */ |
234 | private function applyLinkTitleAttribs( $item, $allowAccessKey, &$attrs ) { |
235 | $tooltipId = $item['single-id'] ?? $item['id'] ?? null; |
236 | if ( $tooltipId === null ) { |
237 | return; |
238 | } |
239 | $tooltipParams = $item['tooltip-params'] ?? []; |
240 | $tooltipOption = isset( $item['exists'] ) && $item['exists'] === false ? 'nonexisting' : null; |
241 | |
242 | if ( !$allowAccessKey || !empty( $item['tooltiponly'] ) ) { |
243 | $title = Linker::titleAttrib( $tooltipId, $tooltipOption, $tooltipParams ); |
244 | if ( $title !== false ) { |
245 | $attrs['title'] = $title; |
246 | } |
247 | } else { |
248 | $tip = Linker::tooltipAndAccesskeyAttribs( |
249 | $tooltipId, |
250 | $tooltipParams, |
251 | $tooltipOption |
252 | ); |
253 | if ( isset( $tip['title'] ) && $tip['title'] !== false ) { |
254 | $attrs['title'] = $tip['title']; |
255 | } |
256 | if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) { |
257 | $attrs['accesskey'] = $tip['accesskey']; |
258 | } |
259 | } |
260 | } |
261 | |
262 | /** |
263 | * @inheritDoc |
264 | */ |
265 | public function getTemplateData(): array { |
266 | return $this->makeLink( $this->key, $this->item, $this->options ); |
267 | } |
268 | } |