diff --git a/includes/RefreshedTemplate.php b/includes/RefreshedTemplate.php index 90418f2..c89f124 100644 --- a/includes/RefreshedTemplate.php +++ b/includes/RefreshedTemplate.php @@ -1,1047 +1,1114 @@ ' ', 'user-loggedin' => ' ', 'user-anon' => ' ', 'menu' => ' ', 'more' => ' ', 'close' => ' ', 'nstab' => ' ', 'talk' => ' ', 'viewsource' => ' ', 'edit' => ' ', 'addsection' => ' ', 'history' => ' ', 'delete' => ' ', 'undelete' => ' ', 'move' => ' ', 'protect' => ' ', 'unprotect' => ' ', 'watch' => ' ', 'unwatch' => ' ', 'wikilove' => ' ', 'purge' => ' ', 'report-problem' => ' ', 'whatlinkshere' => ' ', 'recentchangeslinked' => ' ', 'contributions' => ' ', 'blockip' => ' ', 'log' => ' ', 'emailuser' => ' ', 'userrights' => ' ', 'upload' => ' ', 'print' => ' ', 'permalink' => ' ', 'info' => ' ', 'smwbrowserlink' => ' ' ]; + /** * Parses MediaWiki:Refreshed-wiki-dropdown. * Forked from Games' parseSidebarMenu(), which in turn was forked from * Monaco's parseSidebarMenu(), but none of these three methods are * identical. * * @param string $messageKey Message name * @return array */ private function parseSiteNavigationDropdownMenu( $messageKey ) { $lines = $this->getLines( $messageKey ); $nodes = []; $i = 0; if ( is_array( $lines ) ) { foreach ( $lines as $line ) { # ignore empty lines if ( strlen( $line ) == 0 ) { continue; } $node = $this->parseSiteNavigationDropdownItem( $line ); for ( $x = $i; $x >= 0; $x-- ) { if ( $x == 0 ) { break; } } $nodes[$i + 1] = $node; $i++; } } return $nodes; } /** * Helper method for parseSiteNavigationDropdownMenu. * Parse one pipe-separated line from MediaWiki message to an array with - * indexes "wikiName" (string), "logoURL" (string|null), "wikiURL" (string|null) + * indexes "wikiName" (string), "logoURL" (string|null), + * "wikiURL" (string|null) * (This array will eventually be used to construct a link in the site * dropdown via renderSiteNavigationDropdownItems.) * Each line follows this format of text seperated by pipe symbols: * name|logo URL|wiki URL. * Special cases: * - If no logo URL is provided (name||wiki URL), 'logoURL' => null. * - If no wiki URL is provided (name|logo URL|badly formed wiki URL, or * name|logo URL|, or name|logo URL), 'wikiURL' => '#'. * - Finally if neither is provided (name or name||) then both of the above * apply. * @param string $line Line (beginning with a *) from a MediaWiki: message * @return array attributes for the resulting link */ public static function parseSiteNavigationDropdownItem( $line ) { - // trim spaces and asterisks from line and then split it to maximum three chunks + // trim spaces and asterisks from line and split it to maximum three chunks $line_temp = explode( '|', trim( $line, '* ' ), 3 ); // Likewise we assume the logoURL will be null and the wiki URL will be #, // but if we find alternatives when parsing, we'll switch to them. $logoURL = null; $wikiURL = '#'; $wikiName = $line_temp[0]; // has logo URL if at least 2 chunks and the 2nd isn't empty if ( count( $line_temp ) >= 2 && $line_temp[1] !== '' ) { $logoURL = trim( $line_temp[1] ); } // get link from third chunk if it exists and is a URL if ( isset( $line_temp[2] ) && preg_match( '/^(?:' . wfUrlProtocols() . ')/', $line_temp[2] ) ) { $wikiURL = $line_temp[2]; } return [ 'wikiName' => $wikiName, 'logoURL' => $logoURL, 'wikiURL' => $wikiURL, ]; } /** * @param string $messageKey Name of a MediaWiki: message * @return array|null Array if $messageKey has been given, otherwise null */ private function getMessageAsArray( $messageKey ) { $messageObj = $this->getSkin()->msg( $messageKey )->inContentLanguage(); if ( !$messageObj->isDisabled() ) { $lines = explode( "\n", $messageObj->text() ); if ( count( $lines ) > 0 ) { return $lines; } } return null; } /** * @param string $messageKey Name of a MediaWiki: message * @return array */ private function getLines( $messageKey ) { $title = Title::newFromText( $messageKey, NS_MEDIAWIKI ); $revision = Revision::newFromTitle( $title ); if ( is_object( $revision ) ) { $contentText = ContentHandler::getContentText( $revision->getContent() ); if ( trim( $contentText ) != '' ) { $temp = $this->getMessageAsArray( $messageKey ); if ( count( $temp ) > 0 ) { wfDebugLog( 'Refreshed', sprintf( 'Get LOCAL %s, which contains %s lines', $messageKey, count( $temp ) ) ); $lines = $temp; } } } if ( empty( $lines ) ) { $lines = $this->getMessageAsArray( $messageKey ); - // if $lines isn't countable, should log a different debug message that does not include count( $lines ) - // since in PHP 7.2 and beyond, counting non-countable objects prompts a warning that will break the - // page + // if $lines isn't countable, should log a different debug message that + // does not include count( $lines ) since in PHP 7.2 and beyond, counting + // non-countable objects prompts a warning that will break the page if ( is_array( $lines ) || $lines instanceof Countable ) { wfDebugLog( 'Refreshed', sprintf( 'Get %s, which contains %s lines', $messageKey, count( $lines ) ) ); } else { wfDebugLog( 'Refreshed', sprintf( 'Get %s, which is empty', $messageKey ) ); } } return $lines; } /** * Return an inline SVG containing the inputted icon, as a string. * @param string|null $iconName string or null if no icon * @return string */ private function makeIcon( $iconName ) { // print_r($iconList); // return null if $iconName isn't a string or is the empty string if ( !is_string( $iconName ) || $iconName === '' ) { return ''; } // Sometimes $iconName may be of the form "nstab-something" if it represents // an article button (like "user page"). In this case, there are many // possible suffixes like "-user", "-project", etc. We can't possibly // predict all those suffixes since some of them may represent namespaces // that one wiki in particular has defined. As such, we will strip the // suffix to leave just "nstab" for every namespace. That way article // buttons always use the same icon. if ( strpos( $iconName, 'nstab' ) === 0 ) { $iconName = 'nstab'; } if ( array_key_exists( $iconName, self::$iconList ) ) { return self::$iconList[$iconName]; } return ''; } /** * Render an inline SVG containing the inputted icon to the page. * @param string|null $iconName string or null if no icon * @return string */ private function renderIcon( $iconName ) { echo $this->makeIcon( $iconName ); } /** * Generate a list item using BaseTemplate::makeListItem() that contains the * inline SVG icon specified by $iconName just before the actual link text, * assuming $iconName is specified. * (If the icon name isn't recognized, or the list item or icon HTML can't * be parsed for whatever reason, the list item is returned without * adding the icon.) * @param string $iconName the name of the icon - * @param string $key the "$key" for the standard makeLink/makeListItem (see docs) - * @param array $item the "$item" for the standard makeLink/makeListItem (see docs) - * @param array $options the "$options" for the standard makeLink/makeListItem (see docs); optional + * @param string $key the "$key" for the standard makeLink/makeListItem + * (see docs) + * @param array $item the "$item" for the standard makeLink/makeListItem + * (see docs) + * @param array $options the "$options" for the standard makeLink/makeListItem + * (see docs); optional * @return string string representing the list item */ private function makeListItemWithIcon( $iconName = '', $key, $item, $options = [] ) { return $this->makeElementWithIconHelper( 'list item', $iconName, $key, $item, $options ); } /** * Generate a link using BaseTemplate::makeLink() that contains the * inline SVG icon specified by $iconName just before the actual link text, * assuming $iconName is specified. * (If the icon name isn't recognized, or the link or icon HTML can't * be parsed for whatever reason, the link is returned without * adding the icon.) * @param string $iconName the name of the icon - * @param string $key the "$key" for the standard makeLink/makeListItem (see docs) - * @param array $item the "$item" for the standard makeLink/makeListItem (see docs) - * @param array $options the "$options" for the standard makeLink/makeListItem (see docs); optional + * @param string $key the "$key" for the standard makeLink/makeListItem + * (see docs) + * @param array $item the "$item" for the standard makeLink/makeListItem + * (see docs) + * @param array $options the "$options" for the standard makeLink/makeListItem + * (see docs); optional * @return string string representing the link */ private function makeLinkWithIcon( $iconName = '', $key, $item, $options = [] ) { return $this->makeElementWithIconHelper( 'link', $iconName, $key, $item, $options ); } /** * Helper method for makeListItemWithIcon and makeLinkWithIcon. * * Depending on $mode, generate a) a list item containing a link using * BaseTemplate::makeListItem() or b) just a link using * BaseTemplate::makeLink(). Before the actual link text, there is the inline * SVG icon specified by $iconName, assuming $iconName is specified. * (If the icon name isn't recognized, or the list item/link or icon HTML * can't be parsed for whatever reason, the list item/link is returned without * adding the icon.) * @param string $mode Expects either 'list item' or 'link' * @param string $iconName the name of the icon - * @param string $key the "$key" for the standard makeLink/makeListItem (see docs) - * @param array $item the "$item" for the standard makeLink/makeListItem (see docs) - * @param array $options the "$options" for the standard makeLink/makeListItem (see docs); optional + * @param string $key the "$key" for the standard makeLink/makeListItem + * (see docs) + * @param array $item the "$item" for the standard makeLink/makeListItem + * (see docs) + * @param array $options the "$options" for the standard makeLink/makeListItem + * (see docs); optional * @return string string representing the list item/link */ private function makeElementWithIconHelper( $mode, $iconName, $key, $item, $options ) { // Based on the $mode, either make a list item or link without any icon // added yet. If the $mode is invalid, return null. if ( $mode === 'list item' ) { $outputUnedited = $this->makeListItem( $key, $item, $options ); } elseif ( $mode === 'link' ) { $outputUnedited = $this->makeLink( $key, $item, $options ); } // Get the HTML of the icon we want to add (returns empty string if no icon) $icon = $this->makeIcon( $iconName ); // if there is no icon to add, don't bother doing more processing; just // return the list item/link without the icon if ( $icon === '' ) { return $outputUnedited; } // Now we know there actually is an icon we want to insert. We want to find // where it belongs. To do this, we will parse the HTML of the list item/ // link. // (As of MW 1.31) BaseTemplate::makeListItem and BaseTemplate::makeLink // allow angle brackets in attributes. In case this (or something else) // breaks the HTML parser, rather than deal with the breaking, we will just // not add images/icons in that case. // (two separate DOMs: one for the list item/link, one for the icon) $listItemOrLinkDOM = $this->loadHTMLHandleErrors( $outputUnedited ); $iconDOM = $this->loadHTMLHandleErrors( $icon ); if ( !$listItemOrLinkDOM || !$iconDOM ) { return $outputUnedited; } // otherwise insert the icon into our list item/link // (read variable names for explanation of what's going on below) $xpath = new DOMXPath( $listItemOrLinkDOM ); // Find the first a tag in the link. We know such a tag exists because one // is produced by makeListItem or makeLink, which we haven't modified. $firstATagInListItemOrLink = $xpath->query( '(//a)[1]' )->item( 0 ); // Find the child of the first a tag from the last line. Note this may not // exist (if a tag is empty), in which case it's null. $firstATagChild = $firstATagInListItemOrLink->firstChild; $iconInIconDOM = $iconDOM->documentElement; // Currently the icon is in the iconDOM. We have to put a copy of it in // $listItemOrLinkDOM so we can add the icon to the list item/link $iconInListItemOrLinkDOM = $listItemOrLinkDOM->importNode( $iconInIconDOM, true ); // add the icon to the very beginning of the first a tag if ( $firstATagChild === null ) { $firstATagInListItemOrLink->appendChild( $iconInListItemOrLinkDOM ); } else { $firstATagInListItemOrLink->insertBefore( $iconInListItemOrLinkDOM, $firstATagChild ); } return $listItemOrLinkDOM->saveHTML(); } /** * Helper method for makeElementWithIconHelper. * Given $text, load it into a DOMDocument as HTML. If all goes as planned * (the input doesn't break the parser), return the resulting DOMDocument. * Otherwise, echo errors and return false. - * @param string $text the text to interpret as HTML (shouldn't contain html or body tags) + * @param string $text the text to interpret as HTML (shouldn't contain html + * or body tags) * @return DOMDocument|bool DOMDocument if no errors, otherwise false */ private function loadHTMLHandleErrors( $text ) { // error handling per https://secure.php.net/manual/en/simplexml.examples-errors.php $doc = new DOMDocument(); // config doesn't include doctype, html, or body tags per // https://stackoverflow.com/a/22490902 $html = $doc->loadHTML( $text, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); if ( $html === false ) { foreach ( libxml_get_errors() as $error ) { echo "\n", $error->message; } return false; } return $doc; } /** * Return the user's avatar element as a string (if using SocialProfile). * Otherwise, return the appropriate placeholder element as a string. * @param User $user * @return string */ private function makeAvatar( $user ) { // if using SocialProfile (logged in or not), return SocialProfile avatar if ( class_exists( 'wAvatar' ) ) { $avatar = new wAvatar( $user->getId(), 'l' ); return $avatar->makeAvatarURL( [ 'class' => 'avatar avatar-image' ] ); } elseif ( $this->data['loggedin'] ) { // if no SocialProfile and user is logged in... // if wiki has not set custom image for logged in users... if ( $this->getMsg( 'refreshed-icon-logged-in' )->isDisabled() ) { return $this->makeIcon( 'user-loggedin' ); } else { // if wiki has set custom image for logged in users... return Html::element( 'img', [ 'src' => $this->getMsg( 'refreshed-icon-logged-in' )->escaped(), 'class' => 'avatar avatar-no-socialprofile avatar-image' ] ); } } else { // if no SocialProfile and user is not logged in... // if wiki has not set a custom image for logged out users if ( $this->getMsg( 'refreshed-icon-logged-out' )->isDisabled() ) { return $this->makeIcon( 'user-anon' ); } else { // if wiki has set custom image for logged out users return Html::element( 'img', [ 'src' => $this->getMsg( 'refreshed-icon-logged-out' )->escaped(), 'class' => 'avatar avatar-no-socialprofile avatar-image' ] ); } } } /** * Get the username text (string) to be displayed in the header. * @param User $user * @return string */ private function makeUsernameText( $user ) { // if logged in... if ( $this->data['loggedin'] ) { return $user->getName(); } // if not logged in... return $this->getMsg( 'login' )->text(); } + /** + * Get the personal tools and rearrange them into "dropdown" and "extra" + * tools. The "dropdown" tools are the ones that should go into the user info + * dropdown, and the "extra" tools (like Echo ones) are ones that should be + * placed next to the user dropdown. + * Inspired by and partially adapted from the Timeless skin's getUserLinks + * function. + * @return array $rearrangedPersonalTools where the key "dropdown" contains + * the dropdown tools, and the key "extra" contains the extra tools. + */ + private function getAndRearrangePersonalTools() { + $dropdownTools = $this->getPersonalTools(); + $extraTools = []; + // list of tool names that should be removed from the dropdown tools and be + // added to the extra tools + // (these tools are echo badges) + $toolsToMove = [ 'notifications-alert', 'notifications-notice' ]; + + foreach ( $toolsToMove as $currentToolToMove ) { + if ( isset( $dropdownTools[$currentToolToMove] ) ) { + $extraTools[$currentToolToMove] = $dropdownTools[$currentToolToMove]; + unset( $dropdownTools[$currentToolToMove] ); + } + } + return [ 'dropdown' => $dropdownTools, 'extra' => $extraTools ]; + } + + /** + * Render the list items to be displayed next to the user dropdown + * (e.g., for Echo). + * Inspired by how Timeless handles Echo. + * @param array $extraPersonalTools + */ + private function renderExtraPersonalTools( $extraPersonalTools ) { + foreach ( $extraPersonalTools as $key => $item ) { + echo $this->makeListItem( $key, $item ); + } + } /** * Render the list items to be displayed in the header's user dropdown. + * @param array $dropdownPersonalTools */ - private function renderUserDropdownItems() { - foreach ( $this->getPersonalTools() as $keyAndIconName => $item ) { + private function renderUserDropdownItems( $dropdownPersonalTools ) { + foreach ( $dropdownPersonalTools as $keyAndIconName => $item ) { echo $this->makeListItemWithIcon( $keyAndIconName, $keyAndIconName, $item ); } } + /** * Render the items of the site navigation dropdown to appear in the header. - * @param array $siteNavigationDropdown an array containing info for the site navigation dropdown + * @param array $siteNavigationDropdown an array containing info for the site + * navigation dropdown */ private function renderSiteNavigationDropdownItems( $siteNavigationDropdown ) { // (each item in $siteNavigationDropdown was an output of // parseSiteNavigationDropdownItem) // we're making a bunch of list items here (
  • elements, but NOT ones // created via makeListItem or makeListItemWithIcon...) // the classes to add to each of the dropdown anchors $classList = 'refreshed-logo refreshed-logo-other'; foreach ( $siteNavigationDropdown as $wikiLogoInfo ) { // send each of the parsed pieces of wiki logo info to renderWikiLogo // for rendering echo Html::rawElement( 'li', [ 'class' => 'refreshed-dropdown-item header-dropdown-item site-navigation-dropdown-item' ], $this->makeWikiLinkWithLogo( $wikiLogoInfo['wikiName'], $wikiLogoInfo['logoURL'], $wikiLogoInfo['wikiURL'], $classList ) ); } } /** * Render the items of the header category dropdown to appear in the header. - * @param array $headerCategoryDropdown an array containing info for a header category dropdown + * @param array $headerCategoryDropdown an array containing info for a header + * category dropdown */ private function renderHeaderCategoryDropdownItems( $headerCategoryDropdown ) { foreach ( $headerCategoryDropdown as $key => $value ) { echo Html::rawElement( 'li', [ 'class' => 'refreshed-dropdown-item header-dropdown-item header-category-dropdown-item' ], $this->makeLink( $key, $value ) ); } } /** * Output as a string an anchor for a wiki, with the wiki's logo inside. * @param string $wikiName the wiki's name - * @param string|null $logoURL URL to the wiki's logo image (if null, render logo as text) + * @param string|null $logoURL URL to the wiki's logo image (if null, render + * logo as text) * @param string $wikiURL the URL the anchor goes to - * @param string $classList a list of the classes to add to the outputted anchor element - * @param string $wikiTitle (optional) text to use as the anchor's title attribute instead of $wikiName + * @param string $classList a list of the classes to add to the outputted + * anchor element + * @param string $wikiTitle (optional) text to use as the anchor's title + * attribute instead of $wikiName * @return string HTML of the logo anchor */ private function makeWikiLinkWithLogo( $wikiName, $logoURL, $wikiURL, $classList, $wikiTitle = '' ) { $anchorAttribs = [ 'href' => $wikiURL, 'title' => $wikiTitle !== '' ? $wikiTitle : $wikiName, 'class' => $classList ]; // If wikiURL is null, we're making a text logo. Otherwise, we're making an // image logo. if ( $logoURL === null ) { return Html::element( 'a', $anchorAttribs, $wikiName ); } else { $image = Html::element( 'img', [ 'src' => $logoURL, 'alt' => $wikiName, ] ); return Html::rawElement( 'a', $anchorAttribs, $image ); } } public function execute() { global $wgMemc; $skin = $this->getSkin(); $config = $skin->getConfig(); $user = $skin->getUser(); // Title processing $titleBase = $skin->getTitle(); $title = $titleBase->getSubjectPage(); $titleNamespace = $titleBase->getNamespace(); $key = $wgMemc->makeKey( 'refreshed', 'header' ); $headerCategoriesDropdowns = $wgMemc->get( $key ); if ( !$headerCategoriesDropdowns ) { $headerCategoriesDropdowns = []; $skin->addToSidebar( $headerCategoriesDropdowns, 'refreshed-navigation' ); $wgMemc->set( $key, $headerCategoriesDropdowns, 60 * 60 * 24 ); // 24 hours } $dropdownCacheKey = $wgMemc->makeKey( 'refreshed', 'dropdownmenu' ); $siteNavigationDropdown = $wgMemc->get( $dropdownCacheKey ); if ( !$siteNavigationDropdown ) { $siteNavigationDropdown = $this->parseSiteNavigationDropdownMenu( 'Refreshed-wiki-dropdown' ); $wgMemc->set( $dropdownCacheKey, $siteNavigationDropdown, 60 * 60 * 24 ); // 24 hours } // url to this wiki's homepage/page you visit when logo is clicked; // to be used with renderCurrentWikiLogoAndLink $thisWikiURLMsg = $skin->msg( 'refreshed-this-wiki-url' ); if ( $thisWikiURLMsg->isDisabled() ) { $thisWikiURL = htmlspecialchars( Title::newMainPage()->getFullURL() ); } else { $thisWikiURL = $skin->msg( 'refreshed-this-wiki-url' )->escaped(); } // url to this wiki's logo image (or null if no such image); // to be used with renderCurrentWikiLogoAndLink $thisLogoURLMsg = $skin->msg( 'refreshed-this-wiki-wordmark' ); if ( $thisLogoURLMsg->isDisabled() ) { $thisLogoURL = null; } else { $thisLogoURL = $skin->msg( 'refreshed-this-wiki-wordmark' )->escaped(); } // this wiki's name; to be used with renderCurrentWikiLogoAndLink $thisWikiName = $config->get( 'Sitename' ); // anchor containing this wiki's logo $thisWikiLinkWithLogo = $this->makeWikiLinkWithLogo( $thisWikiName, $thisLogoURL, $thisWikiURL, 'refreshed-logo refreshed-logo-current main header-button', $skin->msg( 'Tooltip-p-logo' ) ); $thisWikiLinkWithSidebarLogo = $this->makeWikiLinkWithLogo( $thisWikiName, $thisLogoURL, $thisWikiURL, 'refreshed-logo refreshed-logo-current main', $skin->msg( 'Tooltip-p-logo' ) ); $thisWikiMobileLogo = $skin->msg( 'refreshed-this-wiki-mobile-logo' ); $thisWikiMobileLogoImgElement = ''; if ( !$thisWikiMobileLogo->isDisabled() ) { $thisWikiMobileLogoImgElement = Html::element( 'img', [ 'src' => $thisWikiMobileLogo->escaped(), 'alt' => $config->get( 'Sitename' ), 'class' => 'refreshed-logo' ] ); } - // allow error handling in makeElementWithIconHelper: see https://secure.php.net/manual/en/simplexml.examples-errors.php + $personalTools = $this->getAndRearrangePersonalTools(); + $dropdownPersonalTools = $personalTools['dropdown']; + $extraPersonalTools = $personalTools['extra']; + + // allow error handling in makeElementWithIconHelper: + // see https://secure.php.net/manual/en/simplexml.examples-errors.php libxml_use_internal_errors( true ); // Output the tag and whatnot $this->html( 'headelement' ); ?>
    isDisabled() ) { // if a mobile logo has been defined ?>
    -
    - - makeAvatar( $user ) ?> - makeUsernameText( $user ) ?> - renderIcon( 'dropdown-expand' ) ?> -
    -
    -
      - renderUserDropdownItems() ?> -
    +
    + +
    +
      + renderExtraPersonalTools( $extraPersonalTools ) ?> +
    +
    + +
    $headerCategoryDropdown ) { ?>
    renderIcon( 'dropdown-expand' ) ?>
      renderHeaderCategoryDropdownItems( $headerCategoryDropdown ); ?>
    - isLoaded( 'Echo' ) ) { - ?> -
    -
    data['sitenotice'] ) { ?> data['newtalk'] ) { ?>
    html( 'newtalk' ) ?>
    getIndicators(); } ?>

    html( 'title' ) ?>

    msg( 'tagline' ) ?>
    data['subtitle'] || $this->data['undelete'] ) { ?>
    html( 'userlangattributes' ) ?>>html( 'subtitle' ) ?>html( 'undelete' ) ?>
    data['content_actions'] ); $pageTab = key( $this->data['content_actions'] ); $isEditing = in_array( $skin->getRequest()->getText( 'action' ), [ 'edit', 'submit' ] ); // determining how many tools need to be generated $totalSmallToolsToGenerate = 0; $listOfToolsToGenerate = [ 'wikiglyph wikiglyph-speech-bubbles' => 'ca-talk', 'wikiglyph wikiglyph-pencil-lock-full' => 'ca-viewsource', 'wikiglyph wikiglyph-pencil' => 'ca-edit', 'wikiglyph wikiglyph-clock' => 'ca-history', 'wikiglyph wikiglyph-trash' => 'ca-delete', 'wikiglyph wikiglyph-move' => 'ca-move', 'wikiglyph wikiglyph-lock' => 'ca-protect', 'wikiglyph wikiglyph-unlock' => 'ca-unprotect', 'wikiglyph wikiglyph-star' => 'ca-watch', 'wikiglyph wikiglyph-unstar' => 'ca-unwatch' ]; foreach ( $this->data['content_actions'] as $action ) { if ( in_array( $action['id'], $listOfToolsToGenerate ) ) { // if the icon in question is one of the listed ones $totalSmallToolsToGenerate++; } } if ( MWNamespace::isTalk( $titleNamespace ) ) { // if talk namespace $totalSmallToolsToGenerate--; // remove a tool (the talk page tool) if the user is on a talk page } if ( $totalSmallToolsToGenerate > 0 && !$isEditing ) { // if there's more than zero tools to be generated and the user isn't editing a page ?>
    data['content_actions'] as $action ) { if ( $smallToolBeingTested > $amountOfSmallToolsToSkipInFront ) { // if we're not supposed to skip this tool (e.g. if we're supposed to skip the first 2 tools and we're at the 3rd tool, then the boolean is true) // @todo Maybe write a custom makeLink()-like function for generating this code? if ( in_array( $action['id'], $listOfToolsToGenerate ) ) { // if the icon being rendered is one of the listed ones (if we're supposed to generate this tool) ?>
    3 ) { ?>
    html( 'bodytext' ) ?>
    html( 'catlinks' ); if ( $this->data['dataAfterContent'] ) { $this->html( 'dataAfterContent' ); } ?>
    printTrail(); echo Html::closeElement( 'body' ); echo Html::closeElement( 'html' ); } } diff --git a/refreshed/styles/screen/main.css b/refreshed/styles/screen/main.css index 2857b89..1dbb7a2 100644 --- a/refreshed/styles/screen/main.css +++ b/refreshed/styles/screen/main.css @@ -1,770 +1,791 @@ .refreshed-menu-collapsible { -ms-transition: max-height 0.4s ease; transition: max-height 0.4s ease; /*max-height: 50vh; /* capping the sidebar height at half the height of the window */ visibility: visible; overflow: hidden; height: auto; } .refreshed-menu-collapsed { -ms-transition: max-height 0.2s ease, visibility 0.2s; transition: max-height 0.2s ease, visibility 0.2s; max-height: 0 !important; visibility: hidden; overflow: hidden; } .fadable { -ms-transition: opacity 0.2s ease; transition: opacity 0.2s ease; opacity: 1; visibility: visible; } .faded { -ms-transition: opacity 0.2s ease, visibility 0.2s; transition: opacity 0.2s ease, visibility 0.2s; opacity: 0; visibility: hidden; } body { font-family: "Lato", sans-serif; margin: 0; background-color: #194a8d; -webkit-tap-highlight-color: transparent; /* preventing gray overlay when pressed on mobile webkit browsers (e.g. mobile Safari) */ } #fade-overlay { width: 100%; height: 100%; position: fixed; top: 0; left: 0; display: block; opacity: 0; -ms-transition: opacity 0.2s ease; transition: opacity 0.2s ease; background: #fff; } .fade-overlay-active { opacity: 0.5 !important; z-index: 10001; /* very high value to guarantee no elements on wikis will appear above it (except the sidebar) */ cursor: pointer; } .fade-overlay-triggered-by-search { z-index: 10000 !important; /* below header but above page content (used for search) */ cursor: pointer; } #header-wrapper { -webkit-font-smoothing: antialiased; width: 100%; height: 3em; line-height: 3em; z-index: 10000; /* very high value to guarantee no elements on wikis will appear above it */ position: fixed; background-color: #103ca2; -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr=#040f28,EndColorStr=#103ca2)"; /* IE9+ */ background-image: linear-gradient(to left, #040f28 0, #103ca2 50%, #040f28 100%); display: flex; flex-direction: row; justify-content: space-between; } #header-wrapper img { margin-bottom: 0.3em; /* images aren't perfectly vertically centered, this corrects it. Better fix in future? */ } .header-button { /* defined at top so individual elements can override these properties */ cursor: pointer; font-family: "Lato Body", sans-serif; color: #fff; display: inline-block; text-align: center; } a.header-button { /* double selectors to override default a element text-decoration without use of !important */ text-decoration: none; } .header-dropdown-item a { color: #fff; } .header-dropdown-item a.new { color: #fdad9d; } .header-button:hover, .header-button-active, .header-dropdown-item:hover { /* defined at bottom so this overrides individual elements' background-color properties */ background-color: midnightblue; } -.header-section { +.header-section, +.header-category-dropdown, +#user-info-dropdown, +#extra-personal-tools { display: inline-block; /* sections don't fill up whole header width, blocking each other */ } .header-button .wikiglyph-user-sleep, .header-button .wikiglyph-user-smile, .header-button .wikiglyph-magnifying-glass { display: inline-block; vertical-align: middle; font-size: 2em; line-height: 1em; /* That is, the line-height is the font size. The line-height and the vertical-align together vertically center the icon. */ } .no-show { display: none; } .header-menu { padding: 0 0.2em 0.2em 0.2em; margin: 0; z-index: 4; list-style: none; background-color: #000; background-color: rgba(0, 0, 0, 0.8); position: absolute; line-height: 1em; } .header-menu a { font-size: 1.125em; text-decoration: none; display: block; padding: 0.2em 0.4em 0.2em 0.4em; } #header-categories-user-info-search-wrapper { display: flex; flex-direction: row-reverse; /* when only one item inside (#user-info-search-wrapper), push it to the right side (LTR) or left (RTL) */ justify-content: space-between; width: 100%; /* fill all the space not occupied by #site-info */ } #user-info-search-wrapper { flex-shrink: 0; /* when this user info hits the header categories, the header categories should shrink, but the user info shouldn't */ } +#extra-personal-tools-tray { + margin: 0; + display: inline-block; +} + +#extra-personal-tools-tray li { + display: inline-block; + margin: 0 0.75em 0 0; /* adapted from Timeless's #personal-extra li rule */ +} + +#pt-notifications-notice, +#pt-notifications-alert { + vertical-align: middle; /* (more or less) center the echo badges */ +} + #user-info .header-menu { right: 0; width: 11em; } #user-info .header-menu a { padding-right: 0.7em; text-align: right; } .sidebar-shower { position: absolute; top: 0; right: -3em; background-color: #194a8d; display: none; -ms-transition: right 0.2s ease 0s; transition: right 0.2s ease 0s; } #site-info .header-menu { width: 11.6em; } #site-info-main { float: left; width: 12em; } #site-info-main.multiple-wikis .main { width: 10em; float: left; /* prevent arrow from being bumped down */ } #site-info-main a.main { width: 12em; color: #fff; font-weight: bold; } #site-info-main.multiple-wikis .header-menu { text-align: center; } #site-info-mobile { display: none; height: 2.75em; width: 2.75em; margin-left: 3em; /* 2.75em for sidebar opener, 0.25em of padding */ padding: 0 0.25em; } #site-info-mobile .header-button { display: block; } .search-shower { position: relative; float: right; text-align: center; display: none; } #header-wrapper .search { - width: 11em; + width: 15em; /* same as #searchInput */ height: 2em; display: inline-block; float: right; padding-right: 1em; padding-left: 0.5em; } #header-wrapper .search-form { height: 100%; } .search-closer { position: absolute; top: 0; right: 0; display: none; z-index: 1; } #searchInput { border: none; font-size: 1em; padding: 0.25em; font-family: "Lato", sans-serif; box-sizing: border-box; -webkit-appearance: none; /* prevent Safari from styling input box */ border-radius: 0; /* prevent iOS from adding rounded corners to input box */ - width: 11em; + width: 15em; /* same as #header-wrapper .search */ height: 100%; } #searchInput::-webkit-input-placeholder { color: #808080; font-weight: 700; } #searchInput::-moz-placeholder { color: #808080; font-weight: 700; } #searchInput::-ms-input-placeholder { color: #808080; font-weight: 700; } #header-categories { position: relative; height: 100%; } /* Dummy wrapper so the header categories experience overflow-x: hidden and overflow-y: visible. Inspired by https://stackoverflow.com/a/29687454. Since .refreshed-header-category-dropdown-tray has position: absolute, it'll pop out of the overflow: hidden. Since .refreshed-dropdown does not have position: absolute, it will stay hidden if the screen is too narrow and it wraps out of the header. */ #header-categories-overflow-wrapper { overflow: hidden; height: 100%; } -.header-category-dropdown { - display: inline-block; /* so header category dropdowns don't fill up the whole space in #header-categories */ -} - .page-item { display: inline-block; } #header-categories .header-button, #user-info .header-button { padding-left: 0.5em; padding-right: 0.5em; } #header-categories a:hover, #header-categories a:focus { text-decoration: none; } #header-categories li img { margin: 0.5em 3px 4px; } #header-categories .header-menu { width: 10em; } #content-heading { /* padding-left/right: 1em (#bodyContent padding-left) * 0.9em (#bodyContent font-size); padding-bottom: 0.25em (padding on top of .standard-toolbox) + 1.5em (height of .standard-toolbox) + 0.75em (padding on bottom of .standard-toolbox) */ padding: 0.5em 0.9em 2.5em 0.9em; position: relative; } #firstHeading { font-size: 2.68em; font-family: "Lato Body", sans-serif; padding: 0; margin-bottom: 0; border: 0; } .mw-indicators { float: right; } .mw-indicator { display: inline; } .scroll-shadow { background: none; } .standard-toolbox { font-family: "Lato", sans-serif; /* assigning here so em values match em values of the links inside */ height: 1.5em; /* 1em for the text, 0.25em for text padding-top, 0.25em for text padding-bottom; meanwhile the links take up 1.75em when you include their 0.25em border-bottom */ position: absolute; bottom: 0.75em; } .toolbox-link { cursor: pointer; /* doesn't have href so must be explicitly defined */ } .standard-toolbox a { text-transform: lowercase; font-weight: normal; text-decoration: none; } .standard-toolbox > a, .standard-toolbox .toolbox-link { /* page action links outside of the dropdown and the "more..."/"tools" button */ margin-left: 0.5em; padding-top: 0.25em; padding-bottom: 0.25em; } .standard-toolbox > a:first-of-type { margin-left: 0; } .standard-toolbox > a:hover, .standard-toolbox > a.selected, .standard-toolbox .toolbox-link:hover { border-bottom: 0.25em solid; } .refreshed-icon { width: auto; height: 1em; fill: currentColor; vertical-align: text-bottom; } .avatar { width: 30px; height: auto; vertical-align: middle; } .avatar-image { padding-left: 0.25em; padding-right: 0.25em; /* this class is added to image avatars because they should have padding but the default WikiFont ones shouldn't */ } .refreshed-icon-menu { width: 1.5em; height: auto; vertical-align: top; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .standard-toolbox-dropdown { left: 0.5em; z-index: 3; background: #fff; position: absolute; width: 12em; box-shadow: 0 3px 9px 0 rgba(75, 75, 75, 0.4); border: 1px solid #ddd; margin-top: 0.25em; } .standard-toolbox-dropdown ul { list-style-type: none; margin-left: 0; margin-top: 0.25em; margin-bottom: 0.25em; } .dropdown-triangle { width: 0; height: 0; border-left: 0.5em solid transparent; border-right: 0.5em solid transparent; border-bottom: 0.5em solid #ccc; position: absolute; top: -.5em; left: 0.1em; } .standard-toolbox-dropdown a { width: 100%; height: 100%; display: inline-block; padding-left: 0.25em; padding-top: 0.1em; padding-bottom: 0.1em; box-sizing: border-box; /* padding/border should be included within the 100% width */ } .standard-toolbox-dropdown a:hover { padding-left: 0; border-left: 0.25em solid; background-color: #eee; } .standard-toolbox-dropdown .toolbox-item-text { display: inline-block; margin-left: 2.5em; text-indent: -1em; /* pulls first line of text 1em to the left, so if you add 1em of padding to the right subsequent lines will look indented */ } .standard-toolbox-dropdown a:before { position: absolute; } .standard-toolbox .toolbox-dropdown-page-action + .toolbox-dropdown-tool { /* first tool in the dropdown that is after a page action (so if no page actions are in the dropdown, this CSS doesn't target anything) */ margin-top: .25em; padding-top: .25em; border-top: 1px solid #ccc; /* divide the tools from the page actions */ } .fixed-toolbox { padding-left: 0.9em; /* 1em (#bodyContent padding-left) * 0.9em (#bodyContent font-size) */ position: fixed; border-bottom: 0.25em solid #eee; background: #fff; top: 3em; /* height of #header */ left: 12em; /* margin-left of #content-wrapper */ right: 1em; /* margin-right of #content-wrapper */ z-index: 9999; /* very high value to guarantee no elements on wikis will appear above it */ } .dropdown-open { -ms-transform: translateY(3em); transform: translateY(3em); } #main-title-messages { border-bottom: 1px solid #ddd; } #back-to-subject { display: none; font-size: 1.25em; padding-bottom: 0.25em; } #small-toolbox-wrapper { display: none; } #site-notice { width: 100%; text-align: center; } #new-talk { margin-left: 2em; } #content-wrapper { min-height: 100%; position: relative; top: 3em; background-color: #fff; z-index: 1; font-family: sans-serif; border-bottom: 0.25em solid #eee; padding-bottom: 1em; } #content-wrapper * { -webkit-tap-highlight-color: rgba(0, 0, 0, 0.4); } #bodyContent { padding: 0 1em; line-height: 1.5em; font-size: 0.875em; word-wrap: break-word; font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; color: #252525; overflow: auto; } #sidebar-wrapper { width: 12em; position: fixed; top: 0; color: #fff; z-index: 2; height: 100%; } /* * Hide the list bullet icons. This is not terribly useful by default, but there * are extensions which can add entire portlets to the sidebarwrapper, and said portlets * might be using the