data['namespace_urls'] = $this->data['content_navigation']['namespaces']; $this->data['view_urls'] = $this->data['content_navigation']['views']; $this->data['action_urls'] = $this->data['content_navigation']['actions']; $this->data['variant_urls'] = $this->data['content_navigation']['variants']; // Move the watch/unwatch star outside of the collapsed "actions" menu to the main "views" menu if ( $this->config->get( 'VectorUseIconWatch' ) ) { $mode = $this->getSkin()->getUser()->isWatched( $this->getSkin()->getRelevantTitle() ) ? 'unwatch' : 'watch'; if ( isset( $this->data['action_urls'][$mode] ) ) { $this->data['view_urls'][$mode] = $this->data['action_urls'][$mode]; unset( $this->data['action_urls'][$mode] ); } } // Naming conventions for Mustache parameters: // - Prefix "is" for boolean values. // - Prefix "msg-" for interface messages. // - Prefix "page-" for data relating to the current page (e.g. Title, WikiPage, or OutputPage). // - Prefix "html-" for raw HTML (in front of other keys, if applicable). // - Conditional values are null if absent. $params = [ 'html-headelement' => $this->get( 'headelement', '' ), 'html-sitenotice' => $this->get( 'sitenotice', null ), 'html-indicators' => $this->getIndicators(), 'page-langcode' => $this->getSkin()->getTitle()->getPageViewLanguage()->getHtmlCode(), 'page-isarticle' => (bool)$this->data['isarticle'], // Remember that the string '0' is a valid title. // From OutputPage::getPageTitle, via ::setPageTitle(). 'html-title' => $this->get( 'title', '' ), 'html-prebodyhtml' => $this->get( 'prebodyhtml', '' ), 'msg-tagline' => $this->getMsg( 'tagline' )->text(), // TODO: mediawiki/SkinTemplate should expose langCode and langDir properly. 'html-userlangattributes' => $this->get( 'userlangattributes', '' ), // From OutputPage::getSubtitle() 'html-subtitle' => $this->get( 'subtitle', '' ), // TODO: Use directly Skin::getUndeleteLink() directly. // Always returns string, cast to null if empty. 'html-undelete' => $this->get( 'undelete', null ) ?: null, // From Skin::getNewtalks(). Always returns string, cast to null if empty. 'html-newtalk' => $this->get( 'newtalk', '' ) ?: null, 'msg-jumptonavigation' => $this->getMsg( 'vector-jumptonavigation' )->text(), 'msg-jumptosearch' => $this->getMsg( 'vector-jumptosearch' )->text(), // Result of OutputPage::addHTML calls 'html-bodycontent' => $this->get( 'bodycontent' ), 'html-printfooter' => $this->get( 'printfooter', null ), 'html-catlinks' => $this->get( 'catlinks', '' ), 'html-dataAfterContent' => $this->get( 'dataAfterContent', '' ), // From MWDebug::getHTMLDebugLog (when $wgShowDebug is enabled) 'html-debuglog' => $this->get( 'debughtml', '' ), // From BaseTemplate::getTrail (handles bottom JavaScript) 'html-printtail' => $this->getTrail(), ]; // TODO: Convert the rest to Mustache ob_start(); ?>
processTemplate( 'index', $params ); } /** * Render a series of portals * * @param array $portals */ protected function renderPortals( array $portals ) { // Force the rendering of the following portals if ( !isset( $portals['TOOLBOX'] ) ) { $portals['TOOLBOX'] = true; } if ( !isset( $portals['LANGUAGES'] ) ) { $portals['LANGUAGES'] = true; } // Render portals foreach ( $portals as $name => $content ) { if ( $content === false ) { continue; } // Numeric strings gets an integer when set as key, cast back - T73639 $name = (string)$name; switch ( $name ) { case 'SEARCH': break; case 'TOOLBOX': $this->renderPortal( 'tb', $this->getToolbox(), 'toolbox', 'SkinTemplateToolboxEnd' ); Hooks::run( 'VectorAfterToolbox' ); break; case 'LANGUAGES': if ( $this->data['language_urls'] !== false ) { $this->renderPortal( 'lang', $this->data['language_urls'], 'otherlanguages' ); } break; default: $this->renderPortal( $name, $content ); break; } } } /** * @param string $name * @param array|string $content * @param null|string $msg * @param null|string|array $hook */ protected function renderPortal( $name, $content, $msg = null, $hook = null ) { if ( $msg === null ) { $msg = $name; } $msgObj = $this->getMsg( $msg ); $labelId = Sanitizer::escapeIdForAttribute( "p-$name-label" ); ?> $element ) { switch ( $element ) { case 'NAMESPACES': $this->renderNamespacesComponent( $templateParser ); break; case 'VARIANTS': $this->renderVariantsComponent(); break; case 'VIEWS': $this->renderViewsComponent( $templateParser ); break; case 'ACTIONS': $this->renderActionsComponent(); break; case 'PERSONAL': $this->renderPersonalComponent( $templateParser ); break; case 'SEARCH': $this->renderSearchComponent( $templateParser ); break; } } } /** * @inheritDoc */ public function makeListItem( $key, $item, $options = [] ) { // For fancy styling of watch/unwatch star if ( $this->config->get( 'VectorUseIconWatch' ) && ( $key === 'watch' || $key === 'unwatch' ) ) { if ( !isset( $item['class'] ) ) { $item['class'] = ''; } $item['class'] = rtrim( 'icon ' . $item['class'], ' ' ); $item['primary'] = true; } // Add CSS class 'collapsible' to links which are not marked as "primary" if ( isset( $options['vector-collapsible'] ) && $options['vector-collapsible'] ) { if ( !isset( $item['class'] ) ) { $item['class'] = ''; } $item['class'] = rtrim( 'collapsible ' . $item['class'], ' ' ); } return parent::makeListItem( $key, $item, $options ); } /** * @param TemplateParser $templateParser */ private function renderNamespacesComponent( TemplateParser $templateParser ) { $props = [ 'tabs-id' => 'p-namespaces', 'empty-portlet' => ( count( $this->data['namespace_urls'] ) == 0 ) ? 'emptyPortlet' : '', 'label-id' => 'p-namespaces-label', 'msg-label' => $this->getMsg( 'namespaces' )->text(), 'html-userlangattributes' => $this->data['userlangattributes'] ?? '', 'html-items' => '', ]; foreach ( $this->data['namespace_urls'] as $key => $item ) { $props[ 'html-items' ] .= $this->makeListItem( $key, $item ); } echo $templateParser->processTemplate( 'VectorTabs', $props ); } private function renderVariantsComponent() { ?> 'p-views', 'empty-portlet' => ( count( $this->data['view_urls'] ) == 0 ) ? 'emptyPortlet' : '', 'label-id' => 'p-views-label', 'msg-label' => $this->getMsg( 'views' )->text(), 'html-userlangattributes' => $this->data['userlangattributes'] ?? '', 'html-items' => '', ]; foreach ( $this->data['view_urls'] as $key => $item ) { $props[ 'html-items' ] .= $this->makeListItem( $key, $item, [ 'vector-collapsible' => true, ] ); } echo $templateParser->processTemplate( 'VectorTabs', $props ); } private function renderActionsComponent() { ?> getPersonalTools(); $props = [ 'empty-portlet' => ( count( $this->data['personal_urls'] ) == 0 ) ? 'emptyPortlet' : '', 'msg-label' => $this->getMsg( 'personaltools' )->text(), 'html-userlangattributes' => $this->data[ 'userlangattributes' ] ?? '', 'html-loggedin' => '', 'html-personal-tools' => '', 'html-lang-selector' => '', ]; if ( !$this->getSkin()->getUser()->isLoggedIn() && User::groupHasPermission( '*', 'edit' ) ) { $props['html-loggedin'] = Html::element( 'li', [ 'id' => 'pt-anonuserpage' ], $this->getMsg( 'notloggedin' )->text() ); } if ( array_key_exists( 'uls', $personalTools ) ) { $props['html-lang-selector'] = $this->makeListItem( 'uls', $personalTools[ 'uls' ] ); unset( $personalTools[ 'uls' ] ); } foreach ( $personalTools as $key => $item ) { $props['html-personal-tools'] .= $this->makeListItem( $key, $item ); } echo $templateParser->processTemplate( 'PersonalMenu', $props ); } /** * @param TemplateParser $templateParser */ private function renderSearchComponent( TemplateParser $templateParser ) { $props = [ 'searchHeaderAttrsHTML' => $this->data[ 'userlangattributes' ] ?? '', 'searchActionURL' => $this->data[ 'wgScript' ] ?? '', 'searchDivID' => $this->config->get( 'VectorUseSimpleSearch' ) ? 'simpleSearch' : '', 'searchInputHTML' => $this->makeSearchInput( [ 'id' => 'searchInput' ] ), 'titleHTML' => Html::hidden( 'title', $this->data[ 'searchtitle' ] ?? null ), 'fallbackSearchButtonHTML' => $this->makeSearchButton( 'fulltext', [ 'id' => 'mw-searchButton', 'class' => 'searchButton mw-fallbackSearchButton' ] ), 'searchButtonHTML' => $this->makeSearchButton( 'go', [ 'id' => 'searchButton', 'class' => 'searchButton' ] ), 'searchInputLabel' => $this->getMsg( 'search' ) ]; echo $templateParser->processTemplate( 'SearchBox', $props ); } }