Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm trying to create a custom post type and would like that post type to have two content blocks. So that in the loop I can ask for content-1 and content-2 separately.

I can write the code just having the hard time figuring out how I should start, i.e., is there a way to add another "post content" block easily. Should I remove the existing content block and use two custom fields (can custom fields have the Kitchen Sink?)?

Thanks!

share|improve this question
add comment

1 Answer

up vote 6 down vote accepted

I would keep the primary content field and add a metabox + a secondary instance of the wp editor (using the handy wp_editor function).

Custom field values are stored in the database as LONGTEXT, so they can handle just about anything you wish to throw at them.

A class to wrap everything up. There's a few constants here we'll use later.

<?php
class Secondary_Content
{
    // meta key we'll use to save things.
    const META = '_secondary_content';

    // nonce name to check
    const NONCE = '_s_content_nonce';

    // post type to which we'll add the box
    const TYPE = 'page';

    private static $ins = null;

    public static function init()
    {
        add_action('plugins_loaded', array(self::instance(), '_setup'));
    }

    public static function instance()
    {
        is_null(self::$ins) && self::$ins = new self;
        return self::$ins;
    }

    public function _setup()
    {
        // we'll add actions here later.
    }
}

To add the meta box, hook into add_meta_boxes_{{YOUR_POST_TYPE}}. I'm just going to use pages for this example. Change the value of the TYPE constant in the class to make it work for a custom post type.

<?php
class Secondary_Content
{
    // snip snip

    public function _setup()
    {
        add_action('add_meta_boxes_' . self::TYPE, array($this, 'add_box'));
    }

    /**
     * Adds a meta box to the `page` post type.
     *
     * @uses    add_meta_box
     * @return  void
     */
    public function add_box()
    {
        add_meta_box(
            'secondary-content',
            __('Secondary Content', 'wspe'),
            array($this, 'box_cb'),
            self::TYPE,
            'normal',
            'high'
        );
    }

    /**
     * Metabox callback function.
     *
     * @access  public
     * @param   object $post The current $post
     * @uses    get_post_meta
     * @uses    wp_editor
     * @return  void
     */
    public function box_cb($post)
    {
        wp_nonce_field(self::NONCE . $post->ID, self::NONCE, false);

        wp_editor(
            get_post_meta($post->ID, self::META, true),
            self::META
        );
    }
}

The meta box callback is also included above. It just spits out a nonce for us to validate as well as the editor field using wp_editor.

Now we just need to hook into save_post and save things. We'll check to make sure we're on the right post type. Then validate the nonce and check if the current user has permission to edit the post, then it's just a matter of calling update_post_meta or delete_post_meta as appropriate. The only other thing of note here is that I check to see if the current user can post unfiltered HTML. If they can, I'll just let whatever go through in the meta box. If not, better run it through wp_filter_post_kses.

<?php
class Secondary_Content
{
    // snip snip

    public function _setup()
    {
        add_action('add_meta_boxes_' . self::TYPE, array($this, 'add_box'));
        add_action('save_post', array($this, 'save'), 10, 2);
    }

    // snip snip

    /**
     * Hooked into `save_post`.  Makes sure this is the request we want and the
     * user has permission, then saves the custom field.
     *
     * @access  public
     * @param   int $post_id
     * @param   object $post
     * @uses    wp_verify_nonce
     * @uses    current_user_can
     * @uses    update_post_meta
     * @uses    delete_post_meta
     * @return  void
     */
    public function save($post_id, $post)
    {
        if(
            self::TYPE != $post->post_type ||
            (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
        ) return;

        if(
            !isset($_POST[self::NONCE]) ||
            !wp_verify_nonce($_POST[self::NONCE], self::NONCE . $post_id)
        ) return;

        if(!current_user_can('edit_post', $post_id))
            return;

        if(!empty($_POST[self::META]))
        {
            update_post_meta(
                $post_id,
                self::META,
                current_user_can('unfiltered_html') ?
                    $_POST[self::META] : wp_filter_post_kses($_POST[self::META])
            );
        }
        else
        {
            delete_post_meta($post_id, self::META);
        }
    }
}

To fetch this on the front end, you just need to do echo get_post_meta($post->ID, '_secondary_content', true); somewhere in the loop. But it might be nicer to include a wrapper function in our class.

<?php
class Secondary_Content
{
    // snip snip

    /**
     * Meant to be used as a template tag. A simple helper to spit out our
     * secondary content.
     *
     * @access  public
     * @param   object $post
     * @param   bool $echo (optional) defaults to true.
     * @uses    get_post_meta
     * @return  string
     */
    public static function content($post, $echo=true)
    {
        $res = apply_filters('secondary_content',
            get_post_meta($post->ID, self::META, true));

        if($echo)
            echo $res;

        return $res;
    }
}

Now you can just do Secondary_Content::content($post); to fetch things.

A final note: this content won't get anything nice like wpautop or the like. If you want to do that, you'll need to add that as a filter to the final output.

<?php
add_filter('secondary_content', 'wpautop');

The end result:

Secondary Content

All of the above as a plugin.

share|improve this answer
    
+1 Loving your answers buddy! Keep it up. –  userabuser Nov 17 '12 at 6:06
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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