How to list child pages with excerpts, e.g. [child-pages depth=”1″ excerpt=”1″]

When Iam creating shortcodes with lots of HTML in it, Iam using two functions. One template function which holds all the shortcode code. And one simple function to create the shortcode from the first function.
I tend to use this setup cause the shortcode function needs to return the content.

At first we need to create an template function which hold out HTML code and also the shortcode attributes.

function child_pages_shortcode_template( $atts ) {

    // Attributes
    // these are the attributes you can insert in the shortcode
    // i.e. [show_child_pages sort_order="desc"]
    // see codex for get_pages for all attributs
    // these are also used as default attribute settings
    $atts = shortcode_atts(array(
        'sort_order'   => 'asc',
        'sort_column'  => 'post_title',
        'show_excerpt' => 1, // excerpt displays as default, set to any other number to disable
        ), $atts );
    extract( $atts );

    // get ID of current page
    $current_page_id = get_the_ID();

    // set the ID of the current page as child_of parameter
    // that means we are only showing children of the current page
    $args = array(
        'child_of'      => $current_page_id,
        'parent'        => $current_page_id, // add this to only show 1 level deep, remove to also show grand children
        'post_type'     => 'page',
        'sort_order'    => $sort_order, //this comes from the shortcode_atts above, so you can change it with the shortcode
        'sort_column'   => $sort_column, //this comes from the shortcode_atts above, so you can change it with the shortcode
    );

    $child_pages = get_pages( $args );

    // only start if we have some children
    if ($child_pages) {

        echo '<ul>';

        foreach ($child_pages as $child_page) {

            echo '<li>';

            $child_page_title = $child_page->post_title; // get children page title
            $child_page_excerpt = $child_page->post_excerpt; // get children page excerpt
            $child_page_id = $child_page->ID; // get children ID to create the permalink
            $child_page_permalink = get_permalink($child_page_id); // get permalink to children page

            echo '<h3><a href="'.$child_page_permalink.'">'.$child_page_title.'</a></h3>';

            // only show excerpt if $show_excerpt is 1
            if ($show_excerpt == 1) {
                echo '<p>'.$child_page_excerpt.'</p>';
            } 

            echo '</li>';

        }

        echo '</ul>';

    } 
}

After this we grab the 1st function and create a shortcode from it:

function child_pages_shortcode($atts, $content = null){
    ob_start();
        $content = child_pages_shortcode_template($atts);
        $content = ob_get_contents();
    ob_end_clean();

    return $content;

}
add_shortcode('show_child_pages', 'child_pages_shortcode');

Now you can use for example [show_child_pages show_excerpt="0"]

With ob_start(), ob_get_contents() and ob_end_clean() we can intercept the content of our 1st function. Only with this function our shortcode will be processed. The content of our 1st function will be buffered here.


Some explanations:

$atts = shortcode_atts(array(
        'sort_order'   => 'asc',
        'sort_column'  => 'post_title',
        'show_excerpt' => 1, // excerpt displays as default, set to any other number to disable
        ), $atts );
    extract( $atts );

These are all the attributes for the shortcode. You can see that we can mix attributes here. For example, sort_order and sort_column are attributes which we will insert into our get_pages() query as arguments. But show_excerpt is just a custom attribute. Later in the code we can check if and what was entered as the attribute.

if ($show_excerpt == 1) {
   echo '<p>'.$child_page_excerpt.'</p>';
}

Here we just check if the attribute $show_excerpt is set to 1. If any other number or string was entered, we dont display the excerpt at all.

$args = array(
        'child_of'      => $current_page_id,
        'parent'        => $current_page_id, // add this to only show 1 level deep, remove to also show grand children
        'post_type'     => 'page',
        'sort_order'    => $sort_order, //this comes from the shortcode_atts above, so you can change it with the shortcode
        'sort_column'   => $sort_column, //this comes from the shortcode_atts above, so you can change it with the shortcode
    );

These are the arguments for our get_pages function. With 'child_of' => $current_page_id, we are making sure that we only list children of the current page.
With 'parent' => $current_page_id, we can influence the depth. If we set it also to the current page, than no grand children will be listed. We need to use this as get_pages dont have an depth argument.