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    /**
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}