Extension:BreadCrumbs2

From MediaWiki.org
Jump to: navigation, search
MediaWiki.org > Extensions > User interface > Extension:BreadCrumbs2
Manual on MediaWiki Extensions
List of MediaWiki Extensions
Crystal Clear action run.png
BreadCrumbs2

Release status: beta

Implementation User interface
Description Breadcrumb navigation based on categories
Author(s) Eric Hartwell
Last Version 0.9 (September 6, 2007)
MediaWiki 1.10
License CC-BY
Download Code (below)
Home page
Example MediaWiki.org > Extensions > User interface > Extension:BreadCrumbs2

check usage (experimental)

Contents

[edit] What can this extension do?

This extension generates "breadcrumbs" to help users navigate around your wiki (assuming it has a structure suitable for navigation). Breadcrumbs replace the page subtitle, which is normally used for redirect messages, with a single line navigation string. A sample breadcrumb for this page is shown in pale yellow above.

BreadCrumbs2 generates breadcrumbs in the "Where am I?" sense[1]; use Extension:BreadCrumbs for breadcrumbs in the traditional "How did I get here?" sense.

For each page the extension can use the categories and/or namespace to:

  1. Replace the page subtitle with a single line navigation string
  2. Highlight the active sidebar link and/or tab
  3. Change the site logo
Breadcrumbs use a single line of text to show a page's location in the site hierarchy... Breadcrumbs have always been a secondary navigation aid... All that breadcrumbs do is make it easier for users to move around the site, assuming its content and overall structure make sense.[2]

BreadCrumbs2 is especially useful with skins like GuMax that display the navigation bar as a row of tabs at the top of the page instead of the Discussion/Edit/History tools. It detects and uses the dynamic sidebar generated by DynamicSkin-based skins.

See InfoDabble for an example of a wiki that uses BreadCrumbs2.

[edit] Usage

The breadcrumb data is stored in a special page, MediaWiki:Breadcrumbs. To access this page, either enter "MediaWiki:Breadcrumbs" into the "search" box and click Go or copy and paste the link [[MediaWiki:Breadcrumbs]] into a page, then open and edit. The contents are formatted as a wikitext list, with one line per breadcrumb option:

* category name @ breadcrumb wikitext @ tab name @ site logo 

Extra blanks between parameters are ignored.

Parameter Type Description
category name required For each line in the list, see if the current page is a member of the category that matches category or the namespace with the same name.
breadcrumb wikitext optional Text to display in the subtitle with the page name appended
tab name optional The tab (navigation) bar with the name tab name that matches category is marked as active.
site logo optional Use a different site logo for this page. (Note: Use this option with care)

The breadcrumbs are built from the data page as follows:

  1. The string '@@@' is stripped from the text to be replaced with '@' before output (since the '@' character is used as a delimiter)
  2. Pseudo-variables of the form @@VAR@@ are evaluated.
    • @@USERID@@: User ID, blank if anonymous
    • @@USERGROUPS@@: Comma-delimited list of groups this user belongs to
  3. Templates and variables in the text are expanded. You can use parser functions like #if: if installed.
  4. The extension scans each line to find the first match with the current page's namespace or one of the page's categories.
  5. The remainder of the line is evaluated to set the breadcrumb string, sidebar, and/or logo (see below)

[edit] Navigation

The extension replaces the page subtitle, which is normally used for redirect messages, with a single line navigation string. Arbitrary wikitext can be used for the navigation text, though it should be kept to a simple series of links and delimiters.

Note
Jakob Nielsen strongly encourages web designers to use the '>' character to delimit breadcrumb levels[2].

For example,

* Hook extensions @ [[Main Page|MediaWiki.org]] > [[:Category:Extensions|Extensions]] >

Generates the following breadcrumb for this page:

MediaWiki.org > Extensions > BreadCrumbs2

Note that BreadCrumbs2 uses the first match with the current page's namespace or one of the page's categories. For example, if the Breadcrumbs page contains:

