kylej,
the idea is to be able to add a custom class or id to menu item according a TV(text).
The main issue is that we have to avoid css conflict between ID (if the menu is called twice in the same document for instance to work on the same tree level) so we will use class.
Well as I cannot explain as I'd like to, here is your code modified for this feature.
As I am not a developper I don't know if it's the best way to do.
The main changes are
- I've added a parameter to the wf class (customCss) which is the name of The TV from which the values are retrieved
- In the buidMenu function added in the $resource loop a call to getTemplateVar
- Added a parameter to setItemClass function
todo:
-debug mode
I guess that's all.
[[WayFinder? &customCss=`IdCssMenu` ]]
<?php
/*
Snippet name: Wayfinder
Short Desc: builds site navigation
Version: beta
Authors: Ryan Thrash (vertexworks.com)
Kyle Jaebker (muddydogpaws.com)
*/
class Wayfinder {
var $id = 0;
var $level = 0;
var $ph = FALSE;
var $debug;
var $ignoreHidden = FALSE;
var $hideSubMenus = FALSE;
var $textOfLinks = 'menutitle';
var $titleOfLinks = 'pagetitle';
var $templates = array();
var $parentTree = array();
var $wfDocs = array();
var $css = array();
var $cssTpl = FALSE;
var $jsTpl = FALSE;
var $rowPlaceHolders = array('[+wf.link+]','[+wf.title+]','[+wf.linktext+]','[+wf.wrapper+]','[+wf.classes+]');
var $outerPlaceHolders = array('[+wf.wrapper+]','[+wf.classes+]');
var $ie = "\n";
var $customCss;//hack customCss which TV do we have to look at
var $debugOutput = '<h2>WayFinder Debug Output:</h2>';
//remove any docs specified with hidemenu
function filterHidden($var) {
return (!$var['hidemenu']==1);
}
function endKey(&$array){
end($array);
return key($array);
}
//generate the menu
function buildMenu($parentId,$curLevel = 1) {
global $modx;
$output = '';
$getFields = 'id,menutitle,pagetitle,menuindex,published,hidemenu,parent,isfolder,description,alias,longtitle';
$resource = $modx->getActiveChildren($parentId,'menuindex','ASC',$getFields);
if (!$this->ignoreHidden) {
$resource = array_filter($resource, array($this, "filterHidden"));
}
$firstItem = 1;
if (is_array($resource) && !empty($resource)) {
$numItems = $this->endKey($resource);
foreach ($resource as $n => $v) {
$v['link'] = $modx->makeUrl($v['id']);
$v['level'] = $curLevel;
$v['first'] = $firstItem;
//hack for specific css
if(!empty($this->customCss)){//
$mycss = $modx->getTemplateVar($this->customCss,$fields='id',$v['id']);
//print_r($mycss);
if(!empty($mycss['value'])) {
$v['specific'] = $mycss['value'];
}
}
//endhack
$firstItem = 0;
if ($n == $numItems) {
$v['last'] = 1;
} else {
$v['last'] = 0;
}
$useTextField = (empty($v[$this->textOfLinks])) ? 'pagetitle' : "$this->textOfLinks";
$v['linktext'] = $v[$useTextField];
$v['title'] = $v[$this->titleOfLinks];
if ($v['isfolder'] && ($curLevel < $this->level || $this->level == 0) && (!$this->hideSubMenus || in_array($v['id'],$this->parentTree))) {
$oldLevel = $curLevel;
$subMenu = $this->ie . $this->buildMenu($v['id'],++$curLevel) . $this->ie;
$curLevel = $oldLevel;
} else {
$subMenu = '';
}
$output .= $this->renderRow($v,$subMenu);
}
if (!empty($this->templates['innerTpl']) && $curLevel > 1) {
$useChunk = $this->templates['innerTpl'];
$usedTemplate = 'innerTpl';
} else {
$useChunk = $this->templates['outerTpl'];
$usedTemplate = 'outerTpl';
}
if ($curLevel > 1) {
$wrapperClass = 'innercls';
} else {
$wrapperClass = 'outercls';
}
$useClass = $this->setItemClass($wrapperClass);
$phArray = array($output,$useClass);
$output = str_replace($this->outerPlaceHolders,$phArray,$useChunk);
if ($this->debug) {
$numDocs = $numItems + 1;
$this->debugOutput .= '<strong>Nesting Complete</strong> - Previous ' . $numDocs . ' level ' . $curLevel . ' items inserted into ' . $usedTemplate . ' with class ' . $useClass . '<br/>';
}
}
return $output;
}
//render each rows output
function renderRow(&$resource,$subMenu) {
global $modx;
$output = '';
if ($resource['isfolder'] && !empty($this->templates['parentRowTpl'])) {
$usedTemplate = 'parentRowTpl';
} elseif ($resource['id'] == $modx->documentObject['id'] && !empty($this->templates['innerHereTpl']) && $resource['level'] > 1) {
$usedTemplate = 'innerHereTpl';
} elseif ($resource['id'] == $modx->documentObject['id'] && !empty($this->templates['hereTpl'])) {
$usedTemplate = 'hereTpl';
} elseif ($resource['level'] > 1 && !empty($this->templates['innerRowTpl'])) {
$usedTemplate = 'innerRowTpl';
} else {
$usedTemplate = 'rowTpl';
}
$useChunk = $this->templates[$usedTemplate];
$useSub = $subMenu;
$useClass = $this->setItemClass('rowcls',$resource['id'],$resource['first'],$resource['last'],$resource['level'],$resource['isfolder'],$resource['specific']);
$phArray = array($resource['link'],$resource['title'],$resource['linktext'],$useSub,$useClass);
$output .= str_replace($this->rowPlaceHolders,$phArray,$useChunk);
if ($this->debug) {
$this->debugOutput .= '<strong>Item Processed: (' . $resource['id'] . ') ' . $resource['pagetitle'] . '</strong><br/>
template: ' . $usedTemplate . ' | class: ' . $useClass . '<br/>
level: ' . $resource['level'] . ' | First/Last: ' . $resource['first'] . '/' . $resource['last'] . '<br/>';
}
return $output . $this->ie;
}
//debugging to check for valid chunks
function checkChunks() {
global $modx;
foreach ($this->templates as $n => $v) {
$chunkcheck = $modx->getChunk($v);
if (empty($v) || !$chunkcheck) {
if ($n === 'outerTpl') {
$this->templates[$n] = '<ul[+wf.classes+]>[+wf.wrapper+]</ul>';
} elseif ($n === 'rowTpl') {
$this->templates[$n] = '<li[+wf.classes+]><a href="[+wf.link+]" title="[+wf.title+]">[+wf.linktext+]</a>[+wf.wrapper+]</li>';
} else {
$this->templates[$n] = '';
}
if ($this->debug) {
$this->debugOutput .= '<p>No chunk found for <strong>' . $n . '</strong> using default.</p>';
}
} else {
$this->templates[$n] = $chunkcheck;
}
}
}
//determine "you are here"
function isHere($did) {
return in_array($did,$this->parentTree);
}
//determine style class for current item being processed
//added 1param to the function for customCss hack
function setItemClass($classType, $docId = 0, $first = 0, $last = 0, $level = 0, $isFolder = 0, $specificCss ='') {
global $modx;
$returnClass = '';
$hasClass = 0;
if ($classType === 'outercls' && !empty($this->css['outer'])) {
//Set outer class if specified
$returnClass .= $this->css['outer'];
$hasClass = 1;
} elseif ($classType === 'innercls' && !empty($this->css['inner'])) {
//Set inner class if specified
$returnClass .= $this->css['inner'];
$hasClass = 1;
} elseif ($classType === 'rowcls') {
//Set row class if specified
if (!empty($this->css['row'])) {
$returnClass .= $this->css['row'];
$hasClass = 1;
}
//Set first class if specified
if ($first && !empty($this->css['first'])) {
$returnClass .= $hasClass ? ' ' . $this->css['first'] : $this->css['first'];
$hasClass = 1;
}
//Set last class if specified
if ($last && !empty($this->css['last'])) {
$returnClass .= $hasClass ? ' ' . $this->css['last'] : $this->css['last'];
$hasClass = 1;
}
//Set level class if specified
if (!empty($this->css['level'])) {
$returnClass .= $hasClass ? ' ' . $this->css['level'] . $level : $this->css['level'] . $level;
$hasClass = 1;
}
//Set parentFolder class if specified
if ($isFolder && !empty($this->css['parent']) && $level < $this->level) {
$returnClass .= $hasClass ? ' ' . $this->css['parent'] : $this->css['parent'];
$hasClass = 1;
}
//Set here class if specified
if (!empty($this->css['here']) && $this->isHere($docId)) {
$returnClass .= $hasClass ? ' ' . $this->css['here'] : $this->css['here'];
$hasClass = 1;
}
//hack customCss
//Set specific class if exists
if (!empty($specificCss)) {
$returnClass .= $hasClass ? ' ' . $specificCss : $specificCss;
$hasClass = 1;
}
}
if ($hasClass) {
$returnClass = ' class="' . $returnClass . '"';
}
return $returnClass;
}
//Add the specified css & javascript chunks to the page
function regJsCss() {
global $modx;
if ($this->debug) {
$this->debugOutput .= '<h3>Processing Css/Js Chunks</h3>';
}
if ($this->cssTpl) {
$cssChunk = $modx->getChunk($this->cssTpl);
if ($cssChunk) {
$modx->regClientCSS($cssChunk);
if ($this->debug) {$this->debugOutput .= '<p>The CSS chunk ' . $this->cssTpl . ' was added to the page.</p>';}
} else {
if ($this->debug) {$this->debugOutput .= '<p>The CSS chunk ' . $this->cssTpl . ' was not found.</p>';}
}
}
if ($this->jsTpl) {
$jsChunk = $modx->getChunk($this->jsTpl);
if ($jsChunk) {
$modx->regClientStartupScript($jsChunk);
if ($this->debug) {$this->debugOutput .= '<p>The JS chunk ' . $this->jsTpl . ' was added to the page.</p>';}
} else {
if ($this->debug) {$this->debugOutput .= '<p>The JS chunk ' . $this->jsTpl . ' was not found.</p>';}
}
}
}
function ultimateParent($id,$top) {
// From UltimateParent - snippet for MODx 0.9x
// March 2006 - sottwell@sottwell.com
global $modx;
if($id==$top || $id==0) { return $id; }
$pid = $modx->getParent($id,1,'id');
if($pid['id'] == $top) { return $id; }
while ($pid['id'] != $top && $pid['id'] != "") {
$id = $pid['id'];
$pid = $modx->getParent($id,1,'id');
if($pid['id'] == $top) { return $id; }
}
return 0; // if all else fails
}
}
?>
The snippet
<?php
/*
::::::::::::::::::::::::::::::::::::::::
Snippet name: Wayfinder
Short Desc: builds site navigation
Version: beta
Authors: Ryan Thrash (vertexworks.com)
Kyle Jaebker (muddydogpaws.com)
Date: August 2, 2006
Changelog:
August 2, 2006 - Initial Public Beta released
::::::::::::::::::::::::::::::::::::::::
Description:
Totally refactored from original DropMenu nav builder to make it easier to
create custom navigation by using chunks as output templates. By using templates,
many of the paramaters are no longer needed for flexible output including tables,
unordered- or ordered-lists (ULs or OLs), definition lists (DLs) or in any other
format you desire.
::::::::::::::::::::::::::::::::::::::::
Example Usage:
[[Wayfinder? &startId=`0`]]
::::::::::::::::::::::::::::::::::::::::
*/
include_once("assets/snippets/wayfinder/wayfinder.inc.php");
if (class_exists('Wayfinder')) {
$wf = new Wayfinder();
} else {
return 'error: Wayfinder class not found';
}
//parameter overrides
if (isset($startUltimateParent)) {
$topDoc = isset($topDoc)? $topDoc: 0;
$wf->id = $wf->ultimateParent($modx->documentIdentifier,$topDoc);
} else {
$wf->id = isset($startId)? $startId: $modx->documentIdentifier;
}
$wf->level = isset($level)? $level: 0;
$wf->ph = isset($ph)? $ph: FALSE;
$wf->debug = isset($debug)? TRUE: FALSE;
$wf->ignoreHidden = isset($ignoreHidden)? $ignoreHidden: FALSE;
$wf->hideSubMenus = isset($hideSubMenus)? $hideSubMenus: FALSE;
isset($removeNewLines)? $wf->ie = '': $wf->ie = "\n";
//Include javascript & css chunks
$wf->cssTpl = isset($cssTpl)? $cssTpl : FALSE;
$wf->jsTpl = isset($jsTpl)? $jsTpl : FALSE;
//get user class definitions
$wf->css['first'] = isset($firstClass)? $firstClass: '';
$wf->css['last'] = isset($lastClass)? $lastClass: 'last';
$wf->css['here'] = isset($hereClass)? $hereClass: 'active';
$wf->css['parent'] = isset($parentClass)? $parentClass: '';
$wf->css['row'] = isset($rowClass)? $rowClass: '';
$wf->css['outer'] = isset($outerClass)? $outerClass: '';
$wf->css['inner'] = isset($innerClass)? $innerClass: '';
$wf->css['level'] = isset($levelClass)? $levelClass: '';
//hack to add custom css
//specify the name of TV from which we get the values
$wf->customCss = isset($customCss) ? "$customCss" : '';
//get fields to output
$wf->textOfLinks = (isset($textOfLinks)) ? $textOfLinks : 'menutitle';
$wf->titleOfLinks = (isset($titleOfLinks)) ? $titleOfLinks : 'pagetitle';
//get user templates
$wf->templates['outerTpl'] = isset($outerTpl) ? $outerTpl : '';
$wf->templates['rowTpl'] = isset($rowTpl) ? $rowTpl : '';
$wf->templates['parentRowTpl'] = isset($parentRowTpl) ? $parentRowTpl : '';
$wf->templates['hereTpl'] = isset($hereTpl) ? $hereTpl : '';
$wf->templates['innerTpl'] = isset($innerTpl) ? $innerTpl : '';
$wf->templates['innerRowTpl'] = isset($innerRowTpl) ? $innerRowTpl : '';
$wf->templates['innerHereTpl'] = isset($innerHereTpl) ? $innerHereTpl : '';
//setup here checking array
$wf->parentTree = $modx->getParentIds($modx->documentIdentifier);
$wf->parentTree[] = $modx->documentIdentifier;
if ($wf->debug) {
$wf->debugOutput .= '<p>Starting Menu from Docid: ' . $wf->id . '<br/>';
$wf->debugOutput .= 'Docids for \'Here\' class checking: ' . implode(', ',$wf->parentTree) . '</p>';
$wf->debugOutput .= '<h3>Chunk Checks</h3>';
}
$wf->checkChunks();
if ($wf->cssTpl || $wf->jsTpl) {
$wf->regJsCss();
}
if ($wf->debug) {
$wf->debugOutput .= '<h3>Document Processing</h3>';
}
$output = $wf->buildMenu($wf->id);
if ($wf->debug) {
$output = $output . $wf->debugOutput;
}
if ($ph) {
$modx->setPlaceholder($ph,$output);
} else {
return $output;
}
:-)