Open main menu

Extension:Widgetbar

MediaWiki extensions manual
OOjs UI icon advanced.svg
Widgetbar
Release status: beta
Implementation User interface, Parser extension
Description Easily create dynamic sidebar widgets
Author(s) EpicWikipediantalk
Latest version 0.1.0 (2012-06-24)
MediaWiki 1.19
License No license specified
Download Click here to download
Hooks used
SkinBuildSidebar
Translate the Widgetbar extension if it is available at translatewiki.net
Check usage and version matrix.

IMPORTANT: Since this extension was designed with mainly Wikkii in mind, it is recommended that you set your default skin to Monobook or a skin based on Monobook while using this extension. The extension wasn't designed for, and never will be designed for, other skins, as they don't really fit with this extension. However, it is still available to all wikis, as we are more than happy for non-Wikkii wikis to use this extension.

The Widgetbar extension provides an easy way to create dynamic sidebar widgets. They can be customized on a per-page basis, and magic words as well as templates can be included in the sidebar. You may define the sidebar in-page or reference another page which contains the text. The extension was mainly designed with Wikkii in mind, and hasn't been tested with any skin other than the Monobook skin or the new Wikkii skin. The extension is largely compatible with the regular sidebar, so you won't need to make any changes to the MediaWiki:Sidebar page on your wiki while installing the extension.

Contents

UsageEdit

WidgetsEdit

These can be created using the regular sidebar code, but the added ability of magic words and templates makes the effect of widgets. There are also a set of built-in widgets that come with this extension:

Built-inEdit

So far, there is only one built-in widget, but there should hopefully be more, once more people begin using the Widgetbar extension on their wikis.

Navigation widgetEdit

Enables users to add third-level menu items in their sidebar. To enable this widget, simply do these steps:

  1. Create a menu structure at the MediaWiki:MenuSidebar page on your wiki. This works differently to the main MediaWiki:Sidebar format, as you need to put [[ and ]] around second-level menu items. Otherwise, though, it works the same. To create a third-level menu item, simply use *** [[<page to link to>|<text to desplay>]].
  2. As the add-on is displayed in addition to the content of MediaWiki:Sidebar, you may want to change some stuff in there in order to prevent the new menu structure from displaying at the bottom of the sidebar.

Additional widgetsEdit

There will be an area opening soon where users can share widgets created using the Widgetbar extension. Users will be able to choose whether or not they can be used by non-Wikkii wikis. This area hasn't opened yet, but will do soon.

Creating widgetsEdit

This works no differently to the format of MediaWiki:Sidebar, although with the added ability of magic words and templates. Just use them in your sidebar as you would in any wiki page.

CustomizationEdit

Just like any extension, you can customize any feature it introduces. You can also control the colour of the first, third, fifth menu items etc. using the li.odd class, and the other menu items using the li.even class.

Using this extension, it is possible to change the sidebar just for a particular page; just use <sidebar> and </sidebar> around the sidebar code, then add it to the page you want a different sidebar on. Alternatively, you can share the same sidebar across several pages, but not the whole wiki, by using <sidebar>Name of page which contains a customized sidebar</sidebar>. The sidebar code for this is no different to the code used for the MediaWiki:Sidebar page.

DownloadEdit

Firstly, create a "Widgetbar" folder in the "extensions" folder located on your wiki's files, then add a file under the name of "Widgetbar.php" with the following code in it:

<?php
/**
 * Widgetbar extension
 */
if ( !defined( 'MEDIAWIKI' ) ) {
	exit( 1 ) ;
}
 
$wgExtensionCredits['parserhook'][] = array(
       'name' => 'Widgetbar',
       'author' => 'EpicWikipedian',
       'url' => 'http://www.mediawiki.org/wiki/Extension:Widgetbar',
       'description' => 'Easy system for adding sidebar widgets',
       'descriptionmsg' => "widgetbar-desc", // Same as above but name of a message, for i18n - string, added in 1.12.0
       'version' => '0.1.0',
       'path' => __FILE__,
       );
 
