We use cookies to improve your experience. No personal information is gathered and we don't serve ads. Cookies Policy.

ExpressionEngine Logo ExpressionEngine
Features Pricing Support Find A Developer
Partners Upgrades
Blog Add-Ons Learn
Docs Forums University
Log In or Sign Up
Log In Sign Up
ExpressionEngine Logo
Features Pro new Support Find A Developer
Partners Upgrades
Blog Add-Ons Learn
Docs Forums University Blog
  • Home
  • Forums

New Module: Structure - Uses entries to create a page hierarchy for static and listing pages

Development and Programming

Brian Litzinger's avatar
Brian Litzinger
693 posts
16 years ago
Brian Litzinger's avatar Brian Litzinger
* main 1 * main 2 * sub 1 * sub 2 * sub 3 * subsub 1 * subsub 2 (=Here) * subsubsub1 * subsubsub2 * subsub 3 * sub 4 * sub 5 * main 3 * main 4

Yeah, option for the main and sub nav to show it’s children is huge.

       
Brian Litzinger's avatar
Brian Litzinger
693 posts
16 years ago
Brian Litzinger's avatar Brian Litzinger

And one more bug/change. The URI isn’t being cleaned the same as EE’s default url_title. Foo/Bar turns into foobar, where as EE’s default behavior is turning it into foo-bar or foo_bar, depending on the URL separation character you have defined.

       
Brian Litzinger's avatar
Brian Litzinger
693 posts
16 years ago
Brian Litzinger's avatar Brian Litzinger

Here is a fix to show the children pages in the main_nav method. Was an easy fix. I’m sure this method could be optimized a bit. This was literally a 5 minute hack job. I just copied the sub_nav method and made a couple of changes.

Change your nav_main method call to:

{exp:structure:nav_main exclude_status="hidden|no_main_nav" show_children="true"}

In nav_main method around line 123 add/change to:

$html .= ">";
            $html .= "<a >" . $entry_data['title'];
            $html .= "</a>";
            if( $show_children == 'true' and $here == 'here'  ) {
                $html .= $this->get_children($entry_data['entry_id'], $exclude_status_list); 
            }
            $html .= "</li>";
       
Brian Litzinger's avatar
Brian Litzinger
693 posts
16 years ago
Brian Litzinger's avatar Brian Litzinger

Add this to mod.structure.php

