summaryrefslogtreecommitdiff
path: root/www/wiki/skins/Timeless/TimelessTemplate.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/skins/Timeless/TimelessTemplate.php')
-rw-r--r--www/wiki/skins/Timeless/TimelessTemplate.php850
1 files changed, 850 insertions, 0 deletions
diff --git a/www/wiki/skins/Timeless/TimelessTemplate.php b/www/wiki/skins/Timeless/TimelessTemplate.php
new file mode 100644
index 00000000..63173207
--- /dev/null
+++ b/www/wiki/skins/Timeless/TimelessTemplate.php
@@ -0,0 +1,850 @@
+<?php
+/**
+ * BaseTemplate class for the Timeless skin
+ *
+ * @ingroup Skins
+ */
+class TimelessTemplate extends BaseTemplate {
+
+ /** @var array */
+ protected $pileOfTools;
+
+ /**
+ * Outputs the entire contents of the page
+ */
+ public function execute() {
+ $this->pileOfTools = $this->getPageTools();
+ $userLinks = $this->getUserLinks();
+
+ // Open html, body elements, etc
+ $html = $this->get( 'headelement' );
+
+ $html .= Html::openElement( 'div', [ 'id' => 'mw-wrapper', 'class' => $userLinks['class'] ] );
+
+ $html .= Html::rawElement( 'div', [ 'id' => 'mw-header-container', 'class' => 'ts-container' ],
+ Html::rawElement( 'div', [ 'id' => 'mw-header', 'class' => 'ts-inner' ],
+ $userLinks['html'] .
+ $this->getLogo( 'p-logo-text', 'text' ) .
+ $this->getSearch()
+ ) .
+ $this->getClear()
+ );
+ $html .= $this->getHeaderHack();
+
+ // For mobile
+ $html .= Html::element( 'div', [ 'id' => 'menus-cover' ] );
+
+ $html .= Html::rawElement( 'div', [ 'id' => 'mw-content-container', 'class' => 'ts-container' ],
+ Html::rawElement( 'div', [ 'id' => 'mw-content-block', 'class' => 'ts-inner' ],
+ Html::rawElement( 'div', [ 'id' => 'mw-site-navigation' ],
+ $this->getLogo( 'p-logo', 'image' ) .
+ $this->getMainNavigation() .
+ $this->getSidebarChunk(
+ 'site-tools',
+ 'timeless-sitetools',
+ $this->getPortlet(
+ 'tb',
+ $this->pileOfTools['general'],
+ 'timeless-sitetools'
+ )
+ )
+ ) .
+ Html::rawElement( 'div', [ 'id' => 'mw-related-navigation' ],
+ $this->getPageToolSidebar() .
+ $this->getInterlanguageLinks() .
+ $this->getCategories()
+ ) .
+ Html::rawElement( 'div', [ 'id' => 'mw-content' ],
+ Html::rawElement( 'div', [ 'id' => 'content', 'class' => 'mw-body', 'role' => 'main' ],
+ $this->getSiteNotices() .
+ $this->getIndicators() .
+ Html::rawElement(
+ 'h1',
+ [
+ 'id' => 'firstHeading',
+ 'class' => 'firstHeading',
+ 'lang' => $this->get( 'pageLanguage' )
+ ],
+ $this->get( 'title' )
+ ) .
+ Html::rawElement( 'div', [ 'id' => 'mw-page-header-links' ],
+ $this->getPortlet(
+ 'namespaces',
+ $this->pileOfTools['namespaces'],
+ 'timeless-namespaces'
+ ) .
+ $this->getPortlet(
+ 'views',
+ $this->pileOfTools['page-primary'],
+ 'timeless-pagetools'
+ )
+ ) .
+ $this->getClear() .
+ Html::rawElement( 'div', [ 'class' => 'mw-body-content', 'id' => 'bodyContent' ],
+ $this->getContentSub() .
+ $this->get( 'bodytext' ) .
+ $this->getClear()
+ )
+ )
+ ) .
+ $this->getAfterContent() .
+ $this->getClear()
+ )
+ );
+
+ $html .= Html::rawElement( 'div', [ 'id' => 'mw-footer-container', 'class' => 'ts-container' ],
+ Html::rawElement( 'div', [ 'id' => 'mw-footer', 'class' => 'ts-inner' ],
+ $this->getFooter()
+ )
+ );
+
+ $html .= Html::closeElement( 'div' );
+
+ // BaseTemplate::printTrail() stuff (has no get version)
+ // Required for RL to run
+ $html .= MWDebug::getDebugHTML( $this->getSkin()->getContext() );
+ $html .= $this->get( 'bottomscripts' );
+ $html .= $this->get( 'reporttime' );
+
+ $html .= Html::closeElement( 'body' );
+ $html .= Html::closeElement( 'html' );
+
+ // The unholy echo
+ echo $html;
+ }
+
+ /**
+ * Generates a block of navigation links with a header
+ *
+ * @param string $name
+ * @param array|string $content array of links for use with makeListItem, or a block of text
+ * Expected array format:
+ * [
+ * $name => [
+ * 'links' => [ '0' =>
+ * [ 'href' => ..., 'single-id' => ..., 'text' => ... ]
+ * ],
+ * 'id' => ...,
+ * 'active' => ...
+ * ],
+ * ...
+ * ]
+ * @param null|string|array|bool $msg
+ *
+ * @return string html
+ * @since 1.29
+ */
+ protected function getPortlet( $name, $content, $msg = null ) {
+ if ( $msg === null ) {
+ $msg = $name;
+ } elseif ( is_array( $msg ) ) {
+ $msgString = array_shift( $msg );
+ $msgParams = $msg;
+ $msg = $msgString;
+ }
+ $msgObj = wfMessage( $msg );
+ if ( $msgObj->exists() ) {
+ if ( isset( $msgParams ) && !empty( $msgParams ) ) {
+ $msgString = $this->getMsg( $msg, $msgParams )->parse();
+ } else {
+ $msgString = $msgObj->parse();
+ }
+ } else {
+ $msgString = htmlspecialchars( $msg );
+ }
+
+ // HACK: Compatibility with extensions still using SkinTemplateToolboxEnd
+ $hookContents = '';
+ if ( $name == 'tb' ) {
+ if ( isset( $boxes['TOOLBOX'] ) ) {
+ ob_start();
+ // We pass an extra 'true' at the end so extensions using BaseTemplateToolbox
+ // can abort and avoid outputting double toolbox links
+ // Avoid PHP 7.1 warning from passing $this by reference
+ $template = $this;
+ Hooks::run( 'SkinTemplateToolboxEnd', [ &$template, true ] );
+ $hookContents = ob_get_contents();
+ ob_end_clean();
+ if ( !trim( $hookContents ) ) {
+ $hookContents = '';
+ }
+ }
+ }
+ // END hack
+
+ $labelId = Sanitizer::escapeId( "p-$name-label" );
+
+ if ( is_array( $content ) ) {
+ $contentText = Html::openElement( 'ul' );
+ if ( $content !== [] ) {
+ foreach ( $content as $key => $item ) {
+ $contentText .= $this->makeListItem(
+ $key,
+ $item,
+ [ 'text-wrapper' => [ 'tag' => 'span' ] ]
+ );
+ }
+ }
+ // Add in SkinTemplateToolboxEnd, if any
+ $contentText .= $hookContents;
+ $contentText .= Html::closeElement( 'ul' );
+ } else {
+ $contentText = $content;
+ }
+
+ $html = Html::rawElement( 'div', [
+ 'role' => 'navigation',
+ 'class' => [ 'mw-portlet', 'emptyPortlet' => !$content ],
+ 'id' => Sanitizer::escapeId( 'p-' . $name ),
+ 'title' => Linker::titleAttrib( 'p-' . $name ),
+ 'aria-labelledby' => $labelId
+ ],
+ Html::rawElement( 'h3', [
+ 'id' => $labelId,
+ 'lang' => $this->get( 'userlang' ),
+ 'dir' => $this->get( 'dir' )
+ ],
+ $msgString
+ ) .
+ Html::rawElement( 'div', [ 'class' => 'mw-portlet-body' ],
+ $contentText .
+ $this->getAfterPortlet( $name )
+ )
+ );
+
+ return $html;
+ }
+
+ /**
+ * Sidebar chunk containing one or more portlets
+ *
+ * @param string $id
+ * @param string $headerMessage
+ * @param string $content
+ *
+ * @return string html
+ */
+ protected function getSidebarChunk( $id, $headerMessage, $content ) {
+ $html = '';
+
+ $html .= Html::rawElement(
+ 'div',
+ [ 'id' => Sanitizer::escapeId( $id ), 'class' => 'sidebar-chunk' ],
+ Html::rawElement( 'h2', [],
+ Html::element( 'span', [],
+ $this->getMsg( $headerMessage )->text()
+ ) .
+ Html::element( 'div', [ 'class' => 'pokey' ] )
+ ) .
+ Html::rawElement( 'div', [ 'class' => 'sidebar-inner' ], $content )
+ );
+
+ return $html;
+ }
+
+ /**
+ * The logo and (optionally) site title
+ *
+ * @param string $id
+ * @param string $part whether it's only image, only text, or both
+ *
+ * @return string html
+ */
+ protected function getLogo( $id = 'p-logo', $part = 'both' ) {
+ $html = '';
+ $language = $this->getSkin()->getLanguage();
+
+ $html .= Html::openElement(
+ 'div',
+ [
+ 'id' => Sanitizer::escapeId( $id ),
+ 'class' => 'mw-portlet',
+ 'role' => 'banner'
+ ]
+ );
+ if ( $part !== 'image' ) {
+ $titleClass = '';
+ if ( $language->hasVariants() ) {
+ $siteTitle = $language->convert( $this->getMsg( 'timeless-sitetitle' )->text() );
+ } else {
+ $siteTitle = $this->getMsg( 'timeless-sitetitle' )->text();
+ }
+ // width is 11em; 13 characters will probably fit?
+ if ( mb_strlen( $siteTitle ) > 13 ) {
+ $titleClass = 'long';
+ }
+ $html .= Html::element( 'a', [
+ 'id' => 'p-banner',
+ 'class' => [ 'mw-wiki-title', $titleClass ],
+ 'href' => $this->data['nav_urls']['mainpage']['href']
+ ],
+ $siteTitle
+ );
+ }
+ if ( $part !== 'text' ) {
+ $html .= Html::element( 'a', array_merge(
+ [
+ 'class' => 'mw-wiki-logo',
+ 'href' => $this->data['nav_urls']['mainpage']['href']
+ ],
+ Linker::tooltipAndAccesskeyAttribs( 'p-logo' )
+ ) );
+ }
+ $html .= Html::closeElement( 'div' );
+
+ return $html;
+ }
+
+ /**
+ * The search box at the top
+ *
+ * @return string html
+ */
+ protected function getSearch() {
+ $html = '';
+
+ $html .= Html::openElement( 'div', [ 'class' => 'mw-portlet', 'id' => 'p-search' ] );
+
+ $html .= Html::rawElement(
+ 'h3',
+ [ 'lang' => $this->get( 'userlang' ), 'dir' => $this->get( 'dir' ) ],
+ Html::rawElement( 'label', [ 'for' => 'searchInput' ], $this->getMsg( 'search' )->text() )
+ );
+
+ $html .= Html::rawElement( 'form', [ 'action' => $this->get( 'wgScript' ), 'id' => 'searchform' ],
+ Html::rawElement( 'div', [ 'id' => 'simpleSearch' ],
+ Html::rawElement( 'div', [ 'id' => 'searchInput-container' ],
+ $this->makeSearchInput( [
+ 'id' => 'searchInput',
+ 'placeholder' => $this->getMsg( 'timeless-search-placeholder' )->text(),
+ ] )
+ ) .
+ Html::hidden( 'title', $this->get( 'searchtitle' ) ) .
+ $this->makeSearchButton(
+ 'fulltext',
+ [ 'id' => 'mw-searchButton', 'class' => 'searchButton mw-fallbackSearchButton' ]
+ ) .
+ $this->makeSearchButton(
+ 'go',
+ [ 'id' => 'searchButton', 'class' => 'searchButton' ]
+ )
+ )
+ );
+
+ $html .= Html::closeElement( 'div' );
+
+ return $html;
+ }
+
+ /**
+ * Left sidebar navigation, usually
+ *
+ * @return string html
+ */
+ protected function getMainNavigation() {
+ $sidebar = $this->getSidebar();
+ $html = '';
+
+ // Already hardcoded into header
+ $sidebar['SEARCH'] = false;
+ // Parsed as part of pageTools
+ $sidebar['TOOLBOX'] = false;
+ // Forcibly removed to separate chunk
+ $sidebar['LANGUAGES'] = false;
+
+ foreach ( $sidebar as $name => $content ) {
+ if ( $content === false ) {
+ continue;
+ }
+ // Numeric strings gets an integer when set as key, cast back - T73639
+ $name = (string)$name;
+ $html .= $this->getPortlet( $name, $content['content'] );
+ }
+
+ $html = $this->getSidebarChunk( 'site-navigation', 'navigation', $html );
+
+ return $html;
+ }
+
+ /**
+ * The colour bars
+ * Split this out so we don't have to look at it/can easily kill it later
+ *
+ * @return string html
+ */
+ protected function getHeaderHack() {
+ $html = '';
+
+ // These are almost exactly the same and this is stupid.
+ $html .= Html::rawElement( 'div', [ 'id' => 'mw-header-hack', 'class' => 'color-bar' ],
+ Html::rawElement( 'div', [ 'class' => 'color-middle-container' ],
+ Html::element( 'div', [ 'class' => 'color-middle' ] )
+ ) .
+ Html::element( 'div', [ 'class' => 'color-left' ] ) .
+ Html::element( 'div', [ 'class' => 'color-right' ] )
+ );
+ $html .= Html::rawElement( 'div', [ 'id' => 'mw-header-nav-hack' ],
+ Html::rawElement( 'div', [ 'class' => 'color-bar' ],
+ Html::rawElement( 'div', [ 'class' => 'color-middle-container' ],
+ Html::element( 'div', [ 'class' => 'color-middle' ] )
+ ) .
+ Html::element( 'div', [ 'class' => 'color-left' ] ) .
+ Html::element( 'div', [ 'class' => 'color-right' ] )
+ )
+ );
+
+ return $html;
+ }
+
+ /**
+ * Page tools in sidebar
+ *
+ * @return string html
+ */
+ protected function getPageToolSidebar() {
+ $pageTools = '';
+ $pageTools .= $this->getPortlet(
+ 'cactions',
+ $this->pileOfTools['page-secondary'],
+ 'timeless-pageactions'
+ );
+ $pageTools .= $this->getPortlet(
+ 'userpagetools',
+ $this->pileOfTools['user'],
+ 'timeless-userpagetools'
+ );
+ $pageTools .= $this->getPortlet(
+ 'pagemisc',
+ $this->pileOfTools['page-tertiary'],
+ 'timeless-pagemisc'
+ );
+
+ return $this->getSidebarChunk( 'page-tools', 'timeless-pageactions', $pageTools );
+ }
+
+ /**
+ * Personal/user links portlet for header
+ *
+ * @return array [ html, class ], where class is an extra class to apply to surrounding objects
+ * (for width adjustments)
+ */
+ protected function getUserLinks() {
+ $user = $this->getSkin()->getUser();
+ $personalTools = $this->getPersonalTools();
+
+ $html = '';
+ $extraTools = [];
+
+ // Remove Echo badges
+ if ( isset( $personalTools['notifications-alert'] ) ) {
+ $extraTools['notifications-alert'] = $personalTools['notifications-alert'];
+ unset( $personalTools['notifications-alert'] );
+ }
+ if ( isset( $personalTools['notifications-notice'] ) ) {
+ $extraTools['notifications-notice'] = $personalTools['notifications-notice'];
+ unset( $personalTools['notifications-notice'] );
+ }
+ $class = empty( $extraTools ) ? '' : 'extension-icons';
+
+ // Re-label some messages
+ if ( isset( $personalTools['userpage'] ) ) {
+ $personalTools['userpage']['links'][0]['text'] = $this->getMsg( 'timeless-userpage' )->text();
+ }
+ if ( isset( $personalTools['mytalk'] ) ) {
+ $personalTools['mytalk']['links'][0]['text'] = $this->getMsg( 'timeless-talkpage' )->text();
+ }
+
+ // Labels
+ if ( $user->isLoggedIn() ) {
+ $userName = $user->getName();
+ // Make sure it fits first (numbers slightly made up, may need adjusting)
+ $fit = empty( $extraTools ) ? 13 : 9;
+ if ( mb_strlen( $userName ) < $fit ) {
+ $dropdownHeader = $userName;
+ } else {
+ $dropdownHeader = wfMessage( 'timeless-loggedin' )->text();
+ }
+ $headerMsg = [ 'timeless-loggedinas', $user->getName() ];
+ } else {
+ $dropdownHeader = wfMessage( 'timeless-anonymous' )->text();
+ $headerMsg = 'timeless-notloggedin';
+ }
+ $html .= Html::openElement( 'div', [ 'id' => 'user-tools' ] );
+
+ $html .= Html::rawElement( 'div', [ 'id' => 'personal' ],
+ Html::rawElement( 'h2', [],
+ Html::element( 'span', [], $dropdownHeader ) .
+ Html::element( 'div', [ 'class' => 'pokey' ] )
+ ) .
+ Html::rawElement( 'div', [ 'id' => 'personal-inner', 'class' => 'dropdown' ],
+ $this->getPortlet( 'personal', $personalTools, $headerMsg )
+ )
+ );
+
+ // Extra icon stuff (echo etc)
+ if ( !empty( $extraTools ) ) {
+ $iconList = '';
+ foreach ( $extraTools as $key => $item ) {
+ $iconList .= $this->makeListItem( $key, $item );
+ }
+
+ $html .= Html::rawElement(
+ 'div',
+ [ 'id' => 'personal-extra', 'class' => 'p-body' ],
+ Html::rawElement( 'ul', [], $iconList )
+ );
+ }
+
+ $html .= Html::closeElement( 'div' );
+
+ return [
+ 'html' => $html,
+ 'class' => $class
+ ];
+ }
+
+ /**
+ * Notices that may appear above the firstHeading
+ *
+ * @return string html
+ */
+ protected function getSiteNotices() {
+ $html = '';
+
+ if ( $this->data['sitenotice'] ) {
+ $html .= Html::rawElement( 'div', [ 'id' => 'siteNotice' ], $this->get( 'sitenotice' ) );
+ }
+ if ( $this->data['newtalk'] ) {
+ $html .= Html::rawElement( 'div', [ 'class' => 'usermessage' ], $this->get( 'newtalk' ) );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Links and information that may appear below the firstHeading
+ *
+ * @return string html
+ */
+ protected function getContentSub() {
+ $html = '';
+
+ $html .= Html::openElement( 'div', [ 'id' => 'contentSub' ] );
+ if ( $this->data['subtitle'] ) {
+ $html .= $this->get( 'subtitle' );
+ }
+ if ( $this->data['undelete'] ) {
+ $html .= $this->get( 'undelete' );
+ }
+ $html .= Html::closeElement( 'div' );
+
+ return $html;
+ }
+
+ /**
+ * The data after content, catlinks, and potential other stuff that may appear within
+ * the content block but after the main content
+ *
+ * @return string html
+ */
+ protected function getAfterContent() {
+ $html = '';
+
+ if ( $this->data['catlinks'] || $this->data['dataAfterContent'] ) {
+ $html .= Html::openElement( 'div', [ 'id' => 'content-bottom-stuff' ] );
+ if ( $this->data['catlinks'] ) {
+ $html .= $this->get( 'catlinks' );
+ }
+ if ( $this->data['dataAfterContent'] ) {
+ $html .= $this->get( 'dataAfterContent' );
+ }
+ $html .= Html::closeElement( 'div' );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Generate pile of all the tools
+ *
+ * We can make a few assumptions based on where a tool started out:
+ * If it's in the cactions region, it's a page tool, probably primary or secondary
+ * ...that's all I can think of
+ *
+ * @return array of array of tools information (portlet formatting)
+ */
+ protected function getPageTools() {
+ $title = $this->getSkin()->getTitle();
+ $namespace = $title->getNamespace();
+
+ $sortedPileOfTools = [
+ 'namespaces' => [],
+ 'page-primary' => [],
+ 'page-secondary' => [],
+ 'user' => [],
+ 'page-tertiary' => [],
+ 'general' => []
+ ];
+
+ // Tools specific to the page
+ $pileOfEditTools = [];
+ foreach ( $this->data['content_navigation'] as $navKey => $navBlock ) {
+ // Just use namespaces items as they are
+ if ( $navKey == 'namespaces' ) {
+ if ( $namespace < 0 ) {
+ // Put special page ns_pages in the more pile so they're not so lonely
+ $sortedPileOfTools['page-tertiary'] = $navBlock;
+ } else {
+ $sortedPileOfTools['namespaces'] = $navBlock;
+ }
+ } else {
+ $pileOfEditTools = array_merge( $pileOfEditTools, $navBlock );
+ }
+ }
+
+ // Tools that may be general or page-related (typically the toolbox)
+ $pileOfTools = $this->getToolbox();
+ if ( $namespace >= 0 ) {
+ $pileOfTools['pagelog'] = [
+ 'text' => $this->getMsg( 'timeless-pagelog' )->text(),
+ 'href' => SpecialPage::getTitleFor( 'Log', $title->getPrefixedText() )->getLocalURL(),
+ 'id' => 't-pagelog'
+ ];
+ }
+ $pileOfTools['more'] = [
+ 'text' => $this->getMsg( 'timeless-more' )->text(),
+ 'id' => 'ca-more',
+ 'class' => 'dropdown-toggle'
+ ];
+
+ // Goes in the page-primary in mobile, doesn't appear otherwise
+ if ( $this->data['language_urls'] !== false ) {
+ $pileOfTools['languages'] = [
+ 'text' => $this->getMsg( 'timeless-languages' )->escaped(),
+ 'id' => 'ca-languages',
+ 'class' => 'dropdown-toggle'
+ ];
+ }
+
+ // This is really dumb, and you're an idiot for doing it this way.
+ // Obviously if you're not the idiot who did this, I don't mean you.
+ foreach ( $pileOfEditTools as $navKey => $navBlock ) {
+ $currentSet = null;
+
+ if ( in_array( $navKey, [
+ 'watch',
+ 'unwatch'
+ ] ) ) {
+ $currentSet = 'namespaces';
+ } elseif ( in_array( $navKey, [
+ 'edit',
+ 'view',
+ 'history',
+ 'addsection',
+ 'viewsource'
+ ] ) ) {
+ $currentSet = 'page-primary';
+ } elseif ( in_array( $navKey, [
+ 'delete',
+ 'rename',
+ 'protect',
+ 'unprotect',
+ 'move'
+ ] ) ) {
+ $currentSet = 'page-secondary';
+ } else {
+ // Catch random extension ones?
+ $currentSet = 'page-primary';
+ }
+ $sortedPileOfTools[$currentSet][$navKey] = $navBlock;
+ }
+ foreach ( $pileOfTools as $navKey => $navBlock ) {
+ $currentSet = null;
+
+ if ( in_array( $navKey, [
+ 'contributions',
+ 'more',
+ 'languages'
+ ] ) ) {
+ $currentSet = 'page-primary';
+ } elseif ( in_array( $navKey, [
+ 'blockip',
+ 'userrights',
+ 'log'
+ ] ) ) {
+ $currentSet = 'user';
+ } elseif ( in_array( $navKey, [
+ 'whatlinkshere',
+ 'print',
+ 'info',
+ 'pagelog',
+ 'recentchangeslinked',
+ 'permalink'
+ ] ) ) {
+ $currentSet = 'page-tertiary';
+ } else {
+ $currentSet = 'general';
+ }
+ $sortedPileOfTools[$currentSet][$navKey] = $navBlock;
+ }
+
+ return $sortedPileOfTools;
+ }
+
+ /**
+ * Categories for the sidebar
+ *
+ * Assemble an array of categories, regardless of view mode. Just using Skin or
+ * OutputPage functions doesn't respect view modes (preview, history, whatever)
+ * But why? I have no idea what the purpose of this is.
+ *
+ * @return string html
+ */
+ protected function getCategories() {
+ global $wgContLang;
+
+ $skin = $this->getSkin();
+ $title = $skin->getTitle();
+ $catList = false;
+ $html = '';
+
+ // Get list from outputpage if in preview; otherwise get list from title
+ if ( in_array( $skin->getRequest()->getVal( 'action' ), [ 'submit', 'edit' ] ) ) {
+ $allCats = [];
+ // Can't just use getCategoryLinks because there's no equivalent for Title
+ $allCats2 = $skin->getOutput()->getCategories();
+ foreach ( $allCats2 as $displayName ) {
+ $catTitle = Title::makeTitleSafe( NS_CATEGORY, $displayName );
+ $allCats[] = $catTitle->getDBkey();
+ }
+ } else {
+ // This is probably to trim out some excessive stuff. Unless I was just high on cough syrup.
+ $allCats = array_keys( $title->getParentCategories() );
+
+ $len = strlen( $wgContLang->getNsText( NS_CATEGORY ) . ':' );
+ foreach ( $allCats as $i => $catName ) {
+ $allCats[$i] = substr( $catName, $len );
+ }
+ }
+ if ( $allCats !== [] ) {
+ $dbr = wfGetDB( DB_REPLICA );
+ $res = $dbr->select(
+ [ 'page', 'page_props' ],
+ [ 'page_id', 'page_title' ],
+ [
+ 'page_title' => $allCats,
+ 'page_namespace' => NS_CATEGORY,
+ 'pp_propname' => 'hiddencat'
+ ],
+ __METHOD__,
+ [],
+ [ 'page_props' => [ 'JOIN', 'pp_page = page_id' ] ]
+ );
+ $hiddenCats = [];
+ foreach ( $res as $row ) {
+ $hiddenCats[] = $row->page_title;
+ }
+ $normalCats = array_diff( $allCats, $hiddenCats );
+
+ $normalCount = count( $normalCats );
+ $hiddenCount = count( $hiddenCats );
+ $count = $normalCount;
+
+ // Mostly consistent with how Skin does it.
+ // Doesn't have the classes. Either way can't be good for caching.
+ if (
+ $skin->getUser()->getBoolOption( 'showhiddencats' ) ||
+ $title->getNamespace() == NS_CATEGORY
+ ) {
+ $count += $hiddenCount;
+ } else {
+ /* We don't care if there are hidden ones. */
+ $hiddenCount = 0;
+ }
+
+ // Assemble the html...
+ if ( $count ) {
+ if ( $normalCount ) {
+ $catHeader = 'categories';
+ } else {
+ $catHeader = 'hidden-categories';
+ }
+ $catList = '';
+ if ( $normalCount ) {
+ $catList .= $this->getCatList( $normalCats, 'catlist-normal', 'categories' );
+ }
+ if ( $hiddenCount ) {
+ $catList .= $this->getCatList(
+ $hiddenCats,
+ 'catlist-hidden',
+ [ 'hidden-categories', $hiddenCount ]
+ );
+ }
+ }
+ }
+ if ( $catList ) {
+ $html = $this->getSidebarChunk( 'catlinks-sidebar', $catHeader, $catList );
+ }
+
+ return $html;
+ }
+
+ /**
+ * List of categories
+ *
+ * @param array $list
+ * @param string $id
+ * @param string|array $message i18n message name or an array of [ message name, params ]
+ *
+ * @return string html
+ */
+ protected function getCatList( $list, $id, $message ) {
+ $html = '';
+
+ $categories = [];
+ // Generate portlet content
+ foreach ( $list as $category ) {
+ $title = Title::makeTitleSafe( NS_CATEGORY, $category );
+ if ( !$title ) {
+ continue;
+ }
+ $categories[ htmlspecialchars( $category ) ] = [ 'links' => [ 0 => [
+ 'href' => $title->getLinkURL(),
+ 'text' => $title->getText()
+ ] ] ];
+ }
+
+ $html .= $this->getPortlet( $id, $categories, $message );
+
+ return $html;
+ }
+
+ /**
+ * Interlanguage links block, also with variants
+ *
+ * @return string html
+ */
+ protected function getInterlanguageLinks() {
+ $html = '';
+
+ if ( isset( $this->data['variant_urls'] ) && $this->data['variant_urls'] !== false ) {
+ $variants = $this->getPortlet( 'variants', $this->data['variant_urls'], true );
+ } else {
+ $variants = '';
+ }
+ if ( $this->data['language_urls'] !== false ) {
+ $html .= $this->getSidebarChunk(
+ 'other-languages',
+ 'timeless-languages',
+ $variants .
+ $this->getPortlet(
+ 'lang',
+ $this->data['language_urls'] ?: [],
+ 'otherlanguages'
+ )
+ );
+ }
+
+ return $html;
+ }
+}