$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 );
 
	if ($rev)
		$lines = explode("\n", $rev->getRawText());
 
	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 an arrow symbol at the end of the line
			$content = rtrim($content); 
			if (strrpos($content,'</a>') === strlen($content) - 4) {
				$content = substr($content,0,-4) . "<em>››</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 = wfMsgForContent( $line[0] );
					if ($link == '-')
						continue;
 
					$text = wfMsgExt($line[1], 'parsemag');
					if (wfEmptyMsg($line[1], $text))
						$text = $line[1];
					if (wfEmptyMsg($line[0], $link))
						$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;
}

//Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980
if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) {
   $wgHooks['ParserFirstCallInit'][] = 'efCustomSideBarInit';
} else { // Otherwise do things the old fashioned way
   $wgExtensionFunctions[] = 'efCustomSideBarInit';
}

function efCustomSideBarInit() {
   global $wgParser;
   $wgParser->setHook( 'sidebar', 'efCustomSideBar' );
   return true;
}

function efCustomSideBar( $input, $args, $parser ) {
   // We can't count of the tag being read if the page is cached.  So instead of this code performing any task
   // we leave it here to easily remove the <sidebar> tag read in the SkinBuildSidbar hook
   // aka lazy way to clear tag
   return '';
}

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

function fnSidebarHook($skin, &$bar)
{
      // this is mostly just the standard sidebar processing function with a custom loader

      global $parserMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry, $wgParser, $wgUser;
      global $wgLang, $wgTitle, $wgArticle, $wgDefaultSideBarText, $wgUser,  $wgDefaultSideBarGroupText, $wgDefaultSidebarNSText;

      $NewSideBar = false;

      if (isset($wgArticle) and $wgArticle)
      {
         $pagetext = $wgParser->preprocess( $wgArticle->getContent(), $wgArticle->getTitle(), ParserOptions::newFromUser( $wgUser ));

         if (preg_match('%\<sidebar\>(.*)\</sidebar\>%isU', $pagetext, $matches))
         {	$NewSideBar = $matches[1];
         }  
         else if (isset($wgDefaultSideBarText) and $wgDefaultSideBarText !== false)
         {	$NewSideBar = $wgDefaultSideBarText;
         }
      }
	
	
		// sidebar cache code
      wfProfileIn( __METHOD__ );

      $key = wfMemcKey( 'sidebar', $wgLang->getCode() );

      if ( $wgEnableSidebarCache ) {
         $cachedsidebar = $parserMemc->get( $key );
         if ( $cachedsidebar ) {
            wfProfileOut( __METHOD__ );
            return $cachedsidebar;
         }
      }
		// end cache code
		
      $new_bar =  fnCustomSidebarProcess($NewSideBar);

      if ((count($new_bar) === 0) && ($wgDefaultSideBarText !== false))
      {
         $new_bar = $bar;
      }
		
		// Add customs bar based on user groups
      $groups = array_reverse($wgUser->mGroups);
      foreach($groups as $n => $v)
      {  if ( is_array($wgDefaultSideBarGroupText) && array_key_exists($v, $wgDefaultSideBarGroupText))
         {
            $new_bar = array_merge($new_bar, fnCustomSidebarProcess($wgDefaultSideBarGroupText[$v]));
         }
      }
		
		// Add custom bar based on namespace
      $ns = $wgTitle->getNamespace();
      if (is_array($wgDefaultSidebarNSText) && array_key_exists($ns , $wgDefaultSidebarNSText))
      {
         $new_bar = array_merge($new_bar, fnCustomSidebarProcess($wgDefaultSidebarNSText[$ns]));
      }

      if (count($new_bar) > 0)
      {
         $bar = $new_bar;
      }
		
		// sidebar cache code
      if ( $wgEnableSidebarCache ) $parserMemc->set( $key, $bar, $wgSidebarCacheExpiry );

      wfProfileOut( __METHOD__ );
		// end sidebar cache code
		
      return true;
}

