<?php /** * NavigateSiblings * * Flexible prev/next/parent navigation for a document with siblings. You can * use it to output templated links, or just set placeholders * * @author Jason Coward, <[email protected]>. * @version 1.0 * * Example usage: * * * [!NavigateSiblings? &mode=`direct`!] -- returns the output directly in place * of the snippet; it is assumed you are using chunk templates for this mode. * This is also the default mode if not specified. * * [!NavigateSiblings? &mode=`template`!] -- sets placeholders using template * mode by using the default template chunk names, prevSibling, nextSibling, and * navSiblingsParent, and producing output to placeholders of the same name as * the chunk: navSiblingsPrev, navSiblingsNext, and navSiblingsParent. * * [!NavigateSiblings? &mode=`urlonly`!] -- no output is returned, and * no templates are processed; instead, the snippet only sets the values of * placeholders for each link URL: prevSiblingUrl, nextSiblingUrl, and * navSiblingParentUrl. */ /* * Define the mode of the snippet: * * &mode=`template` -- will produce placeholders containing templates within * which the URL placeholders can be used (default mode). * * &mode=`direct` -- will output the template chunks in the place where the * snippet is called within your content. * * &mode=`urlonly` -- will only produce placeholders for the navigation URLs. * */ if (!isset ($mode) || ($mode !== 'template') || $mode !== 'urlonly') { $mode= 'direct'; } /* * Define a chunk representing the presentation of the previous sibling link. * &prevChunk=`myPrevSiblingChunkName` */ if (!isset ($prevChunk)) { $prevChunk= 'prevSibling'; } /* * Define a chunk representing the presentation of the next sibling link. * &nextChunk=`myNextSiblingChunkName` */ if (!isset ($nextChunk)) { $nextChunk= 'nextSibling'; } /* * Determine whether or not to show a link back to the parent. * &showParent=`0` -- will disable this, which is set to 1 by default. */ if (!isset ($showParent)) { $showParent= 1; } /* * Define a chunk representing the presentation of the parent link. * &parentChunk=`myNavParentChunkName` */ if (!isset ($parentChunk)) { $parentChunk= 'navSiblingsParent'; } $output= ''; $prevOut= ''; $parentOut= ''; $nextOut= ''; $parentId= (string) $modx->documentObject['parent']; if (!function_exists('filterNonSiblingsFromDocumentMap')) { function filterNonSiblingsFromDocumentMap($var) { global $modx; return (isset ($var[$modx->documentObject['parent']])); } } $siblings= array_filter($modx->documentMap, 'filterNonSiblingsFromDocumentMap'); $currentKey= false; $prevSibling= false; $nextSibling= false; $firstSibling= current($siblings); $currentSibling= $firstSibling; while ($currentSibling !== false) { if (intval($currentSibling[$parentId]) === intval($modx->documentIdentifier)) { $currentKey= key($siblings); break; } $currentSibling= next($siblings); } $lastSibling= end($siblings); if ($currentKey) { if (intval($firstSibling[$parentId]) !== intval($modx->documentIdentifier)) { $modx->setPlaceholder('prevSiblingUrl', $modx->makeUrl($siblings[$currentKey - 1][$parentId])); $prevOut= !empty ($prevChunk) ? $modx->getChunk($prevChunk) : ''; } if ($showParent) { $modx->setPlaceholder('navSiblingParentUrl', $modx->makeUrl($parentId)); $parentOut= !empty ($parentChunk) ? $modx->getChunk($parentChunk) : ''; } if (intval($lastSibling[$parentId]) !== intval($modx->documentIdentifier)) { $modx->setPlaceholder('nextSiblingUrl', $modx->makeUrl($siblings[$currentKey + 1][$parentId])); $nextOut= !empty ($nextChunk) ? $modx->getChunk($nextChunk) : ''; } } if ($mode == 'template') { if (!empty ($prevChunk)) { $this->setPlaceholder($prevChunk, $prevOut); } if (!empty ($nextChunk)) { $this->setPlaceholder($nextChunk, $nextOut); } if (!empty ($parentChunk)) { $this->setPlaceholder($parentChunk, $parentOut); } } elseif ($mode == 'direct') { $output.= $prevOut . "\n"; $output.= $parentOut . "\n"; $output.= $nextOut . "\n"; } return $output; ?>
<a href="[+prevSiblingUrl+]" title="Go to previous page">Prev</a>
<a href="[+nextSiblingUrl+]" title="Go to next page">Next</a>
<a href="[+navSiblingsParentUrl+]" title="Go to the parent page">Index</a>

<?php
if (!function_exists('filterNonSiblingsFromDocumentMap')) {
function filterNonSiblingsFromDocumentMap($var) {
global $modx;
return (isset ($var[$modx->documentObject['parent']]));
}
}
$siblings= array_filter($modx->documentMap, 'filterNonSiblingsFromDocumentMap');
?>
<?php
$siblings= array_filter($modx->documentMap,
create_function( '$var', 'global $modx; return (isset ($var[$modx->documentObject["parent"]]));' )
);
?>Good point TobyL, and thanks for pointing that out. Support for that could probably be added with minimal query work. I’ll see about that when I get a chance.
Nice snippet! However be aware that pages not listed in the menu (as for instance the 404 error page) will show up in the previous / next links! Just so you know...
Hmmm, IMO, I’d rather check for it’s existence in this case and simply reuse the existing function definition if this gets called multiple times on a page, rather than adding the overhead of registering a new function with the PHP interpreter on every instance. Not that it would be used more than twice per page in any situation I can think of, but. I’ll research this a little more later on and see what the real benefits and drawbacks of each approach are.
Also Jason, wouldn’t the below function be a prime candidate for PHP’s create_function()? Instead you’d basically hae a one-liner doing the same thing and no need to test if the function exists.
[[NavigateSibling? &mode=`template`]]
[+nextSibling:ifempty=`[+nextButton+]`+]
That looks like the problem indeed, so instead of returning a real blank value, the placeholder is left for later processing, and so is not empty in the eyes of that PHx statement. You could try making the default values in the snippet code for each of the values to be set as placeholders with an HTML comment, e.g.
To avoid "jumping" sibling numbers when there is no next sibling (that means the "next button" is not shown), I want to output the same button without a link and dimmed grey. To achieve this, I’m using PHx:
[+nextSibling:ifempty=`[+nextButton+]`+]
PHx is correctly installed and works in other places of my site. But here, the content of the "empty" TV is not output. So I suspect the placeholder to not be empty but unset. May this be so?
<!-- Empty previous value -->
[+nextSibling:ifempty=`[+nextButton+]`+]
[+nextSibling:ifempty=`{{nextButton}}`+]