function get_children($entry_id, $exclude_status_list) {
    global $DB, $IN, $PREFS, $TMPL;
    
    if($PREFS->ini('word_separator') == 'dash') {
        $separator = "-";
    } else if ($PREFS->ini('word_separator') == 'underscore') {
        $separator = "_";
    }
       
Brian Litzinger's avatar
Brian Litzinger
693 posts
16 years ago
Brian Litzinger's avatar Brian Litzinger

2nd half of the method:

// get site pages data
    $site_pages = $PREFS->ini('site_pages');
    // get current uri path
    $uri = $IN->URI;
    // get node of the current entry
    $node = $start_node = $entry_id ? $this->nset->getNode($entry_id) : false;
    
    // node does not have any structure data we return nothing to prevent errors
    if ($node === false && ! $entry_id) {
        return '';
    }
    
    // if we have an entry id but no node, we have listing entry
    if ($entry_id && ! $node) {
        // get entry's parent id
        $pid = $this->get_pid_for_listing_entry($entry_id);
        
        // get node of parent entry
        // because we will be showing nav sub from its view point
        $node = $start_node = $this->nset->getNode($pid);
    }
    
    // if we are on a root that is a leaf
    // no sub nav to return
    if ($node && $node['isLeaf'] && $node['depth'] == 0) {
        return '';
    }
    
    if ($node['isLeaf'] && $node['depth'] > 1) {
        // we are on a child leaf past the root level
        // so we use the parent as the starting point for the sub nav
        $start_node = $this->nset->getNode($node['parent_id']);
    }
    
    // get siblings
    $where = 'node.parent_id = ' . ($start_node['depth'] ? $start_node['parent_id'] : $start_node['id']);
    // get children
    $where .= ' OR node.parent_id = ' . $start_node['id'];
    
    $entry_depth = $start_node['depth'] ? $start_node['depth'] : 1;
    
    // get structure data from DB            
    $sql = "SELECT node.*, 
                (COUNT(parent.entry_id) - 1) AS depth, 
                ((COUNT(parent.entry_id) - 1) - $entry_depth) AS subDepth, 
                if((node.rgt - node.lft) = 1,1,0) AS isLeaf, 
                expt.title,
                expt.status 
            FROM exp_structure AS node
            INNER JOIN exp_structure AS parent 
                ON node.lft BETWEEN parent.lft AND parent.rgt 
            INNER JOIN exp_weblog_titles AS expt 
                ON node.entry_id = expt.entry_id
            WHERE parent.lft > 1 
                AND ($where) 
            GROUP BY node.entry_id 
            ORDER BY node.lft";
    $result = $DB->query($sql);
    
    // check how many entries at subDepth 0
    $num = 0;
    foreach ($result->result as $entry) {
        if ($entry['subDepth'] == 0) {
            $num++;
        }
    }
    // if we only have 1 subDepth 0 entry and it has children
    // then we just show the children
    if ($num === 1 && $result->num_rows > 1) {
        unset($result->result[0]);
        foreach ($result->result as &$entry) {
            $entry['subDepth'] -= 1;
        }
    }
     
    // Remove anything to be excluded from the results array
    foreach ($result->result as $key => $entry_data) {
        if( in_array( strtolower($entry_data['status']), $exclude_status_list) ) {
            unset($result->result[$key]);
        }
    }
     
    if(count($result->result) > 0) {
        $html = "<ul>";
        $prev_subDepth = false;
        $ul_open       = false;
        
        foreach ($result->result as $entry_data) {
            if ($prev_subDepth === 0 && $entry_data['subDepth'] == 1) {
                $html = substr($html, 0, -5);
                $html .= "\n<ul>";
                $ul_open = true;
            } else if ($prev_subDepth === 1 && $entry_data['subDepth'] == 0) {
                // Finds the previous entry
                preg_match("~<li class=\"(.+)\"><(.+)\</li>~", $list_item, $matches);
                // Creates the new class entry
                $last_item_class = $matches[1] . " last";
                // Stores the inner of the <li>
                $last_item_inner = $matches[2];
                // Replace the string
                $html = str_replace($list_item, "\n<li class=\"$last_item_class\"><$last_item_inner</li>", $html);
                $html .= "\n</ul></li>";
                $ul_open = false;
            }
            // out entry uri of this loop instance
            $euri = $site_pages['uris'][$entry_data['entry_id']];
            // current page's parent uri
            $puri = $node['parent_id'] ? $site_pages['uris'][$node['parent_id']] : '';
        
            $here = '';
            if ($euri === $puri) {
                $here = ' parent' . $separator . 'here';
            } else if ($uri === $euri) {
                $here = ' here';
            }
            
            $last = '';
            if($entry_data == end($result->result)) {
                $last = ' last';
            }
        
            // Make sure we have the site_url path in case we're operating in a subdirectory
            // If site_index is set then add it to URIs, otherwise leave it blank
            //$root_uri = parse_url($PREFS->ini('site_url'), PHP_URL_PATH); // req. PHP v5.1.2
            $the_uri = parse_url($PREFS->ini('site_url'));
            $root_uri = $the_uri['path'];
            $index_uri = $PREFS->ini('site_index');
            //$index_uri = $PREFS->ini('site_index') ? "/" . $PREFS->ini('site_index') : "";
            $item_uri =    str_replace('//', '/', $root_uri . $index_uri . $euri);
        
            $list_item = "\n<li class=\"sub" . $separator . "level" . $separator . $entry_data['subDepth'] . $here . $last . "\">";
            $list_item .= "<a >" . $entry_data['title'];
            $list_item .= "</a></li>";
        
            $html .= $list_item;
        
            $prev_subDepth = (int)$entry_data['subDepth'];
        }
        $html .= $ul_open ? "\n</ul></li></ul>" : "\n</ul>";
    }
    
    return $html;
}
       
Brian Litzinger's avatar
Brian Litzinger
693 posts
16 years ago
Brian Litzinger's avatar Brian Litzinger

Ok, one more modification. I needed to show the category title above my nav links like so:

Category One — Page 1 — Page 2 — Page 3 Category Two — Page 4 — Page 5

Here is a new nav_main method with the option to print the the pages (first) category as a header above the links with the option to link the category title itself.

{exp:structure:nav_main exclude_status="hidden|no_main_nav" show_children="true" show_category_title="true" link_category_title="true"}
       