function fnCustomSidebarProcess($NewSideBar)
{     global $wgParser, $wgUser, $wgArticle, $wgTitle;
      
      $NewSideBar = fnCustomSidebarPreProcess($NewSideBar);
      
      // custom loader
      if ($NewSideBar !== false)
      {  
      	if (strpos(trim($NewSideBar), '*') === 0)
         {  $text = $NewSideBar;
         }
         else
         {  $text = $NewSideBar;
            do
            {  
            	$oldtext = $text;
               if (($titleFromText = Title::newFromText($text)))
               {  
               	$article = new Article($titleFromText,0);
                  $text = $article->getContent();
                  $text = preg_replace('%\<noinclude\s*\>(.*)\</noinclude\s*\>%isU','',$text);
                  $text = fnCustomSidebarPreProcess($text);
               }
                              
            } while ( $text !== $oldtext);
         }

         $lines = explode( "\n",  $text );
      }
      else
      {  
         return array();
      }

      $new_bar = array();

      $heading = '';
      
      // taken directly from MediaWiki source v1.14.0
      foreach ($lines as $line) {
         if (strpos($line, '*') !== 0)
            continue;
         if (strpos($line, '**') !== 0) {
            $line = trim($line, '* ');
            $heading = $line;
            if( !array_key_exists($heading, $new_bar) ) $new_bar[$heading] = array();
         } else {
            if (strpos($line, '|') !== false) { // sanity check
               $line = array_map('trim', explode( '|' , trim($line, '* '), 2 ) );
               $link = wfMsgForContent( $line[0] );
               if ($link == '-')
                  continue;

               $text = wfMsgExt($line[1], 'parsemag');
               if (wfEmptyMsg($line[1], $text))
                  $text = $line[1];
               if (wfEmptyMsg($line[0], $link))
                  $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';
                  }
               }

               $new_bar[$heading][] = array(
                  'text' => $text,
                  'href' => $href,
                  'id' => 'n-' . strtr($line[1], ' ', '-'),
                  'active' => false
               );
            } else { continue; }
         }
      }
		// End Mediawiki source

      if (count($new_bar) > 0)
      {  return $new_bar;
      }
      else
      {  return array();
      }
}

// processes templates and wiki magic words, plus any add'l custom magic words
function fnCustomSidebarPreProcess($text)
{
   global $wgTitle, $wgParser, $wgUser;
	
	$text = str_ireplace ('{{#__username}}',$wgUser->mName, $text);
   return $wgParser->preprocess( preg_replace('%\<noinclude\>(.*)\</noinclude\>%isU','',$text),  $wgTitle, ParserOptions::newFromUser( $wgUser ));

}

?>

Then, add the following code in LocalSettings.php:

# Enable the Widgetbar extension:
require_once( "$IP/extensions/Widgetbar/Widgetbar.php" );
$wgParseListItems = true;

Finally, you might want to set $wgDefaultSkin to "monobook", then make a few adjustments to the MediaWiki:Monobook.css page on your wiki to make it look better.

To-do listEdit

This is marked on a scale of 1 to 3, with 3 meaning that EpicWikipedian, the creator of this extension, is more dedicated to try and work on that problem.

  • 3 - Add the ability for users to define a seperate sidebar for anonymous users.
  • 2 - Fix a problem where the navigation widget seems to conflict with other widgets.
  • 2 - Add an area where users can share widgets they have created using the Widgetbar extension.
  • 1 - Allow widgets to be collapsed and expanded, similarly to the Vector extension.
  • 1 - Add a special page where users can easily add and remove built-in extensions on demand.

VersionsEdit

Version 0.1.0 (24 June 2012)Edit

  • First public release.

Wikis using the Widgetbar extensionEdit

Please add your wiki to this section if it uses the Widgetbar extension. Thanks.

  • Development Wiki
  • English Nonsary (doesn't actually use the Widgetbar extension, but contains a sidebar which is based on the navigation widget from this extension)

See alsoEdit