Wednesday, May 19, 2010 - 10:25
After writing the function that creates a list of pages that are children of a given page in Wordpress I needed something more robust and automatic. To that end I created a plugin that will create a widget that contains a dynamically created menu of pages.
The widget figures out what page is currently being displayed and will climb the page tree until it finds the root page. Whilst climbing the page tree the plugin will keep the path to the currently selected page and when the tree is printed out the path will be open. It is best suited for sites that have a solid hieratic page structure, rather than a simple blogging site.
In terms of efficiency I have tested it with pages nested up to 25 levels deep with only a small decrease in page load. However, for the average small Wordpress site this plugin is perfect as pages will only be nested a few levels deep.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | <?php /** * Plugin Name: Page Menu Navigation * Plugin URI: http://www.hashbangcode.com/ * Description: Adds an intelligent page navigation menu that is dependent on page hierarchy. * Version: 1.0 * Author: Philip Norton * Author URI: http://www.hashbangcode.com/ */ /** * Print out hirachical page structure. * * @global object $post The current post. */ function printPages() { global $post; if ($post->post_type == 'page') { if ($post->post_parent > 0) { $root = findPathInformation($post); $pages = traversePageTree($root['root'], $root['activepath'], $root['depth']); } else { $root = $post; $pages = traversePageTree($root, array($root->ID), 1); } echo printPageTree($pages); } } /** * From a single page find out how deep it is * * @param object $page The current page. * * @return array An array of information about the page and the path. */ function findPathInformation($page) { // Go up the tree and see what is in the path. $reverse_tree = climbPageTree($page); // Flattern the tree to get the current active path. $activePath = flattenTree($reverse_tree); // Make sure current page is in the active path. $activePath[] = $page->ID; $root = $reverse_tree[0]; // Set to 2 as if we are in this code we are in the level just below root. $depth = 2; // Recursivley loop through the pages and find the root page and the depth. while (is_array($root->post_parent)) { ++$depth; $root = $root->post_parent[0]; } return array('root' => $root, 'depth' => $depth, 'activepath' => $activePath); } /** * Flatten the tree into a single array. * * @param array $tree A multi dimensional array of pages. * * @return array A single dimensional array of pages. */ function flattenTree($tree) { $flat = array(); while (is_array($tree[0]->post_parent)) { $flat[] = $tree[0]->ID; $tree = $tree[0]->post_parent; } $flat[] = $tree[0]->ID; return $flat; } /** * Find out if the current page is in the active path. * * @param integer $id The ID of the current page. * @param array $activePath An array of ID's of the pages in the current path. * * @return boolean True if the page is in current path, otherwise false. */ function inActivePath($id, $activePath) { if (in_array($id, $activePath)) { return true; } else { return false; } } /** * Starting with the current page go up level after level until the root page * reached. This function will run one SQL query for every level. * * @global wpdb $wpdb The current Wordpress database connection object. * * @param object $page The current page. * * @return <type> */ function climbPageTree($page) { global $wpdb; $parent = $wpdb->get_results("SELECT ID, post_title, post_parent FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'page' AND ID = " . $page->post_parent . " ORDER BY menu_order, post_title", OBJECT); if (count($parent) > 0) { foreach ($parent as $item => $par) { if ($par->post_parent != 0) { $parent_parent = climbPageTree($par); if ($parent_parent !== false) { $parent[$item]->post_parent = $parent_parent; } } else { // Reached top of tree return $parent; } } } return $parent; } /** * Traverse the page structure and create a tree of the pages. * * @global wpdb $wpdb The current Wordpress database connection object. * * @param object $page The page to start the tree traversal from, usually root. * @param array $activePath An array of page ID's in the active path. * @param integer $maxdepth The maximum depth to follow the traversal * @param integer $depth The current depth of the traversal. * * @return array A tree of pages. */ function traversePageTree($page, $activePath = array(), $maxdepth = 10, $depth = 0) { if ($depth >= $maxdepth) { // We have reached the maximum depth, stop traversal. return array(); } // Get Wordpress db object. global $wpdb; $children = $wpdb->get_results("SELECT ID, post_title, post_parent FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'page' AND post_parent = " . $page->ID . " ORDER BY menu_order, post_title", OBJECT); if (count($children) > 0) { foreach ($children as $item => $child) { if (inActivePath($child->ID, $activePath)) { // Current page is in active path, find the children. $children[$item]->children = traversePageTree($child, $activePath, $maxdepth, $depth + 1); } } } return $children; } /** * Print out the page tree as created by the traversePageTree() function. * * @see traversePageTree() * * @param array $pages A tree of pages. */ function printPageTree($pages) { $class = ''; $output = ''; $output .= "\n<ul>\n"; foreach ($pages as $page) { if (is_page($page->ID) === true) { $class = ' class="on"'; } $output .= "<li" . $class . "><a href=\"" . get_page_link($page->ID) . "\" title=\"" . $page->post_title . "\">" . $page->post_title . "</a>"; $class = ''; if (isset($page->children) && count($page->children) > 0) { $output .= printPageTree($page->children); } $output .= "</li>\n"; } $output .= "</ul>\n"; return $output; } /** * Widget function * * @param array $args */ function pageMenuNavigationWidget($args) { extract($args); echo '<div id="subNav">'; echo printPages(); echo '</div>'; } register_sidebar_widget(__('Page Menu Navigation'), 'pageMenuNavigationWidget'); $wp_registered_widgets[sanitize_title('Page Menu Navigation')]['description'] = 'Creates a navigation menu.'; |
If you like this plugin and find it useful then let me know and I will add it to the Wordpress plugin centre. I'm sure there are some improvements that could be made to the plugin, if you think of any then leave a comment and let me know.
Update 01/11/2010
I have had another look at generating a dynamic menu using the Walker_Page class. The good thing about using the Walker_Page method is that it can be used to override the default page widget that comes with WordPress.
Comments
Submitted by Acts7 (not verified) on Sat, 06/05/2010 - 10:27 Permalink
Submitted by Dirtyone30 (not verified) on Wed, 10/20/2010 - 13:30 Permalink
Or am i missing something?
Submitted by lobster (not verified) on Wed, 01/05/2011 - 12:41 Permalink
Submitted by Billy Bowden (not verified) on Sat, 01/22/2011 - 19:21 Permalink
Add new comment