Personal tools
shadowfax.org.uk logo
Views

YAMBE Code

From Shadowfax

Jump to: navigation, search

Contents

Notes on Stability

YAMBE works on my site (as you can see above). Version (0.1.5) has been tested and is stable on MediaWiki 1.13.1, 1.14.0 and 1.15.0 on installations using the traditional wiki URLs and short URLs.

Version 0.2.0 fixed an impending issue with MediaWiki 1.16.0 and later. It has been tested and appears stable on 1.15.0, 1.16.0 and the alpha release of 1.17.0 . There is no reason to assume it should not also work with 1.13.0 and 1.14.0, however, the previous stable version (0.1.5) is retained on this site just in case.

If you do encounter any problems please comment below and I will attempt to address them.

Yambe ChangeLog and To Do List

Configuration

There are five variables in the YAMBE code which can be set to configure the way that YAMBE behaves on your system. These are shown below:

//Some global config declarations
// where to split the URL to find page name. Usually either /index.php/ or /wiki/
$URLSplit = "/index.php/"; 
$bcDelim = " > "; // Character to mark the stages of the breadcrumb?
$maxCountBack = 5; // Maximum number of links in breadcrumb 
$overflowPre = "[...]"; // Prefix if breadcrumb is longer than $maxCountBack links
$selfLink = false; // Set true if last page in breadcrumb should be a link

$URLSplit

This is the most important of the four. It should be set to the element which divides your site URL from your wiki page name. In the following example $URLSplit should be set to "/thisbit/"

http://www.mysite.com/thisbit/MyPage

In the default wiki installation the URLSplit is /index.php/. Where you are using friendly URLs this is often changed to /wiki/

$bcDelim

This is the string used to break up elements of the breadcrumb. It allows you to configure what the breadcrumb looks like. The default is to use the greater than chevron, but this can be changed to whatever you need.

$maxCountBack

This is how many elements the breadcrumb should show. It stops YAMBE from looping back over too many pages. Once YAMBE reaches this limit it stops building the breadcrumb and shows the overflow prefix.

$overflowPre

The overflow prefix that YAMBE puts in front of the breadcrumb when the number of pages in the breadcrumb exceeds $maxCountBack.

$selfLink

Set this to true if the last item on the breadcrumb should be a link, otherwise false.

CSS Styles

Yambe encloses the breadcrumb in a div with the id yambe. This can be used to configure the breadcrumb style in the CSS.

The Code

Add the following to LocalSettings.php

require_once ('extensions/yambe.php');

Save the following as yambe.php in MediaWiki extensions directory and edit the global config declarations to fit your wiki.

<?php
/**
 * Yet Another MediaWiki Breadcrumb Extension (Yambe)
 * For documentation, please see http://shadowfax.org.uk/wiki/YAMBE
 *
 * @ingroup Extensions
 * @author Ian Coleman
 * @version 0.2.2
 */
define('YAMBE_VERSION','0.2.2, 2011-08-29');
 
//Extension credits that show up on Special:Version
$wgExtensionCredits['parserhook'][] = array(
 'name' => 'YAMBE Hierarchical Breadcrumb',
 'url' => 'http://shadowfax.org.uk/wiki/YAMBE_Code',
 'version' => YAMBE_VERSION,
 'author' => '[http://shadowfax.org.uk/wiki Ian Coleman]',
 'description' => 'Parser hook to show Breadcumb on MediaWiki.'
);
 
//Some global config declarations
// where to split the URL to find page name. Usually either /index.php/ or /wiki/
$URLSplit = "/index.php/"; 
$bcDelim = " &gt; "; // Character to mark the stages of the breadcrumb?
$maxCountBack = 5; // Maximum number of links in breadcrumb 
$overflowPre = "[...]"; // Prefix if breadcrumb is longer than $maxCountBack links
$selfLink = false;
 
//Set up the hooks
$wgHooks['EditFormPreloadText'][] = array('yambeSetParent');
 
// Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as 
// per r35980
if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) {
 $wgHooks['ParserFirstCallInit'][] = 'yambeInit';
} else {
 $wgExtensionFunctions[] = 'yambeInit';
}
 