* Navigation extensions @ [[Main Page|MediaWiki.org]] > [[:Category:Extensions|Extensions]] > [[:Category:Navigation extensions|Navigation]] >
* User interface extensions @ [[Main Page|MediaWiki.org]] > [[:Category:Extensions|Extensions]] > [[:Category:User interface extensions|User interface]] >

The breadcrumbs for this page would be MediaWiki.org > Extensions > Navigation > Extension:BreadCrumbs2 and not MediaWiki.org > Extensions > User interface > Extension:BreadCrumbs2 because the Navigation line comes first.

[edit] Sidebar

The active link in the navigation sidebar can be highlighted using the tab name parameter, if specified. This is especially useful with skins like GuMax that display the navigation bar as a row of tabs at the top of the page instead of the Discussion/Edit/History tools.

BreadCrumbs2 automatically detects and uses the dynamic sidebar generated by DynamicSkin-based skins.

Note
The tab name is compared to the actual text displayed in the sidebar, so it is not language-independent.

[edit]

If the site logo parameter is specified, an alternate image file is used instead of the site default. The path to this image is relative to the root of your wiki.

Note
Use this feature with caution so that it doesn't interfere with your site's usability. Subtle changes are good, but dramatic changes can be confusing. If your site needs significantly different look and feel between sections, consider using namespaces and namespace-specific styles and/or DynamicSkin.

[edit] Installation

Copy the BreadCrumbs2.php file to the /extensions directory and add a reference to LocalSettings.php.

[edit] Changes to LocalSettings.php

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

[edit] Code

<?php
/***
 * BreadCrumbs2.php
 * @version 0.9
 * @date September 6, 2007
 * @author Eric Hartwell (http://www.ehartwell.com/InfoDabble/BreadCrumbs2)
 * @license Creative Commons Attribution 3.0
 
This extension generates "breadcrumbs" in the web navigation sense ("Where am I?")
 
To activate the functionality of this extension include the following in your
LocalSettings.php file:
 require_once('$IP/extensions/BreadCrumbs2.php');
 
Offered to the community for any use whatsoever with no restrictions other
than that credit be given to Eric Hartwell, at least in the source code,
according to the Creative Commons Attribution 3.0 License.
***/
 
# Change these constants to customize your installation
define ("DELIM", '@');                             // Delimiter/marker for parameters and keywords
define ("CRUMBPAGE", 'MediaWiki:Breadcrumbs');     // Default is 'MediaWiki:Breadcrumbs'
 
# Standard sanity check
if ( !defined( 'MEDIAWIKI' ) ) {
   echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" );
   die( -1 );
}
 
# Credits for Special:Version
$wgExtensionCredits['other'][] = array(
        'name' => 'BreadCrumbs2',
        'version' => '0.9',
        'author' => 'Eric Hartwell',
        'url' => 'http://www.ehartwell.com/InfoDabble/BreadCrumbs2',
        'description' => 'Breadcrumb navigation (where am I)'
);
 
# Hook function modifies skin output after it has been generated
$wgHooks['SkinTemplateOutputPageBeforeExec'][] = 'buildBreadcrumbs';
 
