We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 22851
    • 805 Posts
    Navigator orders documents by menuindex.

    By the way, I notice in your snippet call that transcend is set to 3. The transcend parameter only specifies whether or not to transcend levels, not how many levels.

    Thanks for the info. That was enough to help me diagnose and correct several problems. I was using MODx api calls that only returned information about published or active documents, when I needed info about all documents. I think it’s fixed now. Please could you replace the contents of your navigator.php file with the following and tell me if it works for you? If so, I’ll upload this bug fix version to the MODx repository.

    <?php
    
    // ===================================================================
    // Snippet: Navigator
    // ===================================================================
    // Version: 0.1.2
    // Date: 2009.06.02
    // Author: PMS
    // Licence: GPL GNU - Public
    // Credit: I used some code from the Wayfinder snippet when figuring out how to do templating.
    
    // Version History:
    // 0.1   - initial release
    // 0.1.1 - converted to be PHP 4 compatible
    // 0.1.2 - fixed a bug affecting skipping of unpublished documents
    class Navigator
    {
    
        // Internal parameters
        var $relVals;
        var $weblinkActionVals;
        var $unpublishedActionVals;
        var $notInMenuActionVals;
        var $placeHolderFieldVals;
    
        // User supplied parameters
        var $rel;
        var $stopIds;
        var $offIds;
        var $transcend;
        var $weblinkAction;    
        var $unpublishedAction;    
        var $notInMenuAction;    
        var $templateSource;
        var $modx;
    
        // Derived parameters
        var $stopIdArray;
        var $offIdArray;
        var $template;
    
    
        function Navigator( $params )
        {
            global $modx;
            $this->modx = &$modx;
    
            // Set allowed values
            $this->relVals = array('up', 'prev', 'next');
            $this->weblinkActionVals = array('skip', 'stop', 'link');
            $this->unpublishedActionVals = array('skip', 'stop', 'link');
            $this->notInMenuActionVals = array('skip', 'stop', 'link');
            $this->placeHolderFieldVals = array('nav.rel');
            $this->rel = 'up';
    
            // Set default values
            $this->stopIds = '';
            $this->offIds = '';
            $this->transcend = 1;
            $this->weblinkAction = 'skip';    
            $this->unpublishedAction = 'skip';    
            $this->notInMenuAction = 'skip';    
            $this->templateSource = '';
    
            $this->stopIdArray = array();
            $this->offIdArray = array();
    
            if ( array_key_exists( 'rel', $params ) )
            {
                if ( in_array( $params['rel'], $this->relVals ) )
                {
                    $this->rel = $params['rel'];
                }
            }
    
            if ( array_key_exists( 'stopIds', $params ) )
            {
                $this->stopIdArray = explode( ',', str_replace(' ','', $params['stopIds'] ) );
            }
    
            if ( array_key_exists( 'offIds', $params ) )
            {
                $this->offIdArray = explode( ',', str_replace(' ','', $params['offIds'] ) );
            }
    
            if ( array_key_exists( 'transcend', $params ) )
            {
                if ( $params['transcend'] )
                {
                    $this->transcend = 1;
                }
                else
                {
                    $this->transcend = 0;
                }
            }
    
            if ( array_key_exists( 'weblinkAction', $params ) )
            {
                if ( in_array( $params['weblinkAction'], $this->weblinkActionVals ) )
                {
                    $this->weblinkAction = $params['weblinkAction'];
                }
            }
    
            if ( array_key_exists( 'unpublishedAction', $params ) )
            {
                if ( in_array( $params['unpublishedAction'], $this->unpublishedActionVals ) )
                {
                    $this->unpublishedAction = $params['unpublishedAction'];
                }
            }
    
            if ( array_key_exists( 'notInMenuAction', $params ) )
            {
                if ( in_array( $params['notInMenuAction'], $this->notInMenuActionVals ) )
                {
                    $this->notInMenuAction = $params['notInMenuAction'];
                }
            }
    
            if ( array_key_exists( 'template', $params ) )
            {
                $this->templateSource = $params['template'];
            }
    
        }
       
        function Calculate( )
        {
       
            // ======================================================
            // Calculation
            // ======================================================
    
            // Get the current document id
            $currentId = $this->modx->documentIdentifier;
    
            // If the snippet has been switched off for this document, don't display it
            if ( in_array( $currentId, $this->offIdArray ) )
            {
                return '';
            }
          
            // Get the content
            $this->template = $this->GetTemplate( $this->templateSource );
            if ( $this->template === FALSE )
            {
                // Could create a template based on type here...
                return '';
            }      
    
            while ( true )
            {
    
                // Get the parent document id
                $parentId = $this->GetParentId( $currentId );
    
                switch ( $this->rel )
                {
                case 'up':
                    $id = $parentId;
                    break;
                case 'prev':
                    $id = $this->GetPreviousDocId( $currentId );      
                    break;
                case 'next':
                    $id = $this->GetNextDocId( $currentId );      
                    break;
                default:
                    return '';
                }
              
                if ( $id <= 0 )
                {
                    return '';
                }
    
                if ( $this->IsStopId( $id ) )
                {
                    return '';
                }
    
                if ( $this->IsSkipId( $id ) )
                {
                    $currentId = $id;
                }
                else
                {
                    break;
                }
            }
          
    
            // Return the parent's sibling's id
            return $this->GetOutput( $id );      
        }
    
        function GetTemplate( $source )
        {
            // based on a version in Wayfinder 2.0... which was...
            // based on version by Doze at http://modxcms.com/forums/index.php/topic,5344.msg41096.html#msg41096
            $template = '';
            if ( $this->modx->getChunk( $source ) != '')
            {
                $template = $this->modx->getChunk( $source );
            }
            else if ( substr($tpl, 0, 6) == '@FILE:' )
            {
                $template = $this->GetFileContents( substr( $source, 6 ) );
            }
            else if ( substr($tpl, 0, 6) == '@CODE:' )
            {
                $template = substr( $source, 6 );
            }
            else
            {
                $template = FALSE;
            }
            return $template;
        }
    
        function GetFileContents( $filename )
          {
          // Function written at http://www.nutt.net/2006/07/08/file_get_contents-function-for-php-4/#more-210
          // Returns the contents of file name passed
          if ( ! function_exists( 'file_get_contents' ) )
              {
            $fhandle = fopen( $filename, 'r' );
            $fcontents = fread( $fhandle, filesize( $filename ) );
            fclose( $fhandle );
          }
              else
              {
            $fcontents = file_get_contents( $filename );
          }
          return $fcontents;
        }
    
        function GetPlaceHolderFieldsArray( $tpl )
        {
            // Extract the place holders from the document...
            preg_match_all('~\[\+(.*?)\+\]~', $tpl, $matches);
            return array_unique( $matches[1] );
        }
    
        function GetTVArray()
        {
            // Gets an array of all template variables
            $table = $this->modx->getFullTableName('site_tmplvars');
            $tvs = $this->modx->db->select('name', $table);
            // TODO: make it so that it only pulls those that apply to the current template
            $dbfields = array();
            while ( $dbfield = $this->modx->db->getRow( $tvs ) )
            {
                $dbfields[] = $dbfield['name'];
            }
            return $dbfields;
        }
    
        function GetOutput( $id )
        {        
            // Creates the output in the given format.
    
            // Check that the id is valid
            if ( $id <= 0 )
            {
                return '';
            }
            
            $placeHolderFieldsArray = $this->GetPlaceHolderFieldsArray( $this->template );
    
            // Handle any navigator placeholders
            $nlPlaceHoldersArray = array();
            $nlPlaceHolderValuesArray = array();
            foreach ( $placeHolderFieldsArray as $item => $field )
            {
                if ( in_array( $field, $this->placeHolderFieldVals ) )
                {
                    $nlPlaceHoldersArray[] = '[+' . $field . '+]';
                    // Get the values...
                    switch ( $field )
                    {
                    case 'nav.rel':
                        $nlPlaceHolderValuesArray[] = $this->rel;
                        break;
                    default:
                        return '';
                    }
                    // Remove this field from the placeHolderFieldsArray
                    unset( $placeHolderFieldsArray[$item] );
                }
            }
    
            $tvArray = $this->GetTVArray();
    
            $tvPlaceHoldersArray = array();
            $tvPlaceHolderFieldsArray = array();
            foreach ( $placeHolderFieldsArray as $item => $field )
            {
                if ( in_array( $field, $tvArray ) )
                {
                    $tvPlaceHoldersArray[] = '[+' . $field . '+]';
                    $tvPlaceHolderFieldsArray[] = $field;
                    // Remove this field from the placeHolderFieldsArray
                    unset( $placeHolderFieldsArray[$item] );
                }
            }
    
            // Template variables
            $tvPlaceHolderValuesArray = array();
            if ( count( $tvPlaceHoldersArray ) > 0 )
            {
                $tvPlaceHolderValuesArray = array_values(
                    $this->modx->getTemplateVarOutput(
                        $tvPlaceHolderFieldsArray
                        , $id
                        )
                    );
            }
                
            $docPlaceHoldersArray = array();
            $docPlaceHolderFieldsArray = array();
            foreach ( $placeHolderFieldsArray as $item => $field )
            {
                // if ( in_array( $field, $tvArray ) )
                // {
                    $docPlaceHoldersArray[] = '[+' . $field . '+]';
                    $docPlaceHolderFieldsArray[] = $field;
                    // Remove this field from the placeHolderFieldsArray
                    // unset( $placeHolderFieldsArray[$item] );
                // }
            }
    
            // Get the data....
            // Template variables
            $docPlaceHolderValuesArray = array();
            if ( count( $docPlaceHolderFieldsArray ) > 0 )
            {
                $docPlaceHolderValuesArray = array_values(
                    $this->modx->getPageInfo(
                        $id
                        , 0
                        , implode(',',$docPlaceHolderFieldsArray)
                        )
                    );
            }
    
            return str_replace(
                array_merge(
                    $nlPlaceHoldersArray
                    , $tvPlaceHoldersArray
                    , $docPlaceHoldersArray
                    )
                , array_merge(
                    $nlPlaceHolderValuesArray
                    , $tvPlaceHolderValuesArray
                    , $docPlaceHolderValuesArray
                    )
                , $this->template
                );
          
        }
        
       function IsSkipId( $id )
       {
            if ( $id < 0 )
            {
                return TRUE;
            }
            if ( $id == 0 )
            {
                return FALSE;
            }
            $doc = $this->modx->getPageInfo(
              $id
              , 0
              , 'type,published,hidemenu,deleted' );
            if ( !is_array( $doc ) )
            {
                return TRUE;
            }
            if ( $doc['deleted'] )
            {
                return TRUE;
            }
            if ( $this->weblinkAction == 'skip' && $doc['type'] == 'reference')
            {
                return TRUE;
            }
            if ( $this->unpublishedAction == 'skip' && ! $doc['published'])
            {
                return TRUE;
            }
            if ( $this->notInMenuAction == 'skip' && $doc['hidemenu'] )
            {
                return TRUE;
            }
            return FALSE;
            
        }
    
        function IsStopId( $id )
        {
            if ( $id <= 0 )
            {
                return TRUE;
            }
            if ( in_array( $id, $this->stopIdArray ) )
            {
                return TRUE;
            }
            $doc = $this->modx->getPageInfo(
              $id
              , 0
              , 'type,published,hidemenu,deleted'
              );
            if ( !is_array( $doc ) )
            {
                return TRUE;
            }
            if ( $doc['deleted'] )
            {
                return FALSE;
            }
            if ( $this->weblinkAction == 'stop' && $doc['type'] == 'reference')
            {
                return TRUE;
            }
            if ( $this->unpublishedAction == 'stop' && ! $doc['published'])
            {
                return TRUE;
            }
            if ( $this->notInMenuAction == 'stop' && $doc['hidemenu'] )
            {
                return TRUE;
            }
            return FALSE;
            
        }
    
        function GetParentId( $id )
        {
            // Gets the id of the parent.
            // If the document is the root, and so has no parent, -1 is returned
            if ( $id <= 0 )
            {
                return -1;
            }
            $currentDoc = $this->modx->getPageInfo(
              $id
              , 0
              , 'parent'
              );
            return $currentDoc['parent'];
        }
    
        function GetFirstChildId( $id )
        {
            // Gets the id of the first child of the document
            // Returns -1 if it can't find one
    
            $firstChildId = -1;
    
            // Get the children
            $children = $this->modx->getAllChildren(
              $id
              , 'menuindex'
              , 'ASC'
              , 'id'
              );
    
            // Calculate the number of children
            $nChildren = count( $children );
    
            if ( $nChildren > 0 )
            {
                // Get the id of the first child
                $firstChildId = $children[0]['id'];
                return $firstChildId;
            }
    
            return $firstChildId;
    
        }
    
        function GetLastChildId( $id )
        {
            // Gets the id of the last child of the document
            // Returns -1 if it can't find one
    
            $lastChildId = -1;
    
            // Get the children
            $children = $this->modx->getAllChildren(
              $id
              , 'menuindex'
              , 'ASC'
              , 'id'
              );
    
            // Calculate the number of children
            $nChildren = count( $children );
    
            if ( $nChildren > 0 )
            {
                // Get the id of the last child
                $lastChildId = $children[$nChildren-1]['id'];
    
                return $lastChildId;
            }
    
            return $lastChildId;
    
        }
    
        function GetSiblingId( $id )
        {
            // Gets the next sibling.
            // Returns -1 if one doesn't exist
    
            $siblingId = -1;
    
            // If the document is the document root, then it has no siblings
            if ( $id == 0 )
            {
                return -1;
            }
    
            // Get the parent document id
            $parentId = $this->GetParentId( $id );
    
            // Get the immediate siblings (and the current document)
            $siblings = $this->modx->getAllChildren(
              $parentId
              , 'menuindex'
              , 'ASC'
              , 'id'
              );
    
            // Calculate the number of siblings
            $nSiblings = count( $siblings ) - 1;
    
            $currentIndex = -1;
            // Find the current document in the list of siblings
            foreach ( $siblings as  $index => $sibling )
            {
                if ( $sibling['id'] == $id )
                {
                    $currentIndex = $index;
                    break;
                }
            }
            if ($currentIndex == -1)
            {
                return $siblingId;
            }
    
            switch ( $this->rel )
            {
            case 'prev':
                if ( $currentIndex > 0 )
                {
                    $siblingId = $siblings[$currentIndex-1]['id'];
                    return $siblingId;
                }
                break;
            case 'next':
                if ( $currentIndex < $nSiblings )
                {
                    $siblingId = $siblings[$currentIndex+1]['id'];
                    return $siblingId;
                }
                break;
            default:
                return $siblingId;
            }
    
            return $siblingId;
        }
    
        function GetPreviousDocId( $id )
        {
            if ( $this->transcend )
            {
                // If the current document has a previous sibling, then get its last child
    
                // Check if the current document has a previous sibling
                $siblingId = $this->GetSiblingId( $id );
    
                // If it has a sibling...
                while ( $siblingId >= 0 )
                {
                    // Get the last child of the document
                    $lastChildId = $this->GetLastChildId( $siblingId );
    
                    // If it doesn't have a child, then stop here
                    if ( $lastChildId < 0 )
                    {
                        return $siblingId;
                    }
    
                    // Go down the tree
                    $siblingId = $lastChildId;
    
                }
    
                // The current document doesn't have a previous sibling, so get its parent.
                $parentId = $this->GetParentId( $id );
    
                if ( $parentId >= 0 )
                {
                    return $parentId;
                }
    
                return -1;         
            }
            else
            {
    
                // A sibling was required. Get the sibling
                $siblingId = $this->GetSiblingId( $id );
    
                // If the sibling has been found, then create the link
                if ( $siblingId >= 0 )
                {
                    return $siblingId;
                }
    
                return -1;
            }
    
            return -1;
        }
    
        function GetNextDocId( $id )
        {
            if ( $this->transcend )
            {
                // If the current document has children, then get the first child.
                $firstChildId = $this->GetFirstChildId( $id );
    
                if ( $firstChildId >= 0 )
                {
                    // Return the first page
                    return $firstChildId;
                }
    
                // otherwise, try to get the next sibling.
                $siblingId = $this->GetSiblingId( $id );
    
                if ( $siblingId >= 0)
                {
                    // Return the first page
                    return $siblingId;
                }
    
                // If there is no next sibling, get the parent's sibling.
                $parentId = $this->GetParentId( $id );
    
                // Check if the parent is valid
                while ( $parentId >= 0 )
                {
                    $parentsSiblingId = $this->GetSiblingId( $parentId );
    
                    if ( $parentsSiblingId >= 0 )
                    {
                        // Return the parent's sibling's id
                        return $parentsSiblingId;
                    }
    
                    // Repeat the last step until completion
                    $parentId = $this->GetParentId( $parentId );
                }      
    
                return -1;
    
            }
            else
            {
    
                // A sibling was required. Get the sibling
                $siblingId = $this->GetSiblingId( $id );
    
                // If the sibling has been found, then create the link
                if ( $siblingId >= 0 )
                {
                    return $siblingId;
                }
    
                return -1;
            }
    
            return -1;
        }
    
    
    }
    ?>


    Thanks.
      YAMS: Yet Another Multilingual Solution for MODx
      YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
      Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
      • 1841
      • 141 Posts
      I have updated the navigator.php and it works perfectly!!! Thank you very much for this, I have been trying to get this sort of thing working since last year and tied prevnext and prevjumpnext and even tried modifying them.

      Thanks for the heads up with transcend, didn’t realise.

      Again, thank you very much.
        • 1343 ☆ A M B ☆
        • 2,213 Posts
        Does anyone have the latest release? The repository shows version 0.1 (http://modxcms.com/extras/package/314) instead of the latest release and deprecated.

        Or am I missing something?
          Patrick | Server Wrangler
          About Me: Website | Tweets |  MODX Hosting
          • 22851
          • 805 Posts
          I changed the status of version 0.1 to deprecated because it has known bugs in it. I submitted an update to Navigator which corrects those bugs to the MODx repository several weeks ago, but it has not yet been approved. I’ll email a copy of the latest version I submitted to the repository.
            YAMS: Yet Another Multilingual Solution for MODx
            YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
            Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
            • 22851
            • 805 Posts
            If anyone from MODx is listening in, please could you check what’s happened to the update to Navigator that I submitted to the repository over a month ago? It hasn’t been released. I’d be happy to resubmit it if necessary, but, assuming there has been some kind of bug in the system I don’t want to make it twice as bad by doing that without asking first.

            Thanks.
              YAMS: Yet Another Multilingual Solution for MODx
              YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
              Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
              • 25663 MODX Staff
              • 12,272 Posts
              Thanks for bringing that to our attention PMS. Should be out there now.
                Ryan Thrash, MODX Co-Founder
                Follow me on Twitter at @rthrash or catch my occasional unofficial thoughts at thrash.me
                • 22851
                • 805 Posts
                Thank you Ryan.

                By the way, before anyone asks: Yes, I did make a complete hash of the version numbers, with 0.1.3 and 1.0.3 etc. appearing all over the place. Let’s call it v1.0.3 then. I’ll tidy up the version in the repository later today to sort it out.
                  YAMS: Yet Another Multilingual Solution for MODx
                  YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
                  Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
                  • 11920
                  • 8 Posts
                  The snippet doesn’t show properly. The next link for the parent goes to the next parent in the menu structure. I want it to go the first child.

                  http://www.climatec.com/index.php?id=27

                  [!Navigator? &rel=`next`&template=`NextChunk`&transcend=`0`&notInMenuAction=`link`&unpublishedAction=`link`!]


                    • 22851
                    • 805 Posts
                    Quote from: heather at Sep 17, 2009, 04:48 PM

                    The snippet doesn’t show properly. The next link for the parent goes to the next parent in the menu structure. I want it to go the first child.

                    http://www.climatec.com/index.php?id=27

                    [!Navigator? &rel=`next`&template=`NextChunk`&transcend=`0`&notInMenuAction=`link`&unpublishedAction=`link`!]


                    Sounds like you want the snippet to transcend levels, so you’ll need to use &transcend=`1`.
                      YAMS: Yet Another Multilingual Solution for MODx
                      YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
                      Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
                      • 11920
                      • 8 Posts
                      That was it! Thanks!

                      I misunderstood what it meant by transcend. I though it would only show the parent and it’s children and then stop. Now, I understand.