function yambeInit(){
 global $wgParser;
 $wgParser->setHook ( 'yambe:breadcrumb', 'yambeBreadcrumb' );
 return true;
}
 
// Function to build the breadcrumb
function yambeBreadcrumb ($data, $args)
{
global $wgTitle, $wgParser;
global $bcDelim, $maxCountBack, $overflowPre, $selfLink;
 
$wgParser->disableCache();
 
// Grab the self argument if it exists
if (isset($args['self'])) $yambeSelf = $args['self'];
else $yambeSelf = $wgTitle->getText();
 
// Breadcrumb is built in reverse and ends with this rather gratuitous self-link
if ($selfLink) $breadcrumb = linkFromText($wgTitle->getText(),$yambeSelf,$wgTitle->getNamespace());
else $breadcrumb = $yambeSelf;
 
$cur = str_replace(" ", "_", ($wgTitle->getText()));
 
// Store the current link details to prevent circular references
if ($wgTitle->getNsText() == "Main") $bcList[$cur] = "";
else $bcList[$cur] = $wgTitle->getNsText();
 
if ($data!="")
  {
  $cont = true;
  $count = 2; // because by first check, breadcrumb will have 2 elements!
 
  do
    {
    // Grab the parent information from the tag
    $parent = explode("|",$data);
    $page   = splitName(trim($parent[0]));
 
    // Allow for use of only the parent page, no display text
    if(count($parent) < 2) $parent[1] = "";
 
    // Check link not already in stored in list to prevent circular references
    if (array_key_exists($page['title'],$bcList))
      if ($bcList[$page['title']] == $page['namespace']) $cont = false;
 
    if ($cont) 
      {
      // Store the current link details to prevent circular references
      $bcList[str_replace(" ", "_",$page['title'])] = $page['namespace'];
 
      // make a url from the parent
      $url = yambeMakeURL($page, trim($parent[1]));
 
      // And if valid add to the front of the breadcrumb
      if ($url != "")
        {
        $breadcrumb = $url . $bcDelim . $breadcrumb;
 
         // Get the next parent from the database
        $par = getTagFromParent($page['title'], $page['namespaceid']);
 
        // Check to see if we've tracked back too far
        if ($count >= $maxCountBack)
          {
          $cont = false;
          if ($par['data'] != "")
            $breadcrumb = $overflowPre . $bcDelim . $breadcrumb;
          }
        else
          {
          $page['title'] = str_replace(" ", "_", $page['title']); 
 
          $data = $par['data'];
          if ($data == "")
            $cont = false;
          }
        }
      }
    $count++;
    }
    while ($cont); // Loop back to get next parent
  }
 
// Encapsulate the final breadcrumb in its div and send it back to the parser
return "<div id='yambe'>$breadcrumb</div>\n";
}
 
// Function to get namespace id from name
function getNamespaceID($namespace)
{
if ($namespace == "") return 0;
else
  {
  $ns = new MWNamespace();
  return $ns->getCanonicalIndex(trim(strtolower($namespace)));
  }
}
 
// Function to build a url from text
function linkFromText($page, $displayText, $nsID=0)
{
global $wgUser;
 
$skin = $wgUser->getSkin();
$title = Title::newFromText (trim($page), $nsID);
 
if (!is_null($title)) return $skin->makeKnownLinkObj($title, $displayText, "");
else return "";
}
 
function pageExists($page, $nsID=0)
{
$page = str_replace(" ", "_", $page);
 
$dbr = wfGetDB( DB_SLAVE );
 
if ($dbr->selectField( 'page', 'page_id', 
    array("page_title" => $page, "page_namespace" => $nsID), 
   __METHOD__ ) == "") return false;
else return true;
}
 
// Function checks that the parent page exists and if so builds a link to it
function yambeMakeURL($page, $display)
{
if (pageExists($page['title'],$page['namespaceid']))
  return linkFromText($page['title'],$display,$page['namespaceid']);
else return "";
}
 
