Extension:CSS MenuSidebar

From MediaWiki.org
Jump to navigation Jump to search
MediaWiki extensions manual
OOjs UI icon advanced.svg
MenuSidebar
Release status: beta
Extension MenuSidebar screenshot.jpg
Implementation User interface
Description menu with dropdown in the sidebar
Author(s) Wolfgang Stöttinger (w.stoettingertalk)
Latest version 0.4.1 (2009-07-15)
MediaWiki 1.14+
License GPL
Download below
$wgParseListItems
Translate the CSS MenuSidebar extension if it is available at translatewiki.net
Check usage and version matrix.

This extension makes it possible to create additional sidebar elements as a menus with dropdown items.

This extension is inspired by Extension:CSS Dropdowns, and designed for MediaWiki 1.14 but should also work for later and some earlier versions. Please try it out and post your experiences here!

Download and Installation[edit]

Please copy the code found below and place it in $IP/extensions/MenuSidebar/MenuSidebar.php. Note: $IP stands for the root directory of your MediaWiki installation, the same directory that holds LocalSettings.php.

Paste some menu structure in the MediaWiki:MenuSidebar. The basic behaviour is similar to the MediaWiki:Sidebar with three major differences:

  1. You can also use text-only entrys (no links), which is useful for menus with sub-menus
  2. You can switch on the parser to parse every list item in your menu. This results in a different syntax for all entrys (e.g. [[ and ]] around links) see Configuration
  3. Use "-" as list item to create a separator

below is an Example for the MediaWiki:MenuSidebar

