Custom post type archive content

Published Dec 14, 2017

One of the much discussed points of WordPress is the missing archive content of custom post types. Where custom taxonomy terms have their own textarea where you can edit the term content, there is for custom post types no such thing.

Fortunately we can create functionality by ourself to solve this problem with the help op the wp_options table. I will show you how to create a custom submenu under you custom post type menu tab with a title and content field to store archive page data.

First let’s add the submenu and hook into the load-page function to be able to create functionality to save the submitted form.

Note: I use “services” as custom post type in my examples. Change the name to your post type name to get the right results for your project.

/**
 * Register services page content submenu
 */
function cpt_services_submenu() {
 
   // Add the menu to the existing services tab
   $page = add_submenu_page( 'edit.php?post_type=services', __( 'Content', 'theme' ), __( 'Content', 'theme' ), 'edit_posts', 'ctp-services-content', 'ctp_services_content_page' );
 
   // For loading the page so the submitted data can be saved
   add_action( 'load-' . $page, 'ctp_services_content_page_load'  );
 
}
add_action( 'admin_menu', 'cpt_services_submenu' );

Now that we have added the submenu under our custom post type we need to create the page with the form and field itself.

/**
 * Services post type content menu page
 *
 * A page under admin services post type tab where a
 * custom title and content of the services post type can be added.
 *
 * @uses cpt_custom_content() to retrieve data from option
 */
function ctp_services_content_page() { ?>
 
    <div class="wrap">
 
        <h2><?php _e( 'Products page content', 'gtp_tranlate' ); ?></h2>
 
        <?php
        if ( isset( $_GET['updated'] ) && esc_attr( $_GET['updated'] ) == 'true' )
            echo '<div class="updated"><p>' . __( 'Content updated successfully', 'theme' ) . '</p></div>';
        ?>
 
        <form method="post" action="<?php admin_url( 'edit.php?post_type=services&page=services-page-content' ); ?>">
 
            <table class="form-table">
                <tr>
                    <th><label for="services_title"><?php _e( 'Page title', 'theme' ); ?>:</label></th>
                    <td><input type="text" name="data[title]" id="services_title" class="regular-text" value="<?php echo cpt_custom_content( 'services', 'title' ); ?>"></td>
                </tr>
 
                <tr>
                    <th><label for="services_content"><?php _e( 'Page content', 'theme' ); ?>:</label></th>
                    <td><?php wp_editor( cpt_custom_content( 'services', 'content' ), 'services_content', ['textarea_name' => 'data[content]'] ); ?></td>
                </tr>
            </table><!--End .form-table-->
 
            <?php submit_button( __( 'Save', 'theme' ), 'primary', 'submit', false ); ?>
 
        </form>
 
    </div><!--End .wrap-->
 
    <?php
}

Finally we need to make the save functionality that runs during the load-page function added earlier, so the custom post type data can be stored in our options table.

/**
 * Loads services page content menu page
 *
 * Stores the post type data submitted by the form.
 */
function ctp_services_content_page_load() {
 
   // If form submitted
   if ( isset( $_POST['submit'] ) )  {
 
      if ( ! empty( $_POST['data'] ) ) {
 
         $data = [];
 
         // Loop trough the submitted field
         foreach ( $_POST['data'] as $key => $value ) {
            if ( ! empty( $_POST['data'][$key] ) ) {
               if ( 'content' == $key ) {
                  // Stripslashes because of the wp editor
                  $data[$key] = stripslashes_deep( $_POST['data'][$key] );
               } else {
                  $data[$key] = $_POST['data'][$key];
               }
            }
         }
 
         // Update option
         if ( ! empty( $data ) )
         update_option( 'cpt_services_content', $data );
      }
 
      // Redirect back to services content page
      $redirect = admin_url( 'edit.php?post_type=services&page=ctp-services-content&updated=true' );
      wp_redirect( $redirect );
      exit;
   }
}

To retrieve the custom post type data I made a simple function that will help to keep you code clean each time you need to retrieve the data.

/**
 * Get custom post type custom content
 *
 * Returns custom post type data stored in option table
 * like custom title, content
 *
 * @param string $post_type the post type to retrieve data from
 * @param string $field which custom field to retrieve
 */
function cpt_custom_content( $post_type = 'services', $field = 'content' ) {
   if ( isset( $post_type ) ) {
      $data = get_option( 'cpt_' . $post_type . '_content' );
      if ( ! empty( $data[$field] ) ) {
         return $data[$field];
      }
   }
   return false;
}
Discover and read more posts from Robbert Vermeulen
get started