// Get the parents tag
function getTagFromParent($pgName, $ns = 0)
{
$par['data']= "";
$par['exists']= false;
$par['self']="";
 
$dbr = wfGetDB( DB_SLAVE );
 
$pgName = str_replace(" ", "_", $pgName);
 
$res = $dbr->select(array ("revision", "text", "page",),
                    "old_text", 
                     array( "page_title" =>$pgName, "page_namespace" => $ns),
                     __METHOD__, 
                     array ("ORDER BY"=>"rev_id desc limit 1"),
                     array("text" => array ("LEFT JOIN", "old_id=rev_text_id" ), 
                           "revision" => array( 'LEFT JOIN', 'rev_page=page_id')));
 
// Check to see if the query worked
if ($res)
  {
  if ($dbr->numRows( $res) > 0)
    {
    // We've got the parent text. Now locate it's parent tag
    $row = $dbr->fetchRow( $res );
    $text = $row["old_text"];
 
    $dbr->freeResult( $res ); 
    $par = yambeUnpackTag ($text);
    }
  }
return $par;
}
 
function splitName($in)
{
if (substr_count($in,":"))
  {
  // Parent name includes Namespace - grab the page name out for display element
  $fullName  = explode (":", $in);
  $page['title']     = str_replace(" ", "_", $fullName[1]);
  $page['namespace'] = $fullName[0];
  $page['namespaceid'] = getNamespaceID($fullName[0]);
  }
else
  {
  $page['title']     = str_replace(" ", "_", $in);
  $page['namespace'] = "";
  $page['namespaceid'] = 0;
  }
return $page;
}
 
// Set up the breadcrumb link in a new page
function yambeSetParent(&$textbox, &$title)
{
global $URLSplit;
 
$parent = explode($URLSplit,$_SERVER['HTTP_REFERER']);
 
// If the code breaks on this line check declaration of $URLSplit on line 23 matches your wiki 
$page = splitName($parent[1]); 
$par = getTagFromParent($page['title'],$page['namespaceid']);
 
if ($par['exists'])
  {  
  if ($par['self'] != "") $display = $par['self'];
  else $display = str_replace("_", " ", $page['title']);
 
  $textbox = "<yambe:breadcrumb>$parent[1]|$display</yambe:breadcrumb>";
  }
 
return true;
}
 
// Bit of a kludge to get data and arguments from a yambe tag
function yambeUnpackTag ($text)
{
$ret['exists']=false;
$ret['data']= "";
$ret['self']= "";
$end = false;
 
// Find the opening tag in the supplied text
$start = strpos($text,"<yambe:breadcrumb");
 
// Find the end of the tag
// Grab it and convert <yambe_breadcrumb> because simplexml doesn't like <yambe:breadcrumb>
if ($start !== false)
  {
  $end = strpos($text,"</yambe:breadcrumb>", $start);
  if ($end !== false) $tag = substr($text, $start, $end-$start+19);
  else
    {
    $end = strpos($text,"/>", $start);
    if ($end !== false) $tag = substr($text, $start, $end-$start+2);
    }
 
  if ($end !== false)
    {
    $tag = str_replace("yambe:breadcrumb", "yambe_breadcrumb", $tag);
 
    // encapsulate in standalone XML doc
    $xmlstr = "<?xml version='1.0' standalone='yes'?><root>$tag</root>";
 
    $xml = new SimpleXMLElement($xmlstr);
 
    // And read the data out of it
    $ret['self'] = $xml->yambe_breadcrumb['self'];
    $ret['data'] = $xml->yambe_breadcrumb[0];
    $ret['exists'] = true;
    }
  }
 
return $ret;
}
?>

To kick things off enter a yambe tag in the root page of your hierarchy. Because this is the root page it has no parent so the data element of the tag is blank

<yambe:breadcrumb />

The normal form of the yambe tags is:

<yambe:breadcrumb>Page Title|Display Text for Breadcrumb</yambe:breadcrumb>

As of 0.2.1 the Display Text for Breadcrumb can be omitted (Thanks to Chris Cauthen for suggesting this) eg

<yambe:breadcrumb>Page Title</yambe:breadcrumb>