Then copy the CSS found below and place it in your MediaWiki:Common.css. Be aware, that you have to change the css and put the titles of your menus instead of the example menu titles (#p-MyMenu instead of #p-Menu ...). I optimized the CSS with firefox 3.0, it also works on IE 8 and I tried to add some extra CSS for IE 7 but it's not what I would call perfect ;)

To activate the extension, add the following to LocalSettings.php:

require_once("$IP/extensions/MenuSidebar/MenuSidebar.php");

Note: you may need to clear cache first before you can see the effect, if you have set $wgEnableSidebarCache = true; on the wiki.

Configuration[edit]

You can set the following parameter in your LocalSettings.php to enable the Parser for the individual list items in the menu. This enables you to put links to files etc. Be aware that you have to use a different syntax in the MediaWiki:MenuSidebar $wgParseListItems = true;

Code[edit]

MenuSidebar.php[edit]

<?php
/**
 * Changes in 0.2:
 *  * Use "-" as list item to create a separator
 * Changes in 0.3:
 *  * Added CSS class item# and even/odd to list items to make styling easyer
 * Changes in 0.4:
 *  * Added a small arrow ">" for each menu (with subitems) displayed on the right.
  * Changes in 0.4.1:
 *  * BUGFIX: last </li> was missing
 */
if (!defined('MEDIAWIKI')) {
    exit(1) ;
}
$wgExtensionCredits['other'][] = array(
    'name' => 'MenuSidebar',
    'version' => '0.4.1',
    'author' => 'Wolfgang St&ouml;ttinger',
    'description' => 'Sidebar can be displayed as a Menu',
    'url'     => 'http://www.mediawiki.org/wiki/Extension:CSS_MenuSidebar',
);

$wgHooks['SkinBuildSidebar'][] = 'fnMenuSidebar';

/* If this is set to true, each ListItem is parsed by the MediaWiki parser 
 * which allows more flexible inclusion of MediaWiki content e.g links to files. 
 * If set to false, a similar behaviour to the normal SideBar is used.
 * 
 * Be careful when using this!
 */
$wgParseListItems = false;

function fnMenuSidebar($skin, &$bar) {
	global $wgParser, $wgUser, $wgTitle, $wgParseListItems;
	
	//wfProfileIn( __METHOD__ );
		
	$title = Title::newFromText("MenuSidebar",NS_MEDIAWIKI);	
	/** Use the revision directly to prevent other hooks to be called */
	$rev = Revision::newFromTitle( $title );
    $content = $rev->getContent( Revision::RAW );
    $text = ContentHandler::getContentText( $content );

    if ($rev)
		$lines = explode("\n", $text);
    
	if ($lines && count($lines) > 0) {
		
		$opt = null; 
		 
		/* init the parser */
		if ($wgParseListItems) {
			if (!is_object($wgParser)) {
			    $wgParser = new Parser();
			    $opt = $wgParser->mOptions;
			}
			if (!is_object($opt)) {
			    $opt = ParserOptions::newFromUser($wgUser);
			}
		}

		for ($i = 0; $i < sizeof($lines); $i++) {
			$line = $lines[$i];
			
			if (strpos($line, '**') === 0 && $i > 0) {// entry in a deeper level:
				$content = '
			<div class="menuSidebar">
				<ul>
					' . fnBuildList($lines,$i,1, $opt) . '
				</ul>
			</div>
			';
				$bar[$title] = $content;
				$i--;
			}
			else { // use Entry as Title:
				$title = trim($line, '* ');
			}	
		}
	}
	return true;
}

function fnBuildList($lines,&$i, $level, $opt) {
	global $wgParser, $wgTitle, $wgParseListItems;
	
	$content = "";
	$closeLI = false;
	$itemCount = 0;
	for (;$i < sizeof($lines); $i++) {
		$itemCount++;		
		
  		$class = "item{$itemCount}"; 
  		$class .= ($itemCount % 2 == 0 ? " even" : " odd"); 
  		
		$line = $lines[$i];
		$line = substr($line,$level);
				
		if (strpos($line, '**') === 0) {// entry in a deeper level:		
			// inject a &gt; at the end of the line
			$content = rtrim($content); 
			if (strrpos($content,'</a>') === strlen($content) - 4) {
				$content = substr($content,0,-4) . "<em>&gt;</em></a>"; 
			}

			$content .= '
				<div><ul>
					' .fnBuildList($lines,$i,$level+1, $opt) . '
				</ul></div>
				';
			$i--;
			$itemCount--;
		}
		else if (strpos($line, '*') === 0) { // entry in this level:
			if ($closeLI) { //workaround to close the last LI 
				$content .= "</li>";
				$closeLI = false;
			}
			if ($wgParseListItems) {
				$text = $wgParser->parse(trim($line, '* '),$wgTitle,$opt,true,true)->getText();
				$text = substr(trim($text),3,-5); // removes <p> and \n</p> that is generated by the parser
				
				if (trim($text) == "-"){
					$class .= " separator";
					$text = "";
				}
				if (strpos($text, '<a') !== 0) 
  					$text = "<a>" . $text . "</a>"; // this is needed to display normal text correctly
		
				$content .= "<li class=\"$class\">$text";
				$closeLI = true;
			}
			else
			{
				if (strpos($line, '|') !== false) {
					$line = array_map('trim', explode( '|' , trim($line, '* '), 2 ) );
					$link = wfMessage( $line[0] )->inContentLanguage()->text();
					if ($link == '-')
						continue;
				
					$text = wfMessage($line[1], 'parsemag')->text();
					if (wfMessage($line[1])->inContentLanguage()->isBlank())
						$text = $line[1];
					if (wfMessage($line[0])->inContentLanguage()->isBlank())
						$link = $line[0];
				
					if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $link ) ) {
						$href = $link;
					} else {
						$title = Title::newFromText( $link );
						if ( $title ) {
							$title = $title->fixSpecialName();
							$href = $title->getLocalURL();
						} else {
							$href = 'INVALID-TITLE';
						}
					}
					$href = htmlspecialchars($href);
					$text = htmlspecialchars($text);
					$content .= "<li class=\"$class\"><a href=\"$href\">$text</a>";
					$closeLI = true;
				}
				else {
					if (trim($line) == "-") {
						$class = " separator";
						$text = "";
					}
					
					$text = htmlspecialchars( trim($line, '* '));
					$content .= "<li class=\"$class\"><a>$text</a>";
					$closeLI = true;
				}
			}
		}
		else {
			if ($closeLI) { //workaround to close the last LI
				$content .= "</li>";
				$closeLI = false;
			}
			break; 
		}	
	}
	if ($closeLI) { //workaround to close the last LI 
		$content .= "</li>";
		$closeLI = false;
	}
	return $content;
}

Common.css[edit]

/*
* MenuSidebar
*/
/* this has to be overridden for every menu */
#p-Menu,
#p-Menu2,
#p-Menu3 {
 overflow: visible; 
}

/* this has to be overridden for every menu */
#p-Menu div,
#p-Menu2 div,
#p-Menu3 div {
 margin: 0;
 padding: 0;
}

/* this is an example to make the list of the second entry bigger e.g. when ther is a longer text in a list item
#p-Menu ul li.item2 ul {
 width: 16em;
}
*/

