Advertisement
Guest User

Exclude Pages from Navigation

a guest
Oct 3rd, 2013
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 14.32 KB | None | 0 0
  1. <?php
  2. /*
  3. Plugin Name: Exclude Pages from Navigation
  4. Plugin URI: http://wordpress.org/extend/plugins/exclude-pages/
  5. Description: Provides a checkbox on the editing page which you can check to exclude pages from the primary navigation. IMPORTANT NOTE: This will remove the pages from any "consumer" side page listings, which may not be limited to your page navigation listings.
  6. Version: XXX
  7. Author: Simon Wheatley
  8. Author URI: http://simonwheatley.co.uk/wordpress/
  9.  
  10. Copyright 2007 Simon Wheatley
  11.  
  12. This script is free software; you can redistribute it and/or modify
  13. it under the terms of the GNU General Public License as published by
  14. the Free Software Foundation; either version 3 of the License, or
  15. (at your option) any later version.
  16.  
  17. This script is distributed in the hope that it will be useful,
  18. but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. GNU General Public License for more details.
  21.  
  22. You should have received a copy of the GNU General Public License
  23. along with this program.  If not, see <http://www.gnu.org/licenses/>.
  24.  
  25. */
  26.  
  27. // Full filesystem path to this dir
  28. define('EP_PLUGIN_DIR', dirname(__FILE__));
  29.  
  30. // Option name for exclusion data
  31. define('EP_OPTION_NAME', 'ep_exclude_pages');
  32. // Separator for the string of IDs stored in the option value
  33. define('EP_OPTION_SEP', ',');
  34. // The textdomain for the WP i18n gear
  35. define( 'EP_TD', 'exclude-pages' );
  36.  
  37. // Take the pages array, and return the pages array without the excluded pages
  38. // Doesn't do this when in the admin area
  39. function ep_exclude_pages( $pages ) {
  40.     // If the URL includes "wp-admin", just return the unaltered list
  41.     // This constant, WP_ADMIN, only came into WP on 2007-12-19 17:56:16 rev 6412, i.e. not something we can rely upon unfortunately.
  42.     // May as well check it though.
  43.     // Also check the URL... let's hope they haven't got a page called wp-admin (probably not)
  44.     // SWTODO: Actually, you can create a page with an address of wp-admin (which is then inaccessible), I consider this a bug in WordPress (which I may file a report for, and patch, another time).
  45.     $bail_out = ( ( defined( 'WP_ADMIN' ) && WP_ADMIN == true ) || ( strpos( $_SERVER[ 'PHP_SELF' ], 'wp-admin' ) !== false ) );
  46.     $bail_out = apply_filters( 'ep_admin_bail_out', $bail_out );
  47.     if ( $bail_out ) return $pages;
  48.     $excluded_ids = ep_get_excluded_ids();
  49.     $length = count($pages);
  50.     // Ensure we catch all descendant pages, so that if a parent
  51.     // is hidden, it's children are too.
  52.     for ( $i=0; $i<$length; $i++ ) {
  53.         $page = & $pages[$i];
  54.         // If one of the ancestor pages is excluded, add it to our exclude array
  55.         if ( $page !== NULL && ep_ancestor_excluded( $page, $excluded_ids, $pages ) ) {
  56.             // Can't actually delete the pages at the moment,
  57.             // it'll screw with our recursive search.
  58.             // For the moment, just tag the ID onto our excluded IDs
  59.             $excluded_ids[] = $page->ID;
  60.         }
  61.     }
  62.  
  63.     // Ensure the array only has unique values
  64.     $delete_ids = array_unique( $excluded_ids );
  65.    
  66.     // Loop though the $pages array and actually unset/delete stuff
  67.     for ( $i=0; $i<$length; $i++ ) {
  68.         $page = & $pages[$i];
  69.         // If one of the ancestor pages is excluded, add it to our exclude array
  70.         if ($page !== NULL && in_array( $page->ID, $delete_ids ) ) {
  71.             // Finally, delete something(s)
  72.             unset( $pages[$i] );
  73.         }
  74.     }
  75.  
  76.     // Reindex the array, for neatness
  77.     // SWFIXME: Is reindexing the array going to create a memory optimisation problem for large arrays of WP post/page objects?
  78.     if ( ! is_array( $pages ) ) $pages = (array) $pages;
  79.     $pages = array_values( $pages );
  80.  
  81.     return $pages;
  82. }
  83.  
  84. /**
  85.  * Recurse down an ancestor chain, checking if one is excluded
  86.  *
  87.  * @param  
  88.  * @return boolean|int The ID of the "nearest" excluded ancestor, otherwise false
  89.  * @author Simon Wheatley
  90.  **/
  91. function ep_ancestor_excluded( $page, $excluded_ids, $pages ) {
  92.     if($page === NULL) return false;
  93.    
  94.     $parent = & ep_get_page( $page->post_parent, $pages );
  95.     // Is there a parent?
  96.     if ( ! $parent )
  97.         return false;
  98.     // Is it excluded?
  99.     if ( in_array( $parent->ID, $excluded_ids ) )
  100.         return (int) $parent->ID;
  101.     // Is it the homepage?
  102.     if ( $parent->ID == 0 )
  103.         return false;
  104.     // Otherwise we have another ancestor to check
  105.     return ep_ancestor_excluded( $parent, $excluded_ids, $pages );
  106. }
  107.  
  108. /**
  109.  * {no description}
  110.  *
  111.  * @param int $page_id The ID of the WP page to search for
  112.  * @param array $pages An array of WP page objects
  113.  * @return boolean|object the page from the $pages array which corresponds to the $page_id
  114.  * @author Simon Wheatley
  115.  **/
  116. function ep_get_page( $page_id, $pages ) {
  117.     // PHP 5 would be much nicer here, we could use foreach by reference, ah well.
  118.     $length = count($pages);
  119.     for ( $i=0; $i<$length; $i++ ) {
  120.         $page = & $pages[$i];
  121.         if ( $page !== NULL && $page->ID == $page_id ) return $page;
  122.     }
  123.     // Unusual.
  124.     return false;
  125. }
  126.  
  127. // Is this page we're editing (defined by global $post_ID var)
  128. // currently NOT excluded (i.e. included),
  129. // returns true if NOT excluded (i.e. included)
  130. // returns false is it IS excluded.
  131. // (Tricky this upside down flag business.)
  132. function ep_this_page_included() {
  133.     global $post_ID;
  134.     // New post? Must be included then.
  135.     if ( ! $post_ID ) return true;
  136.     $excluded_ids = ep_get_excluded_ids();
  137.     // If there's no exclusion array, we can return true
  138.     if ( empty($excluded_ids) ) return true;
  139.     // Check if our page is in the exclusion array
  140.     // The bang (!) reverses the polarity [1] of the boolean
  141.     return ! in_array( $post_ID, $excluded_ids );
  142.     // fn1. (of the neutron flow, ahem)
  143. }
  144.  
  145. // Check the ancestors for the page we're editing (defined by
  146. // global $post_ID var), return the ID if the nearest one which
  147. // is excluded (if any);
  148. function ep_nearest_excluded_ancestor() {
  149.     global $post_ID, $wpdb;
  150.     // New post? No problem.
  151.     if ( ! $post_ID ) return false;
  152.     $excluded_ids = ep_get_excluded_ids();
  153.     // Manually get all the pages, to avoid our own filter.
  154.     $sql = "SELECT ID, post_parent FROM $wpdb->posts WHERE post_type = 'page'";
  155.     $pages = $wpdb->get_results( $sql );
  156.     // Start recursively checking the ancestors
  157.     $parent = ep_get_page( $post_ID, $pages );
  158.     return ep_ancestor_excluded( $parent, $excluded_ids, $pages );
  159. }
  160.  
  161. function ep_get_excluded_ids() {
  162.     $exclude_ids_str = get_option( EP_OPTION_NAME );
  163.     // No excluded IDs? Return an empty array
  164.     if ( empty($exclude_ids_str) ) return array();
  165.     // Otherwise, explode the separated string into an array, and return that
  166.     return explode( EP_OPTION_SEP, $exclude_ids_str );
  167. }
  168.  
  169. // This function gets all the exclusions out of the options
  170. // table, updates them, and resaves them in the options table.
  171. // We're avoiding making this a postmeta (custom field) because we
  172. // don't want to have to retrieve meta for every page in order to
  173. // determine if it's to be excluded. Storing all the exclusions in
  174. // one row seems more sensible.
  175. function ep_update_exclusions( $post_ID ) {
  176.     // Bang (!) to reverse the polarity of the boolean, turning include into exclude
  177.     $exclude_this_page = ! (bool) @ $_POST['ep_this_page_included'];
  178.     // SWTODO: Also check for a hidden var, which confirms that this checkbox was present
  179.     // If hidden var not present, then default to including the page in the nav (i.e. bomb out here rather
  180.     // than add the page ID to the list of IDs to exclude)
  181.     $ctrl_present = (bool) @ $_POST['ep_ctrl_present'];
  182.     if ( ! $ctrl_present )
  183.         return;
  184.    
  185.     $excluded_ids = ep_get_excluded_ids();
  186.     // If we need to EXCLUDE the page from the navigation...
  187.     if ( $exclude_this_page ) {
  188.         // Add the post ID to the array of excluded IDs
  189.         array_push( $excluded_ids, $post_ID );
  190.         // De-dupe the array, in case it was there already
  191.         $excluded_ids = array_unique( $excluded_ids );
  192.     }
  193.     // If we need to INCLUDE the page in the navigation...
  194.     if ( ! $exclude_this_page ) {
  195.         // Find the post ID in the array of excluded IDs
  196.         $index = array_search( $post_ID, $excluded_ids );
  197.         // Delete any index found
  198.         if ( $index !== false ) unset( $excluded_ids[$index] );
  199.     }
  200.     $excluded_ids_str = implode( EP_OPTION_SEP, $excluded_ids );
  201.     ep_set_option( EP_OPTION_NAME, $excluded_ids_str );
  202. }
  203.  
  204. // Take an option, delete it if it exists, then add it.
  205. function ep_set_option( $name, $value ) {
  206.     // Delete option   
  207.     delete_option($name);
  208.     // Insert option
  209.     add_option( $name, $value );
  210. }
  211.  
  212. /**
  213.  * Callback function for the metabox on the page edit screen.
  214.  *
  215.  * @return void
  216.  * @author Simon Wheatley
  217.  **/
  218. function ep_admin_sidebar_wp25() {
  219.     $nearest_excluded_ancestor = ep_nearest_excluded_ancestor();
  220.     echo '  <div id="excludepagediv" class="new-admin-wp25">';
  221.     echo '      <div class="outer"><div class="inner">';
  222.     echo '      <p><label for="ep_this_page_included" class="selectit">';
  223.     echo '      <input ';
  224.     echo '          type="checkbox" ';
  225.     echo '          name="ep_this_page_included" ';
  226.     echo '          id="ep_this_page_included" ';
  227.     if ( ep_this_page_included() )
  228.         echo 'checked="checked"';
  229.     echo ' />';
  230.     echo '          '.__( 'Include this page in lists of pages', EP_TD ).'</label>';
  231.     echo '      <input type="hidden" name="ep_ctrl_present" value="1" /></p>';
  232.     if ( $nearest_excluded_ancestor !== false ) {
  233.         echo '<p class="ep_exclude_alert"><em>';
  234.         printf( __( 'N.B. An ancestor of this page is excluded, so this page is too (<a href="%1$s" title="%2$s">edit ancestor</a>).', EP_TD), "post.php?action=edit&amp;post=$nearest_excluded_ancestor", __( 'edit the excluded ancestor', EP_TD ) );
  235.         echo '</em></p>';
  236.     }
  237.     // If there are custom menus (WP 3.0+) then we need to clear up some
  238.     // potential confusion here.
  239.     if ( ep_has_menu() ) {
  240.         echo '<p id="ep_custom_menu_alert"><em>';
  241.         if ( current_user_can( 'edit_theme_options' ) )
  242.             printf( __( 'N.B. This page can still appear in explicitly created <a href="%1$s">menus</a> (<a id="ep_toggle_more" href="#ep_explain_more">explain more</a>)', EP_TD),
  243.                 "nav-menus.php" );
  244.         else
  245.             _e( 'N.B. This page can still appear in explicitly created menus (<a id="ep_toggle_more" href="#ep_explain_more">explain more</a>)', EP_TD);
  246.         echo '</em></p>';
  247.         echo '<div id="ep_explain_more"><p>';
  248.         if ( current_user_can( 'edit_theme_options' ) )
  249.             printf( __( 'WordPress provides a simple function for you to maintain your site <a href="%1$s">menus</a>. If you create a menu which includes this page, the checkbox above will not have any effect on the visibility of that menu item.', EP_TD),
  250.                 "nav-menus.php" );
  251.         else
  252.             _e( 'WordPress provides a simple function for you to maintain the site menus, which your site administrator is using. If a menu includes this page, the checkbox above will not have any effect on the visibility of that menu item.', EP_TD);
  253.         echo '</p><p>';
  254.         echo _e( 'If you think you no longer need the Exclude Pages plugin you should talk to your WordPress administrator about disabling it.', EP_TD );
  255.         echo '</p></div>';
  256.     }
  257.     echo '      </div><!-- .inner --></div><!-- .outer -->';
  258.     echo '  </div><!-- #excludepagediv -->';
  259. }
  260.  
  261. /**
  262.  * A conditional function to determine whether there are any menus
  263.  * defined in this WordPress installation.
  264.  *
  265.  * @return bool Indicates the presence or absence of menus
  266.  * @author Simon Wheatley
  267.  **/
  268. function ep_has_menu() {
  269.     if ( ! function_exists( 'wp_get_nav_menus' ) )
  270.         return false;
  271.     $menus = wp_get_nav_menus();
  272.     foreach ( $menus as $menu_maybe ) {
  273.         if ( $menu_items = wp_get_nav_menu_items($menu_maybe->term_id) )
  274.             return true;
  275.     }
  276. }
  277.  
  278. /**
  279.  * Hooks the WordPress admin_head action to inject some CSS.
  280.  *
  281.  * @return void
  282.  * @author Simon Wheatley
  283.  **/
  284. function ep_admin_css() {
  285.     echo <<<END
  286. <style type="text/css" media="screen">
  287.     .ep_exclude_alert { font-size: 11px; }
  288.     .new-admin-wp25 { font-size: 11px; background-color: #fff; }
  289.     .new-admin-wp25 .inner {  padding: 8px 12px; background-color: #EAF3FA; border: 1px solid #EAF3FA; -moz-border-radius: 3px; -khtml-border-bottom-radius: 3px; -webkit-border-bottom-radius: 3px; border-bottom-radius: 3px; }
  290.     #ep_admin_meta_box .inner {  padding: inherit; background-color: transparent; border: none; }
  291.     #ep_admin_meta_box .inner label { background-color: none; }
  292.     .new-admin-wp25 .exclude_alert { padding-top: 5px; }
  293.     .new-admin-wp25 .exclude_alert em { font-style: normal; }
  294. </style>
  295. END;
  296. }
  297.  
  298. /**
  299.  * Hooks the WordPress admin_head action to inject some JS.
  300.  *
  301.  * @return void
  302.  * @author Simon Wheatley
  303.  **/
  304. function ep_admin_js() {
  305.     echo <<<END
  306. <script type="text/javascript">
  307. //<![CDATA[
  308.     jQuery( '#ep_explain_more' ).hide();
  309.     jQuery( '#ep_toggle_more' ).click( function() {
  310.         jQuery( '#ep_explain_more' ).toggle();
  311.         return false;
  312.     } );
  313. //]]>
  314. </script>
  315. END;
  316. }
  317.  
  318. // Add our ctrl to the list of controls which AREN'T hidden
  319. function ep_hec_show_dbx( $to_show ) {
  320.     array_push( $to_show, 'excludepagediv' );
  321.     return $to_show;
  322. }
  323.  
  324. // PAUSE & RESUME FUNCTIONS
  325.  
  326. function pause_exclude_pages() {
  327.     remove_filter('get_pages','ep_exclude_pages');
  328. }
  329.  
  330. function resume_exclude_pages() {
  331.     add_filter('get_pages','ep_exclude_pages');
  332. }
  333.  
  334. // INIT FUNCTIONS
  335.  
  336. function ep_init() {
  337.     // Call this function on the get_pages filter
  338.     // (get_pages filter appears to only be called on the "consumer" side of WP,
  339.     // the admin side must use another function to get the pages. So we're safe to
  340.     // remove these pages every time.)
  341.     add_filter('get_pages','ep_exclude_pages');
  342.     // Load up the translation gear
  343.     $locale = get_locale();
  344.     $folder = rtrim( basename( dirname( __FILE__ ) ), '/' );
  345.     $mo_file = trailingslashit( WP_PLUGIN_DIR ) . "$folder/locale/" . EP_TD . "-$locale.mo";
  346.     load_textdomain( EP_TD, $mo_file );
  347. }
  348.  
  349. function ep_admin_init() {
  350.     // Add panels into the editing sidebar(s)
  351.     global $wp_version;
  352.     add_meta_box('ep_admin_meta_box', __( 'Exclude Pages', EP_TD ), 'ep_admin_sidebar_wp25', 'page', 'side', 'low');
  353.  
  354.     // Set the exclusion when the post is saved
  355.     add_action('save_post', 'ep_update_exclusions');
  356.  
  357.     // Add the JS & CSS to the admin header
  358.     add_action('admin_head', 'ep_admin_css');
  359.     add_action('admin_footer', 'ep_admin_js');
  360.  
  361.     // Call this function on our very own hec_show_dbx filter
  362.     // This filter is harmless to add, even if we don't have the
  363.     // Hide Editor Clutter plugin installed as it's using a custom filter
  364.     // which won't be called except by the HEC plugin.
  365.     // Uncomment to show the control by default
  366.     // add_filter('hec_show_dbx','ep_hec_show_dbx');
  367. }
  368.  
  369. // HOOK IT UP TO WORDPRESS
  370.  
  371. add_action( 'init', 'ep_init' );
  372. add_action( 'admin_init', 'ep_admin_init' )
  373.  
  374. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement