2

I am trying to add a field to Wordpress posts (not pages) which would simply include a label and a checkbox. Something like: Make this the sticky post? If check, the key should be set to TRUE or something that is easily accessible. The problem is, however, that I only want one post to have possibly the TRUE value. This means, when I have checked the field in post-1, and afterwards I check that field in post-2, post-1's field value should be set to FALSE so that only one post can have TRUE at the same time.

I would then loop the post in my index.php to get the post that has value==TRUE and display it somewhere on the page. Something like this (not tested):

    $args = array(
        'order'             => 'DESC',
        'posts_per_page'    => 1,
        'meta_key'          => 'post_sticky',
        ),
    );
    $stickyPost = new WP_Query( $args );

Ideally I want this field to be set in my functions.php file, but from what I've gather on the Internet this isn't as easy as it should be. You can add fields in Wordpress itself, but I don't want to do it like that because I am dependent on writers and I don't want them fiddling around.

EDIT: apparently when no post is checked as the highlight, 4 posts are displayed (which is as much as I allow on the index page). Would I need to reset the post data of the main loop before calling this loop? Even so, I would expect that the if has posts would exclude any other posts than the stickied one. This is the code in my sidebar.php file:

<?php $args = array(
    'p' => get_option('my_sticky_post')
    );
    $stickyPost = new WP_Query($args);
?>
<?php if($stickyPost->have_posts()) : while($stickyPost->have_posts()) : $stickyPost->the_post(); ?>
    <aside class="highlight">
        <h3>Must-read</h3>
        <div class="sidebar-content">
            <div class="thumbnail">
            <?php $image_id = get_post_thumbnail_id();
            $image_url = wp_get_attachment_image_src($image_id,'home-featured'); ?>
                <a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>" style="background-image: url('<?php echo $image_url[0]; ?>');"><span><?php the_title(); ?></span></a>
            </div>
            <?php the_excerpt(); ?>
        </div>
    </aside>
    <?php endwhile; ?>
<?php endif; ?>
<?php wp_reset_postdata(); ?>

EDIT 2: we can check for the existence of the option 'my_sticky_post'. If it doesn't exist, WP will return FALSE. Therefore, the following works:

<?php 

$stickyId = get_option('my_sticky_post');
if ($stickyId) :
    $args = array(
        'p' => $stickyId
    );
    $stickyPost = new WP_Query($args);

    if($stickyPost->have_posts()) : while($stickyPost->have_posts()) : $stickyPost->the_post(); ?>
        <aside class="highlight">
            <h3>Must-read</h3>
            <div class="sidebar-content">
                <div class="thumbnail">
                <?php $image_id = get_post_thumbnail_id();
                $image_url = wp_get_attachment_image_src($image_id,'home-featured'); ?>
                    <a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>" style="background-image: url('<?php echo $image_url[0]; ?>');"><span><?php the_title(); ?></span></a>
                </div>
                <?php the_excerpt(); ?>
            </div>
        </aside>
    <?php endwhile; // have posts ?>
    <?php endif; // have posts ?>
    <?php wp_reset_postdata(); ?>
<?php endif; // $stickyId ?>
4

Hopefully I've understood your question correctly, and I hope the code below helps.

First you must add a metabox to hold the option that you wish to add to each Post, and the my_add_sticky_metabox() function will do this, in conjunction with the add_meta_boxes action hook.

At this stage you are not actually printing anything, but rather telling WordPress to print using your declared callback (my_output_sticky_metabox()) whenever the Post edit screen is displayed.

Using the my_output_sticky_metabox() function the metabox content is printed, including a check as to whether or not the box should be checked and/or disabled.

Finally the correct value is saved using the my_save_sticky_metabox() function in conjunction with the save_post action hook.

add_action('add_meta_boxes', 'my_add_sticky_metabox');
function my_add_sticky_metabox(){

    add_meta_box(
        'my_sticky_post_metabox',
        __('Sticky Post', 'my_text_domain'),
        'my_output_sticky_metabox',
        'post'
    );

}

