<?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 - [email protected]
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
}
}
?>
<?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;
}




This discussion is closed to further replies. Keep calm and carry on.