Brian Litzinger's avatar
Brian Litzinger
693 posts
16 years ago
Brian Litzinger's avatar Brian Litzinger
function nav_main() {
    global $DB, $IN, $OUT, $PREFS, $TMPL;
    
    if($PREFS->ini('word_separator') == 'dash') {
        $separator = "-";
    } else if ($PREFS->ini('word_separator') == 'underscore') {
        $separator = "_";
    }
    
    // get site ID
    $site_id = $PREFS->ini('site_id');
    // get current uri path
    $uri = $IN->URI;
    
    // Retrieve status labels to exclude
    // break apart string at pipe chars and build array
    $exclude_status = $TMPL->fetch_param('exclude_status');
    $exclude_status_list = split("\|", strtolower($exclude_status));
    
    $show_children = strtolower($TMPL->fetch_param('show_children'));
    $show_category_title = strtolower($TMPL->fetch_param('show_category_title'));
    $link_category_title = strtolower($TMPL->fetch_param('link_category_title'));
    
    // get site pages data
    //$site_pages = $PREFS->core_ini['site_pages'];
    $site_pages = $PREFS->ini('site_pages');
    
    // get structure data from DB    
    $sql = "SELECT node.*, 
                expt.title,
                expt.status 
            FROM exp_structure AS node
            INNER JOIN exp_weblog_titles AS expt 
                ON node.entry_id = expt.entry_id
            WHERE node.parent_id = 0 
                AND node.site_id = $site_id
            GROUP BY node.entry_id 
            ORDER BY node.lft";
    $result = $DB->query($sql);

    // Remove anything to be excluded from the results array
    foreach ($result->result as $key => $entry_data) {
        if( in_array( strtolower($entry_data['status']), $exclude_status_list) ) {
            unset($result->result[$key]);
        }
    }
    
    $segment_1 = $IN->fetch_uri_segment('1');

    if ($OUT->out_type == '404') { $page_not_found = true; } else { $page_not_found = false; }
     
    if(count($result->result) > 0) {
        $html = "<ul id=\"nav\">";
        $ul_open       = false;
        foreach ($result->result as $entry_data) {
            // out entry uri of this loop instance
            $euri = $site_pages['uris'][$entry_data['entry_id']];
            // current page's parent uri
            $puri = $node['parent_id'] ? $site_pages['uris'][$node['parent_id']] : '';

            $here = '';
            if ($uri === $euri || ($uri == '' && $euri == '/') || $segment_1 === trim($euri, '/') && !$page_not_found ) {
                $here = 'here';
            }
            
            $last = '';
            if($entry_data == end($result->result)) {
                $last = 'last';
                if($here != '') { $last = ' ' . $last; }
            }
            
            // get category title
            $category_name = '';
            if( $show_category_title == 'true' )
            {                                
                $cat_sql = "SELECT exp_categories.cat_name AS category_name,
                            exp_categories.cat_url_title AS category_url_title
                            FROM exp_categories
                            JOIN exp_category_posts
                            ON exp_category_posts.cat_id = exp_categories.cat_id
                            WHERE exp_category_posts.entry_id = ${entry_data['entry_id']}
                            GROUP BY exp_category_posts.cat_id";

                $cat_result = $DB->query($cat_sql);
                $category_name = $cat_result->result[0]['category_name'];
                $category_url_title = $cat_result->result[0]['category_url_title'];
                
                $blog_sql = "SELECT blog_name FROM exp_weblogs WHERE weblog_id = ${entry_data['weblog_id']}";
                $blog_result = $DB->query($blog_sql);
                $blog_name = $blog_result->result[0]['blog_name'];
            }
        
            // Make sure we have the site_url path in case we're operating in a subdirectory
            // If site_index is set then add it to URIs, otherwise leave it blank
            //$root_uri = parse_url($PREFS->ini('site_url'), PHP_URL_PATH); // req. PHP v5.1.2
            $the_uri = parse_url($PREFS->ini('site_url'));
            $root_uri = $the_uri['path'];
            $index_uri = $PREFS->ini('site_index');
            //$index_uri = $PREFS->ini('site_index') ? "/" . $PREFS->ini('site_index') : "";
            $item_uri =    str_replace('//', '/', $root_uri . $index_uri . $euri);
            
            if( $show_category_title == 'true' and $previous_category != $category_name ){
                if( $link_category_title == 'true' ){
                    $category_title = "<a href="http://.">ini('reserved_category_word'). "/" .$category_url_title .">". $category_name ."</a>";
                } else {
                    $category_title = $category_name;
                }
                $html .= "\n<li class=\"category\">". $category_title ."</li>";
            }
            
            $html .= "\n<li";
            
            if($here || $last) {
                 $html .= ' class="' . $here . $last . '"';
            }
            
            $html .= ">";
            $html .= "<a >" . $entry_data['title'];
            $html .= "</a>";
            if( $show_children == 'true' and $here == 'here' ) {
                $html .= $this->get_children($entry_data['entry_id'], $exclude_status_list); 
            }
            $html .= "</li>";
            
            if( $show_category_title == 'true'){
                $previous_category = $category_name;
            }
            
            
        }
        
        $html .= $ul_open ? "\n</ul></li></ul>" : "\n</ul>";
        
    }
    //$html .= '<pre>' . $sql . '</pre>

<p>’;
    return $html;
}
</pre>

       
Martin Luff's avatar
Martin Luff
52 posts
16 years ago
Martin Luff's avatar Martin Luff

