tags:

views:

99

answers:

1

The following code will add the categories selector widget to the WordPress Page editor interface...

add_action('admin_menu', 'my_post_categories_meta_box');
function my_post_categories_meta_box() {
    add_meta_box('categorydiv', __('Categories'), 'post_categories_meta_box', 'page', 'side', 'core');
}

What I would like to do is to figure out how to modify the resulting category listing so that it only contains a predefined list of hard coded categories that I define.

Since I'm adding this via my custom theme, it will only appear on the page editor when my theme is active on the site. And I have some specific "handler" categories that my theme installs into the site and later uses to determine layout elements. I only want these specific categories to be listed in this particular instance of the categories widget.

A: 

Use a modified version of post_categories_meta_box, in which you change the call to wp_category_checklist to a modified version, wp_category_checklist_modified.

post_categories_meta_box_modified:

function post_categories_meta_box_modified($post) {
?>
<ul id="category-tabs">
    <li class="ui-tabs-selected"><a href="#categories-all" tabindex="3"><?php _e( 'All Categories' ); ?></a></li>
    <li class="hide-if-no-js"><a href="#categories-pop" tabindex="3"><?php _e( 'Most Used' ); ?></a></li>
</ul>

<div id="categories-pop" class="ui-tabs-panel" style="display: none;">
    <ul id="categorychecklist-pop" class="categorychecklist form-no-clear" >
        <?php $popular_ids = wp_popular_terms_checklist('category'); ?>
    </ul>
</div>

<div id="categories-all" class="ui-tabs-panel">
    <ul id="categorychecklist" class="list:category categorychecklist form-no-clear">
        <?php wp_category_checklist($post->ID, false, array($cat1_id, $cat2_id.... ,$catn_id), $popular_ids) ?>
    </ul>
</div>

<?php if ( current_user_can('manage_categories') ) : ?>
<div id="category-adder" class="wp-hidden-children">
    <h4><a id="category-add-toggle" href="#category-add" class="hide-if-no-js" tabindex="3"><?php _e( '+ Add New Category' ); ?></a></h4>
    <p id="category-add" class="wp-hidden-child">
        <label class="hidden" for="newcat"><?php _e( 'Add New Category' ); ?></label><input type="text" name="newcat" id="newcat" class="form-required form-input-tip" value="<?php _e( 'New category name' ); ?>" tabindex="3" aria-required="true"/>
        <label class="hidden" for="newcat_parent"><?php _e('Parent category'); ?>:</label><?php wp_dropdown_categories( array( 'hide_empty' => 0, 'name' => 'newcat_parent', 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => __('Parent category'), 'tab_index' => 3 ) ); ?>
        <input type="button" id="category-add-sumbit" class="add:categorychecklist:category-add button" value="<?php _e( 'Add' ); ?>" tabindex="3" />
        <?php wp_nonce_field( 'add-category', '_ajax_nonce', false ); ?>
        <span id="category-ajax-response"></span>
    </p>
</div>
<?php
endif;

}

I've only change the line of the original function

<?php wp_category_checklist($post->ID, false, false, $popular_ids) ?>

to

<?php wp_category_checklist_modified($post->ID, false, false, $popular_ids) ?>

wp_category_checklist_modified:

function wp_category_checklist_modified( $post_id = 0, $descendants_and_self = 0, $selected_cats = false, $popular_cats = false, $include_cats = array() ) {
    $walker = new Walker_Category_Checklist;
    $descendants_and_self = (int) $descendants_and_self;
    $cat_ids_list = implode(',', $include_cats);

    $args = array();

    if ( is_array( $selected_cats ) )
        $args['selected_cats'] = $selected_cats;
    elseif ( $post_id )
        $args['selected_cats'] = wp_get_post_categories($post_id);
    else
        $args['selected_cats'] = array();

    if ( is_array( $popular_cats ) )
        $args['popular_cats'] = $popular_cats;
    else
        $args['popular_cats'] = get_terms( 'category', array( 'fields' => 'ids', 'orderby' => 'count', 'order' => 'DESC', 'number' => 10, 'hierarchical' => false ) );

    if ( $descendants_and_self ) {
        $categories = get_categories( "child_of=$descendants_and_self&hierarchical=0&hide_empty=0&include=$cat_ids_list" );
        $self = get_category( $descendants_and_self );
        array_unshift( $categories, $self );
    } else {
        $categories = get_categories('get=all&include='. $cat_ids_list);
    }

    // Post process $categories rather than adding an exclude to the get_terms() query to keep the query the same across all posts (for any query cache)
    $checked_categories = array();
    for ( $i = 0; isset($categories[$i]); $i++ ) {
        if ( in_array($categories[$i]->term_id, $args['selected_cats']) ) {
            $checked_categories[] = $categories[$i];
            unset($categories[$i]);
        }
    }

    // Put checked cats on top
    echo call_user_func_array(array(&$walker, 'walk'), array($checked_categories, 0, $args));
    // Then the rest of them
    echo call_user_func_array(array(&$walker, 'walk'), array($categories, 0, $args));
}

Here I've added an extra argument to wp_category_checklist_modified, $include_cats, in which you can specify the categories ids, then I use this list in the two calls to get_categories passing it as the include parameter.

These functions are not documented (as much as I am be able to find), so I've had to take a look to the source code.

Then you will simply use

function my_post_categories_meta_box() {
   add_meta_box('categorydiv', __('Categories'), 'post_categories_meta_box_modified', 'page', 'side', 'core');
}

Hope this helps.

Nicolò Martini
Sounds sweet Nicolo. Off to try it now :)
Scott B
Nicolo, I can only get the full categories listing. Not sure what I'm doing wrong. I just want two categories in that list. Here's my function call... <?php wp_category_checklist($post->ID, false, array($nofollow, $noindex), $popular_ids) ?>
Scott B
@nicolo - is there a way that I can specify the categories by slug rather than ID. My problem with Cat_id is that this is a custom theme and I will not know the cat_ids from install to install. I do know that the category slug will always be "noindex" and "nofollow"
Scott B
For the last question, you can use get_term_by function. This should work: <?php $cat = get_term_by( 'slug', 'category_slug', 'category' ); $cat_id = $cat->term_id; ?>. For the first two, I've found that the answer is wrong, the array argument specifies only the selected cats... I will correct the answer, sorry
Nicolò Martini
I've corrected the answer... hope this works
Nicolò Martini
Nicolo: Thanks very much for your help with this. I was sure hoping it would be much simpler than this :) My original function was 3 lines of code, LOL. Now to simply reduce the list to my specified categories, I'm having to add 82 lines of code??? I'm not being ungrateful, I'm just saying...
Scott B
Yeah, is not a so elegant solution, but the fact is that post_categories_meta_box is not made to be customizable... another solution is to filter the html returned by the original function to remain only with the wanted cats, but you always have to deal with a function that do not do wat you want...
Nicolò Martini