/* this an example to change the background color of every second list item
li.even {
 background-color: #eeeeee;
}
*/

.menuSidebar ul { /* remove bullets and list indents */
 list-style: none;
 margin: 0;
 padding: 0;
}

/* this div is only for better handling and navigation  ... could also be done in inner ul*/
.menuSidebar ul div { 
 position: absolute;
 top: -6px;
 left: 100%; /* to position them to the right of their containing block */
 width: 100%; /* width is based on the containing block */
 z-index: 100; /* show ul on top */
}

.menuSidebar ul ul { 
 background-color: white;
 border: 1px solid #aaaaaa;
 margin-top: 5px; /* make navigation easier*/
 margin-bottom: 5px; /* make navigation easier*/
 z-index: 101; /* show on top; Needed for IE 7 */
 width: 12em; /* width of inner menu, Needed for IE 7 */
}

.menuSidebar li {
 position: relative; /* make li a container for inner elements */
 margin: 1px;
 z-index: 102; /* show on top; Needed for IE 7 */
}

/* style, color and size links and headings to suit */
.menuSidebar a {
 font-weight: bold;
 display: block;
 border-width: 0px 0px 1px 0px;
 border-style: solid;
 border-color: #aaaaaa;
 margin: 0px;
 padding: 1px;
 padding-left: 5px;
 color: #000000;
 text-decoration: none;
}

/* move the &gt; to the right */
.menuSidebar a em {
 position: absolute;
 right: 4px;
}

.menuSidebar li:hover, /* Needed for IE 7 */
.menuSidebar a:hover {
 background: #ccccff;
}

.menuSidebar li.separator a:hover {
 background: white;
}

/* Needed for IE 7 */
.menuSidebar li.separator a,
.menuSidebar li.separator {
 line-height: 0px;
 height: 0px;
 font-size: 0px;
}

.menuSidebar ul li:last-Child a { 
 border-width: 0px 0px 0px 0px; // remove the last bottom line 
 padding-bottom: 0px;
}

/* hides the first level */
.menuSidebar ul li div { display: none; }

/* shows the first level and hide the second */
.menuSidebar ul li:hover div, 
.menuSidebar ul li div:hover { display: block; } /* div is again used for easier navigation*/
.menuSidebar ul li:hover ul li div { display: none; } /* hide the next level */

/* second level*/
.menuSidebar ul li ul li:hover div,
.menuSidebar ul li ul li div:hover  { display: block; } /* div is again used for easier navigation*/
.menuSidebar ul li ul li:hover ul li div { display: none; }  /* hide the next level */

/* third level */
.menuSidebar ul li ul li ul li:hover div,
.menuSidebar ul li ul li ul li div:hover   { display: block; } /* div is again used for easier navigation*/
.menuSidebar ul li ul li ul li:hover ul li div { display: none; }  /* hide the next level */

Example[edit]

Example for $wgParseListItems = false; (Default)

* Menu 1
** Submenu 1.1|Submenu 1.1
*** Item 1.1.1|Item 1.1.1
*** Item 1.1.2|Item 1.1.2
** Submenu 1.2|Submenu 1.2
* Another Menu
** not a link
*** Item 2.1.1|An Item
**** bla|still a deeper level

Example for $wgParseListItems = true;

* Menu 1
** [[Submenu 1.1|Submenu 1.1]]
*** [[Item 1.1.1|Item 1.1.1]]
*** [[Item 1.1.2|Item 1.1.2]]
** [[Submenu 1.2|Submenu 1.2]]
* Another Menu
** not a link
*** [[Item 2.1.1|An Item]]
**** [[bla|still a deeper level]]
*** [[Media:bla|also Files are possible]]

MediaWiki 1.17 and Vector[edit]

To make this extension work on MediaWiki 1.17.x and 1.18.x, add the following to MediaWiki:Vector.css:

/* keep nested list fonts from continually decreasing in size with each nesting */
#mw-panel div.portal div.body ul li {
	font-size: small !important;
}

/* Allow popup submenus to overflow the sidebar area */
#mw-panel div.portal div.body ul li {
	overflow: visible !important;
}



/* minor adjustments to the sidebar area for the Vector skin */
.menuSidebar ul div { 
        top: -1px !important; /* vertical offset of submenus */
}

.menuSidebar ul {
 width: 8em; /* width of main menu */
}

See also[edit]