@Tilzinger

Yes, indeed! I’d add my request for that feature too. If there were a way of producing a ‘sitemap’ nav tree from the Structure pages then that would add an awesome function.

Here’s hoping… (and thanks again Travis for all the work put in)

       
RJB's avatar
RJB
35 posts
16 years ago
RJB's avatar RJB

I’ve found what I would call a “bug” in Structure (version 1.1.1) in that it doesn’t respect the current path of the “themes” directory in ExpressionEngine — or better said, Structure’s CSS needs to be updated so that it doesn’t rely on the Control Panel’s “themes” path.

For example, if the “themes” path in ExpressionEngine is changed from the default then the background images for the Structure Settings” button (CP Home >Modules > Structure) doesn’t appear — instead you’re left with an issue of white text on a white background and the “Settings” button disappearing!!!

This problem drove me nuts for the longest time until I figured out what was wrong. Here’s the two lines of problematic code:

 [b]/modules/structure/css/structure.css[/b]

 [b]Line 94:[/b] background: transparent url(/themes/cp_themes/default/images/bg_button_a.gif) no-repeat top right;
 [b]Line 105:[/b] background: transparent url(/themes/cp_themes/default/images/bg_button_div.gif) no-repeat top left;

Note the dependance upon the ExpressionEngine Control Panel images and path.

The problem should be fixed by either:

 1. Bundling the images with the Structure Module (easiest; preferred)
 2. Making Structure aware of the themes path setting (difficult; not necessary)

Since Structure already bundles its own assets (Images, Style Sheets, JavaScript), there’s absolutely no reason why the background images can’t be included along with the other assets found in /modules/structure/img.

While this situation is rare and only affects those who have changed the default path of the ‘themes’ directory, the entire problem can (and should) be avoided by fixing two lines of code.

       
rockthenroll's avatar
rockthenroll
485 posts
16 years ago
rockthenroll's avatar rockthenroll

@Ryan J. Bonnell thanks! We’ll look into this for the next release

       
RJB's avatar
RJB
35 posts
16 years ago
RJB's avatar RJB
@Ryan J. Bonnell thanks! We’ll look into this for the next release

Good to know! For my own needs, I updated the CSS on my own and uploaded the images to the Structure directory and all is well.

       
mkuplens's avatar
mkuplens
6 posts
16 years ago
mkuplens's avatar mkuplens

I’ve got to say that once you work out the quirks and conflicts, Structure is absolutely brilliant. That said, I could do with a bit of guidance:

I’m building out a site with multiple listing pages – since the weblog that’s being listed is selected for the page within the Structure tab, is there a way of reading that in and passing it to the {exp:weblog:entries weblog="pressreleases" dynamic="off"} tag? I’d hate to have to create a separate template for every listing page, only to change the weblog value…

Oh, also: I can’t seem to get pagination to work correctly. It’s generating the correct links, but doesn’t seem to pick up the offset when you click any of them, instead just showing the first n records. The tag configuration I’m trying to use is:

{exp:structure:paginate parse="inward" show_num_pages="3"}
            {exp:weblog:entries weblog="news" dynamic="off" limit="2"}
               <h3><a href="http://{page_url}">{title}</a></h3>
               {entry_date format="%F %d, %Y"}
            {/exp:weblog:entries}
          {/exp:structure:paginate}
       
rockthenroll's avatar
rockthenroll
485 posts
16 years ago
rockthenroll's avatar rockthenroll

@mkuplens You could run all of those listings through the same weblog and just do conditional statements to show the correct weblog tags per the URL. For pagination, try adding offset=”0” to your weblog tag and see if it works then.

       
mkuplens's avatar
mkuplens
6 posts
16 years ago
mkuplens's avatar mkuplens

No joy with that, unfortunately, Travis.

Incidentally, do you have any advice on how to set up RSS feeds of content managed by Structure? My current best guess is that I would create a page that uses an RSS-configuration as its template… but that just seems messy, no?

       
rockthenroll's avatar
rockthenroll
485 posts
16 years ago
rockthenroll's avatar rockthenroll

@mkuplens no reason to involve Structure in that as that’s not really what it’s meant for. Just make a normal template group for that. Structure is really for an easy dynamic page hierarchy and simple interface for your clients. They will never “edit” the RSS feed as it’s drawn automatically from other “listing” weblogs.

       
First 23 24 25 26 27 Last

Reply

Sign In To Reply

ExpressionEngine Home Features Pro Contact Version Support
Learn Docs University Forums
Resources Support Add-Ons Partners Blog
Privacy Terms Trademark Use License

Packet Tide owns and develops ExpressionEngine. © Packet Tide, All Rights Reserved.