function my_output_sticky_metabox($post){

    /** Grab the current 'my_sticky_post' option value */
    $sp = intval(get_option('my_sticky_post'));

    /** Check to see if the 'my_sticky_post' option should be disabled or checked for the current Post */
    $checked = checked($sp, $post->ID, false);
    if($sp > 0) :
        $disabled = (!disabled($sp, $post->ID, false)) ? 'disabled="true"' : '';
    else :
        $disabled = '';
    endif;

    /** Add a nonce field */
    wp_nonce_field('my_sticky_post_metabox', 'my_sticky_post_metabox_nonce');

    /** Add a hidden field to check against in case it is unchecked before save */
    $value = ($checked) ? '1' : '0';
    echo '<input type="hidden" name="was_checked" value="' . $value . '" />';

    /** Output the checkbox and label */
    echo '<label for="my_sticky_post">';
    echo '<input type="checkbox" id="my_sticky_post" name="my_sticky_post" value="' . $post->ID . '" ' . $checked . $disabled . ' />';
    echo 'Make this the sticky post?</label>';

    /** Let the user know which Post is currently sticky */
    switch($sp) :

        case 0:
            $message = 'There is currently no Sticky Post.';
            break;
        case $post->ID:
            $message = 'This Post is the Sticky Post.';
            break;
        default:
            $message = '<a href="' . get_edit_post_link($sp) . '" title="' . the_title_attribute('before=Edit post \'&after=\'&echo=0') . '">' . get_the_title($sp) . '</a> is the current Sticky Post';
            $message.= '<br />You must remove the sticky status from that post before you can make this one sticky.';

    endswitch;
    echo '<p><em>' . $message .'</em></p>';

}

add_action('save_post', 'my_save_sticky_metabox');
function my_save_sticky_metabox($post_id){

    /*
     * We need to verify this came from our screen and with proper authorization,
     * because the save_post action can be triggered at other times.
     */

    /** Ensure that a nonce is set */
    if(!isset($_POST['my_sticky_post_metabox_nonce'])) :
        return;
    endif;

    /** Ensure that the nonce is valid */
    if(!wp_verify_nonce( $_POST['my_sticky_post_metabox_nonce'], 'my_sticky_post_metabox')) :
        return;
    endif;

    /** Ensure that an AUTOSAVE is not taking place */
    if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) :
        return;
    endif;

    /** Ensure that the user has permission to update this option */
    if(!current_user_can('edit_post', $post_id)) :
        return;
    endif;

    /**
     * Everything is valid, now the option can be updated
     */

    /** Check to see if the 'my_sticky_post' option was checked */
    if(isset($_POST['my_sticky_post'])) : // It was...

        update_option('my_sticky_post', $_POST['my_sticky_post']);  // Update the option

    else : // It was not...

        /** Check to see if the option was checked prior to the options being updated */
        if($_POST['was_checked'] != 0) : // It was...

            update_option('my_sticky_post', -1);    // Set the option to '-1'

        endif;

    endif;

}

Now when you want to grab your custom sticky post you use this query -

$args = array(
    'p' => get_option('my_sticky_post')
);
$stickyPost = new WP_Query($args);

if($stickyPost->have_posts()) : while($stickyPost->have_posts()) : $stickyPost->the_post();

        { Your code goes here }

    endwhile;
endif;
wp_reset_postdata();

There is plenty of reading material I'd encourage you to take a look at here, hopefully it'll answer any questions you may have about the code above -

Edit

You mention that if no sticky post is set, you have 4 posts output on index.php (which is your standard number).

I believe that is because if the p parameter is empty it is ignored, thus you need to check the value of the my_sticky_post option.

Make this change to the $args array (and note the additional line above) -

$sticky_id = get_option('my_sticky_post');
$args = array(
    'p' => ($sticky_id) ? $sticky_id : '-1'
);
  • Wow David! This is great!Am I right in presuming that the best way to use this in a template, is to use an if-clause after setting the new WP_Query like so?: if ($stickyPost->have_posts() ) : $stickyPost->the_post();, then do all my HTML outputting, and then conclude with closing the if-clause and resetting the query? wp_reset_postdata();? – Bram Vanroy Feb 11 '15 at 15:15
  • Yes, that's correct, although you should use while($stickyPost->have_posts()) as well. I've updated my answer to clarify. – David Gard Feb 11 '15 at 15:18
  • As I'm relatively new to PHP I must ask, why is while necessary as we know there can only possibly be one sticky post? – Bram Vanroy Feb 11 '15 at 15:30
  • And a heads-up: I got a PHP exception error because the second line of your query 'p' => get_option('my_sticky_post'); contains a trailing semicolon. – Bram Vanroy Feb 11 '15 at 15:31
  • Ah, good spot, I'll remove that semicolon - added in error. For the use of While - techniclly yes just using if would work in this instance (in fact it would work with either one or the other, or both), but I'd personally say it's a good habit to get in to if you are going to be using The Loop going forward. – David Gard Feb 11 '15 at 15:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.