3 namespace MediaWiki\Skin
;
5 use MediaWiki\Html\Html
;
6 use MediaWiki\Linker\Linker
;
7 use MediaWiki\Message\Message
;
8 use MediaWiki\Parser\Sanitizer
;
12 * @internal for use inside Skin and SkinTemplate classes only
15 class SkinComponentMenu
implements SkinComponent
{
23 /** @var MessageLocalizer */
33 private $htmlAfterContent;
36 private $htmlBeforeContent;
41 * @param MessageLocalizer $localizer
42 * @param string $content
43 * @param array $linkOptions
44 * @param string $htmlAfterContent
45 * @param string $htmlBeforeContent
47 public function __construct(
50 MessageLocalizer
$localizer,
52 array $linkOptions = [],
53 string $htmlAfterContent = '',
54 string $htmlBeforeContent = ''
57 $this->items
= $items;
58 $this->localizer
= $localizer;
59 $this->content
= $content;
60 $this->linkOptions
= $linkOptions;
61 $this->htmlAfterContent
= $htmlAfterContent;
62 $this->htmlBeforeContent
= $htmlBeforeContent;
65 private function msg( string $key ): Message
{
66 return $this->localizer
->msg( $key );
70 * @param string $name of the menu e.g. p-personal the name is personal.
72 * @return string that is human-readable corresponding to the menu.
74 private function getMenuLabel( $name ) {
75 // For historic reasons for some menu items, there is no language key corresponding
79 'personal' => 'personaltools',
80 'lang' => 'otherlanguages',
82 $msgObj = $this->msg( $mappings[ $name ] ??
$name );
83 // If no message exists fallback to plain text (T252727)
84 return $msgObj->exists() ?
$msgObj->text() : $name;
90 public function getTemplateData(): array {
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' ) {
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' ) {
106 if ( strpos( $name, 'footer' ) === 0 ) {
107 // Retain footer IDs.
110 $id = Sanitizer
::escapeIdForAttribute( "p-$name" );
113 $isEmptyContent = !$this->content
;
114 $isEmptyAfterContent = !$this->htmlAfterContent
;
115 $isEmptyBeforeContent = !$this->htmlBeforeContent
;
116 $isEmptyItems = count( $this->items
) === 0;
117 $isEmptyPortlet = ( $isEmptyContent && $isEmptyAfterContent && $isEmptyBeforeContent && $isEmptyItems );
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 ),
124 // Will be populated by SkinAfterPortlet hook.
125 'html-after-portal' => '',
126 'html-before-portal' => '',
127 'is-empty' => $isEmptyPortlet,
130 // for output. In production this currently supports the Wikibase 'edit' link.
131 if ( !$isEmptyAfterContent ) {
132 $data['html-after-portal'] = Html
::rawElement(
137 Sanitizer
::escapeClass( "after-portlet-$name" ),
140 $this->htmlAfterContent
144 if ( !$isEmptyBeforeContent ) {
145 $data['html-before-portal'] = Html
::rawElement(
150 Sanitizer
::escapeClass( "before-portlet-$name" ),
153 $this->htmlBeforeContent
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;
165 $data['html-items'] = $html;
166 $data['array-items'] = $arrayItems;
168 $data['label'] = $this->getMenuLabel( $name );
169 $data['class'] .= $isEmptyPortlet ?
' emptyPortlet' : '';