Tags are automatically produced when creating pages under pages that are already tagged, and the display text defaults to the page title.



Leave your comment

Comments

Ian Peter. I've sent you an email, but it sounds like the $URLSplit parameter does not match the settings on your live environment. Please see the configuration notes at the top of this page, or email me if there are further issues.
Peter In my XAMPP test-surrounding all was working well. but online this extension not work for me...
i have an error:
Notice: Undefined offset: 1 in /www/htdocs/SomeNumbers/wiki/extensions/yambe.php on line 230
Chris Ian, Found another PHP error and thought I would let you know, just an initialisation issue I think.
Given: Main Page > Page AAA > BBB (with all pages having the yambe tag and they are working).
I renamed "PAGE AAA" to "PAGE A" (officially move in mediawiki).
I get "Page A > BBB" since the parent name in the yambe tag did not get changed. But I get and undefined index "data" on line 111. I took a look and $ret is not initialised with 'data' or 'self'. Once I initialised $ret I then proceeded to edit all the pages and change the parent. Not related to the PHP error, maybe there is a way to put out something to remind the user that the parent lookup failed Like: ERR > PAGE A > BBB (Just a thought)
Ian Thanks Chris. That looks like a useful addition to the YAMBE code. I'll test it later and pop it in.
Chris Thanks for this extension, it was exactly the way I wanted my navigations to work. Unfortunately I had already created a lot of pages and I didn't want to enter the Parent Page title twice on every page. I was fine with having the "Display Text for Breadcrumb" to be the same as the "Page Title". So I didn't enter the second parameter and your code worked but got a error about index missing for $parent[1], obviously since there wasn't a second parameter. I just entered: Page Title

So I made this small change to remove the error and make sure the title was set correctly:
In function: yambeBreadcrumb
Line: 77 just after "$page = splitName(trim($parent[0]));
I added the following:
// Allow for use of only the parent page, no display text
if( count($parent) < 2 )
{
$parent[1] = $page['title'];
}

You may want to require the second parameter, but just in case it doesn't matter, I thought I would share what I found.
Thanks Again!
Ian Hi Jeff. Thanks for the comments. The plugin doesn't add tags when you edit existing content - only when you create new. So yes, currently the only way to get tags on existing content is to manually add them. If you have a lot of old content that is obviously a pain, but I can't think of an easy way for it to work out a page's parent.
Jeff Hey ... So I love this breadcrumb extension over all the others. Mostly because it does not duplicate itself no matter how many articles you read. One thing I don't understand...do I have to go through all of my current Articles and add a Tag, or will the plugin add them after I edit the page? I understand new articles get this...but what about all of my current content?
Ian Replied to Greg in another forum - but for the record YAMBE is registered with the extension Matrix on MediaWiki.org
greg Ian, why didn't you publish it on mediawiki.org?
Ian No worries, Ben. Thanks for the update.
Ben Chenoweth Hi Ian. I worked it out. I hadn't realised I needed to customise the variable $URLSplit. But after I changed it to '/wiki/', the tags appeared as expected. Sorry for causing you unnecessary stress!
Ian Sorry, no. I got as far as finding out that FCK doesn't override the EditFormPreloadText hook and then I ran out of ideas. Currently I can't see how FCK extension interacts with the creating a new entry to understand why it is not picking up the text preloaded into the mediawiki edit control.
Ben Chenoweth Any luck with sorting this out? I know nothing about modifying existing extensions, so I was loath to tinker around!
Ian Hi Ben.

I've not used YAMBE with the FCK Editor. My guess is that the FCK Editor extension also overrides the EditFormPreloadText hook and this is taking precedence over Yambe. If that is the case it should be a relatively simple matter of getting the FCK editor's hook to call yambeSetParent. I'll take a quick look at the FCK editor extension later on today and see if this is the case. Hope this makes sense.
Ben Chenoweth I have installed yambe and it displays correctly when I manually add tags. However, I have also installed FCKeditor (http://mediawiki.fckeditor.net/) and it appears that it is preventing yambe from automatically creating the <yambe? tag of a newly created page. Any ideas?
Menu