# This is the main function. Identify the categories for the current page,
# then locate the first match in the navigation list.
function buildBreadcrumbs( $skin, $template ) {
 
    # Get the list of categories for the current page       
    preg_match_all( '`title="Category:(.*?)"`', $skin->getCategories(), $matches, PREG_PATTERN_ORDER );
    $categories = $matches[1];
 
    # Treat the namespace as a category too
    if ( $skin->mTitle->getNsText() )
        $categories[] = $skin->mTitle->getNsText();
 
    # Load and parse the breadcrumb template. If it's a redirected page, extract redirect info
    $crumbs = matchFirstCategory( CRUMBPAGE, $categories );
    $breadcrumb = trim( $crumbs[0] . ' ' . $skin->mTitle->getText() );
    if ( preg_match('/\(Redirected.*?\)/', $template->data['subtitle'], $match) )
        $breadcrumb .= ' ' . $match[0];
    # Set the page subtitle to the breadcrumb contents
    $template->set( 'subtitle', $breadcrumb  );
 
    # If the current page is a category page, add it to the list
    # We didn't add it before because we don't want Category > Category'
    $pagecat = strstr( $skin->mTitle->getPrefixedText(), 'Category:' );
    if ( $pagecat !== FALSE )
        $categories[] = substr( $pagecat, strlen('Category:') );
    # If it's not a category page, try for an exact match of the title (e.g. 'Main')
    else
        $categories[] = $skin->mTitle->getText();
 
    # Mark the corresponding tab of the sidebar as active
    $crumbs = matchFirstCategory( CRUMBPAGE, $categories );
    if ( !empty($crumbs[1]) ) {
        # See if there's a corresponding link in the sidebar and mark it as active.
        # This is especially useful for skins that display the sidebar as a tab bar. 
        if ( method_exists( $template, 'setActiveSidebarLink' ) ) {
            # The DynamicSkin extension can build the tabs (sidebar) dynamically,
            # and not necessarily from $template->data['sidebar'], so DynamicSkin 
            # and derived skins have a setActiveSidebarLink() function
            $template->setActiveSidebarLink( $crumbs[1] );
        } else {
            # Normal skins use the global sidebar data
	    foreach ($template->data['sidebar'] as $bar => $cont) {
                foreach ($cont as $key => $val) {
                    if ( $val['text'] == $crumbs[1] )
                    {
                        $template->data['sidebar'][$bar][$key]['active'] = true;
                        break;
                    }
                }
            }                
        }
     }
 
    # Finally, see if we should change the site logo
    # Don't go overboard with this... subtle is better.
    if ( ! empty($crumbs[2]))
    {
        global $wgLogo, $wgScriptPath;
        $wgLogo = $wgScriptPath . '/' . $crumbs[2];
    }
 
    return true;
}
 
# Look up the menu corresponding to the first matching category from the list
function matchFirstCategory( $menuname, $categories ) {
    # First load and parse the template page 
    $content = loadTemplate( $menuname );
 
    # Navigation list
    $breadcrumb = '';
    preg_match_all( "`<li>\s*?(.*?)\s*</li>`", $content, $matches, PREG_PATTERN_ORDER );
 
    # Look for the first matching category or a default string
    foreach ( $matches[1] as $nav ) {
        $pos = strpos( $nav, DELIM );                // End of category
        if ( $pos !== false ) {
            $cat = trim( substr($nav, 0, $pos) );
            $crumb = trim( substr($nav, $pos + 1) );
            // Is there a match for any of our page's categories? 
            if ( $cat == 'default' ) {
                $breadcrumb = $crumb;
            }
            else if ( in_array( $cat, $categories ) ) {
                $breadcrumb = $crumb;
                break;
            }
        }
    }
 
    return normalizeParameters( $breadcrumb, DELIM, 3 );
}
 
# Loads and preprocesses the template page 
function loadTemplate( $title ) {
    global $wgUser, $wgParser, $wgTitle;
 
    # Load the template article for this skin
    $article = new Article( Title::newFromText( $title ) );
    if ( $article ) {
        $template = $article->fetchContent(0,false,false);
        if ( $template ) {
            # Drop leading and trailing blanks and escape delimiter before parsing
            # Substitute a few skin-related variables before parsing
            $template = preg_replace('/(^\s+|\s+$)/m', '', $template );
            $template = str_replace( DELIM.DELIM.DELIM, "\x07", $template);
            $template = preg_replace('/'.DELIM.DELIM.'(.*?)'.DELIM.DELIM.'/e',
                                     'translate_variable( $1 )', $template );
 
            # Use the parser preprocessor to evaluate conditionals in the template
            # Copy the parser to make sure we don't trash the parser state too much
            $lparse = clone $wgParser;
            $template = $lparse->parse( $template, $wgTitle, ParserOptions::newFromUser($wgUser) );
            $template = str_replace( '&nbsp;', ' ', $template->getText() );
            return $template ;
        }
    }
    return '';
}
 
