. * * @file * @ingroup Skins */ declare( strict_types=1 ); namespace Citizen\Partials; use Exception; use ExtensionRegistry; use MediaWiki\MediaWikiServices; use SkinTemplate; final class PageTools extends Partial { /** @var null|array for caching purposes */ private $languages; /** * Render page-related tools * TODO: Break this down and clean up when 1.39 * * Possible visibility conditions: * * true: always visible (bool) * * false: never visible (bool) * * 'login': only visible if logged in (string) * * 'permission-*': only visible if user has permission * e.g. permission-edit = only visible if user can edit pages * * @param array $parentData * @return array html */ public function buildPageTools( $parentData ): array { $condition = $this->getConfigValue( 'CitizenShowPageTools' ); $contentNavigation = $this->skin->buildContentNavigationUrlsPublic(); $portals = $this->skin->buildSidebar(); $props = []; // Login-based condition, return true if condition is met if ( $condition === 'login' ) { $condition = $this->skin->getUser()->isRegistered(); } // Permission-based condition, return true if condition is met if ( is_string( $condition ) && strpos( $condition, 'permission' ) === 0 ) { $permission = substr( $condition, 11 ); try { $condition = MediaWikiServices::getInstance()->getPermissionManager()->userCan( $permission, $this->skin->getUser(), $this->skin->getTitle() ); } catch ( Exception $e ) { $condition = false; } } if ( $condition === true ) { if ( !method_exists( SkinTemplate::class, 'runOnSkinTemplateNavigationHooks' ) ) { $viewshtml = $this->skin->getPortletData( 'views', $contentNavigation[ 'views' ] ?? [] ); $actionshtml = $this->skin->getPortletData( 'actions', $contentNavigation[ 'actions' ] ?? [] ); $namespaceshtml = $this->skin->getPortletData( 'namespaces', $contentNavigation[ 'namespaces' ] ?? [] ); $variantshtml = $this->skin->getPortletData( 'variants', $contentNavigation[ 'variants' ] ?? [] ); $toolboxhtml = $this->skin->getPortletData( 'tb', $portals['TOOLBOX'] ?? [] ); $languageshtml = $this->skin->getPortletData( 'lang', $portals['LANGUAGES'] ?? [] ); } else { $viewshtml = $parentData['data-portlets']['data-views']; $actionshtml = $parentData['data-portlets']['data-actions']; $namespaceshtml = $parentData['data-portlets']['data-namespaces']; $variantshtml = $parentData['data-portlets']['data-variants']; // data-languages can be undefined index $languageshtml = $parentData['data-portlets']['data-languages'] ?? []; // For some reason core does not set this if ( empty( $languageshtml ) ) { $languageshtml['is-empty'] = true; } // Finds the toolbox in the sidebar. // The reason we do this is because we removed some site-wide tools from // the toolbar in Drawer.php, now we just want the leftovers $sidebar = [ $parentData['data-portlets-sidebar']['data-portlets-first'], ...$parentData['data-portlets-sidebar']['array-portlets-rest'] ]; $toolboxhtml = [ 'is-empty' => true, ]; foreach ( $sidebar as $portlet ) { if ( $portlet['id'] === 'p-tb' ) { $toolboxhtml = $portlet; $toolboxhtml['is-empty'] = false; break; } } } if ( $viewshtml ) { $viewshtml[ 'label-class' ] ??= ''; $viewshtml[ 'label-class' ] .= 'screen-reader-text'; } if ( $actionshtml ) { $actionshtml[ 'label-class' ] ??= ''; $actionshtml[ 'label-class' ] .= 'screen-reader-text'; } if ( $namespaceshtml ) { $namespaceshtml[ 'label-class' ] ??= ''; $namespaceshtml[ 'label-class' ] .= 'screen-reader-text'; } $props = [ 'data-page-views' => $viewshtml, 'data-page-actions' => $actionshtml, 'data-namespaces' => $namespaceshtml, 'has-languages' => $this->shouldShowLanguages( $languageshtml, $variantshtml ), 'is-uls-ready' => $this->shouldShowULS( $variantshtml ), 'data-languages' => $languageshtml, 'data-variants' => $variantshtml, 'data-page-toolbox' => $toolboxhtml, 'html-language-count' => $this->getLanguagesCount(), ]; } return $props; } /** * Calls getLanguages with caching. * * Based on Vector * * @return array */ private function getLanguagesCached(): array { $skin = $this->skin; if ( $this->languages === null ) { $this->languages = $skin->getLanguages(); } return $this->languages; } /** * This should be upstreamed to the Skin class in core once the logic is finalized. * Returns false if the page is a special page without any languages, or if an action * other than view is being used. * * Based on Vector * * @return bool */ private function canHaveLanguages(): bool { $skin = $this->skin; if ( $skin->getContext()->getActionName() !== 'view' ) { return false; } $title = $skin->getTitle(); // Defensive programming - if a special page has added languages explicitly, best to show it. if ( $title && $title->isSpecialPage() && empty( $this->getLanguagesCached() ) ) { return false; } return true; } /** * Show or hide the language button * * @param array $languageshtml * @param array $variantshtml * @return bool */ private function shouldShowLanguages( $languageshtml, $variantshtml ): bool { if ( !$this->canHaveLanguages() ) { return false; } // If both language and variant menu contains nothing if ( $languageshtml['is-empty'] && $variantshtml['is-empty'] ) { return false; } return true; } /** * Check if UniversalLanguageSelector can be used to replace the language menu * * @param array $variantshtml * @return bool */ private function shouldShowULS( $variantshtml ): bool { // ULS does not support variants if ( !$variantshtml['is-empty'] ) { return false; } // ext.uls.interface only attaches to mw-interlanguage-selector >1.36 if ( version_compare( MW_VERSION, '1.36', '<' ) ) { return false; } return ExtensionRegistry::getInstance()->isLoaded( 'UniversalLanguageSelector' ); } /** * Count languages avaliable * TODO: Consider having an option to count for variants? * * @return int */ private function getLanguagesCount(): int { return count( $this->getLanguagesCached() ); } }