gschoppe
5/14/2015 - 2:00 PM

Wordpress Board of Directors Custom Post Type, with custom profile pictures and meta information (http://www.gschoppe.com)

Wordpress Board of Directors Custom Post Type, with custom profile pictures and meta information (http://www.gschoppe.com)

<?php
// CPT Board
// by Greg Schoppe (http://gschoppe.com)
// Licensed under GPL
// creates board members post type, to render custom board of directors archive page
// usage: in functions.php, add require_once('cpt-board.php');
//        then add both files to theme root directory

// add custom image size for board member images
add_action( 'after_setup_theme', function() {
    add_image_size( 'board-thumb', 350, 350, true );
});

// register custom 'board' post type
add_action('init', function() {
    register_post_type('board',
        array(
          'labels' => array(
            'name'          => __( 'Board Members' ),
            'singular_name' => __( 'Board Member' ),
            'add_new_item'  => __('Add New Board Member'),
            'edit_item'     => __('Edit Member'),
            'new_item'      => __('New Member'),
            'view_item'     => __('View Member')
          ),
          'public' => true,
          'has_archive' => true,
          'show_in_nav_menus' => true
        )
    );
});
// redirect single posts to the archive page, scrolled to current ID 
add_action( 'template_redirect', function() {
    if ( is_singular('board') ) {
        global $post;
        $redirectLink = get_post_type_archive_link( 'board' )."#".$post->ID;
        wp_redirect( $redirectLink, 302 );
        exit;
    }
});

// turn off pagination for the archive page
add_action('parse_query', function($query) {
    if (is_post_type_archive('board')) {
        $query->set('nopaging', 1);
    }
});

// change the post title to request the name of the board member
add_filter( 'enter_title_here', function( $title ) {
     $screen = get_current_screen();
     if  ( 'board' == $screen->post_type ) {
          $title = 'Enter Name Here';
     }
     return $title;
});

// add custom metabox for profile picture, name, title
add_action('add_meta_boxes', function() {
    // Define the custom attachment for posts
    add_meta_box(
        'board_member_meta',
        'Member Information',
        'board_member_meta_box',
        'board',
        'normal',
        'high'
    );
});

// render metabox
function board_member_meta_box($post) {
    // create security nonce
    wp_nonce_field('wp_board_member_nonce', 'wp_board_member_nonce');
    // get any existing values (if we're editing a board member)
    $company = get_post_meta( $post->ID, '_board_member_company' , true );
    $title   = get_post_meta( $post->ID, '_board_member_title' , true );
    $pic     = get_post_meta( $post->ID, '_board_member_pic' , true );
    $picsrc  = wp_get_attachment_image_src( $pic, "thumbnail" );
    ?>
    <table>
    <tr>
        <td><label for="board_member_company">Company: </label></td>
        <td><input id="board_member_company" name="board_member_company" type="text" value="<?=$company?>"/></td>
    </tr><tr>
        <td><label for="board_member_title">Position: </label></td>
        <td><input id="board_member_title" name="board_member_title" type="text"  value="<?=$title?>"/></td>
    </tr><tr>
        <td><label for="board_member_pic">Picture: </label></td>
        <td>
            <input id="board_member_pic" name="board_member_pic" type="hidden" value="<?=$pic?>"/>
            <img src="<?=$picsrc[0]?>" id="board_member_pic-vis" width="150" height="150" alt="preview" style="display: block; float: none;"/>
            <input id="board_member_pic_button" class="button" name="board_member_pic_button" type="button" value="Choose Profile Image"/>
        </td>
    </tr>
    </table>
    <?php 
    // this is how we attach the media manager to the metabox
    ?>
    <script type="text/javascript">
        jQuery(document).ready(function($){
            var _custom_media = true,
            _orig_send_attachment = wp.media.editor.send.attachment;
            $('#board_member_pic_button').click(function(e) {
                var send_attachment_bkp = wp.media.editor.send.attachment;
                var button = $(this);
                // this is just my shorthand for the id for hidden field associated with the button. could be hard coded
                var id = button.attr('id').replace('_button', '');
                _custom_media = true;
                wp.media.editor.send.attachment = function(props, attachment){
                    if ( _custom_media ) {
                        $("#"+id).val(attachment.id);
                        if(typeof attachment.sizes.thumbnail != "undefined" && typeof attachment.sizes.thumbnail.url != "undefined") {
                            var src = attachment.sizes.thumbnail.url;
                        } else {
                            var src = attachment.url;
                            // warning, in case the image is too small to generate a thumbnail
                            alert('WARNING: This image is below the recommended dimensions of 500x500 pixels.');
                        }
                        // this one is my shorthand for the preview element id.  could be hard coded instead
                        $("#"+id+"-vis").attr('src', src);
                    } else {
                        return _orig_send_attachment.apply( this, [props, attachment] );
                    };
                }
                wp.media.editor.open(button);
                return false;
            });
            $('.add_media').on('click', function(){
                _custom_media = false;
            });
        });
    </script>
    <?php
}

// save values entered into metabox
add_action( 'save_post', function($post_id) {
    // verify nonce
    if(!isset($_POST['wp_board_member_nonce']) || !wp_verify_nonce($_POST['wp_board_member_nonce'], 'wp_board_member_nonce')) {
        return;
    }
    // ignore autosaves (this is up to you.  it might be good for the metabox to autosave with drafts, but it adds complexity)
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    // check post type
    if(!(isset($_POST['post_type']) && 'board' == $_POST['post_type'])) {
        return;
    }
    // check user priviledges
    if (!current_user_can('edit_page', $post_id)) {
        return;
    }
    // get field values
    $company = (isset($_POST['board_member_company']))?sanitize_text_field($_POST['board_member_company']):"";
    $title = (isset($_POST['board_member_title']))?sanitize_text_field($_POST['board_member_title']):"";
    $pic = (isset($_POST['board_member_pic']))?sanitize_text_field($_POST['board_member_pic']):"";
    // update the meta values
    update_post_meta( $post_id, '_board_member_company', $company );
    update_post_meta( $post_id, '_board_member_title', $title );
    update_post_meta( $post_id, '_board_member_pic', $pic );
});

// move the editor below the custom meta box (this is a nasty hack, but seems to work ok)
add_action('add_meta_boxes', function() {
    global $_wp_post_type_features;
    //move editor
    if (isset($_wp_post_type_features['board']['editor']) && $_wp_post_type_features['board']['editor']) {
        // remove the editor from it's normal position
        unset($_wp_post_type_features['board']['editor']);
        // create a clone of it in a custom metabox
        add_meta_box(
            'bio_section',
            __('Biography'),
            function($post) {
                echo '<div class="wp-editor-wrap">';
                wp_editor($post->post_content, 'content', array('dfw' => true, 'tabindex' => 1) );
                echo '</div>';
            },
            'board',
            'normal',
            'high'
        );
    }
    // add styles to make the editor look normal
    add_action( 'admin_head', function(){
        ?>
            <style type="text/css">
                .wp-editor-container{background-color:#fff;}
            </style>
        <?php
    });
});

// Add CPT Archives to menu list (works for all CPTs, so should only happen once, site-wide)
if(!defined("ADD-CPT-ARCHIVES-TO-WP-MENUS")) {
  define("ADD-CPT-ARCHIVES-TO-WP-MENUS", true);
  add_action( 'admin_head-nav-menus.php', function() {
      add_meta_box( 'add-cpt', __( 'CPT Archives' ), function() {
          // get custom post types with archive support
          $post_types = get_post_types( array( 'show_in_nav_menus' => true, 'has_archive' => true ), 'object' );
          // hydrate the necessary properties for identification in the walker (read as: insert magic here)
          foreach ( $post_types as &$post_type ) {
              $post_type->classes = array();
              $post_type->type = $post_type->name;
              $post_type->object_id = $post_type->name;
              $post_type->title = $post_type->labels->name . ' ' . __( 'Archive', 'default' );
              $post_type->object = 'cpt-archive';
              $post_type->menu_item_parent = 0;
              $post_type->url = 0;
              $post_type->target = 0;
              $post_type->attr_title = 0;
              $post_type->xfn = 0;
              $post_type->db_id = 0;
          }
          // the native menu checklist
          $walker = new Walker_Nav_Menu_Checklist( array() );
          ?>
              <div id="cpt-archive" class="posttypediv">
                  <div id="tabs-panel-cpt-archive" class="tabs-panel tabs-panel-active">
                      <ul id="ctp-archive-checklist" class="categorychecklist form-no-clear">
                      <?php
                      echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $post_types), 0, (object) array( 'walker' => $walker) );
                      ?>
                      </ul>
                  </div><!-- /.tabs-panel -->
  
                  <p class="button-controls">
                      <span class="add-to-menu">
                          <input type="submit"<?php disabled( $nav_menu_selected_id, 0 ); ?> class="button-secondary right submit-add-to-menu" value="<?php esc_attr_e('Add to Menu'); ?>" name="add-ctp-archive-menu-item" id="submit-cpt-archive" />
                          <span class="spinner"></span>
                      </span>
                  </p>
              </div>
          <?php
      }, 'nav-menus', 'side', 'default' );
  });
  // take care of the URLs and active status for CPT Archives in menus
  add_filter( 'wp_get_nav_menu_items', function( $items, $menu, $args ) {
      // alter the URL for cpt-archive objects
      foreach ( $items as &$item ) {
          if ( $item->object != 'cpt-archive' ) continue;
          $item->url = get_post_type_archive_link( $item->type );
          // set current
          if ( get_query_var( 'post_type' ) == $item->type ) {
              $item->classes []= 'current-menu-item';
              $item->current = true;
          }
      }
  
      return $items;
  }, 10, 3 );
}
<?php
// archiveboard.php (companion to cpt-board.php)
// by Greg Schoppe (http://gschoppe.com)
// Licensed under GPL
// Renders archive page for board of directors custom post type
// Will need to be customized to match theme.  This one is customized for the _TK bootstrap theme

get_header(); ?>
	<?php // add the class "panel" below here to wrap the content-padder in Bootstrap style ;) ?>
	<div class="content-padder">
		<?php if ( have_posts() ) : ?>
			<header class="col-xs-12">
				<h1 class="page-title">
					Board of Directors
				</h1>
				<?php
					// Show an optional term description.
					$term_description = term_description();
					if ( ! empty( $term_description ) ) :
						printf( '<div class="taxonomy-description">%s</div>', $term_description );
					endif;
				?>
			</header><!-- .page-header -->
			<?php /* Start the Loop */ ?>
			<div class="board-members col-xs-12">
                <?php while ( have_posts() ) : the_post(); ?>
                <a name="<?=get_the_ID()?>"></a>
                <div class="row">
                    <div class="col-sm-2">
                        <?php
                        if(get_post_meta(get_the_ID(), '_board_member_pic', true)) {
                            $imgID      = get_post_meta(get_the_ID(), '_board_member_pic', true);
                            $imgDetails = wp_get_attachment_image_src( $imgID, "board-thumb" );
                            $imgSrc     = isset($imgDetails[0])?$imgDetails[0]:"";
                            if($imgSrc) {
                        ?>
                            <div class="board-image-wrap"><img src="<?=$imgSrc?>" alt="<?php the_title(); ?>"/></div>
                        <?php
                            }
                        }
                        ?>
                    </div>
                    <article id="board-<?php the_ID(); ?>" class="board-member col-sm-10">
                        <header>
                            <h2 class="page-title"><?php the_title(); ?></h2>
                            <div class="board-meta">
                                
                                <?php
                                if(get_post_meta(get_the_ID(), '_board_member_title', true)) { 
                                ?>
                                    <span class="board-position"><?=get_post_meta(get_the_ID(), '_board_member_title', true).", "?></span>
                                <?php
                                }
                                if(get_post_meta(get_the_ID(), '_board_member_company', true)) { 
                                ?>
                                    <span class="board-company" ><?=get_post_meta(get_the_ID(), '_board_member_company', true)?></span>
                                <?php } ?>
                            </div>
                        </header><!-- .entry-header -->
                        <div class="entry-content">
                            <?php the_content( __( 'Continue reading <span class="meta-nav">&rarr;</span>', '_tk' ) ); ?>
                            <?php
                                wp_link_pages( array(
                                    'before' => '<div class="page-links">' . __( 'Pages:', '_tk' ),
                                    'after'  => '</div>',
                                ) );
                            ?>
                        </div><!-- .entry-content -->
                    </article><!-- #post-## -->
                </div>
                <?php endwhile; ?>
                <?php _tk_content_nav( 'nav-below' ); ?>
            </div>
		<?php else : ?>
			<?php get_template_part( 'no-results', 'archive' ); ?>
		<?php endif; ?>
	</div><!-- .content-padder -->
<?php get_footer(); ?>