# Normalize a delimited parameter line: trim leading and trailing blanks,
# restore escaped delimiter characters, add null elements until all optional
# parameters are accounted for, and drop extra parameters
function normalizeParameters( $input, $delimiter, $count ) {
     # Split the parameters into an array
    $params = explode( $delimiter, $input );
    $output = array();
    for ( $i = 0 ; $i < $count ; $i++ ) {
        $output[] = str_replace("\x07", $delimiter, ($i < count($params)) ? trim($params[$i]) : '');
    }
    return $output ;
}
 
# Returns HTML text for the specified pseudo-variable
function translate_variable( $tag )
{
    global $wgParser, $wgUser;
 
    switch ( strtoupper($tag) ) {
 
    case 'USERGROUPS':             // @@USERGROUPS@@ pseudo-variable: Groups this user belongs to
        $wgParser->disableCache(); // Mark this content as uncacheable
        return( implode( ",", $wgUser->getGroups() ) );
 
    case 'USERID':                 // @@USERID@@ pseudo-variable: User Name, blank if anonymous
        $wgParser->disableCache(); // Mark this content as uncacheable
        # getName() returns IP for anonymous users, so check if logged in first
        return( $wgUser->isLoggedIn() ? $wgUser->getName() : '' );
 
    }
}
 
?>

[edit] Implementation

[edit] Tips, tricks, and hoops

 preg_match_all( '`title="Kategorie:(.*?)"`', $skin->getCategories(), $matches, PREG_PATTERN_ORDER );

You need to replace Kategorie with your word for Category. You also need to replace the "Redirected" text with appropriate text from the redirectedfrom message

Alternatively, make the following changes to BreadCrumbs2.php to make it work with any language:
  • Add the following code at the start of function buildBreadcrumbs:
$parser = new Parser();
$parserOptions = new ParserOptions();
$categoryTitle = ($wgLanguageCode == 'zh') ? 'Category' : $parser->preprocess(wfMsgReal('pagecategories', array(1), true, true, false), null, $parserOptions);
$redirectString = $parser->preprocess(wfMsgReal('redirectedfrom', array('.*'), true, true, false), null, $parserOptions);
# Turn redirectString into a regular expression (already replaced $1 with .*)
$redirectString = str_replace(array('(', ')'), array('\(', '\)'), $redirectString);
  • Replace all instances of the word "Category" in this function with "'.$categoryTitle.'" or "$categoryTitle.'" as appropriate.
  • Replace the line running a preg_match on "Redirected" with the following:
if ( preg_match('/'.$redirectString.'/', $template->data['subtitle'], $match) )
$currentBreadcrumb = $template->data['subtitle'];
if (strlen($currentBreadcrumb) > 0)
    $breadcrumb = $breadcrumb . ' ' . $currentBreadcrumb;
addOnloadHook(function(){
var contentSub = document.getElementById("contentSub")
if (null !== contentSub) {
  var newSubText = contentSub.innerHTML.replace(new RegExp("<a.*<\\/a>\\s&gt;\\s"+wgTitle,"i"), "")
  if (0 == newSubText.length) {
    contentSub.parentNode.removeChild(contentSub)
  } else {
    contentSub.innerHTML = newSubText
  }
}})

[edit] See also

[edit] References

  1. Street signs and Breadcrumbs, Chapter 6, Don't Make Me Think - Steve Krug
  2. 2.0 2.1 Breadcrumb Navigation Increasingly Useful - Jakob Nielsen's Alertbox, April 10, 2007

[edit] Links

[edit] Version history

Personal tools
Namespaces
Variants
Actions
Site
Support
Download
Development
Communication
Toolbox