CPT (Custom Post Type) WordPress – Plugin boilerplate

Source

 

This plugin includes a couple common features that are used with custom post types:

  • Registers a post type
  • Registers a custom taxonomy
  • Registers a few metaboxes (Title, Twitter, Facebook, LinkedIn)
  • Adds the featured image to the admin column display
  • Adds the post count to the admin dashboard
<?php
/**
* Dashboard Glancer
*
* @package Team_Post_Type
* @author Gary Jones
* @link http://gamajo.com/dashboard-glancer
* @copyright 2014 Gary Jones, Gamajo Tech
* @license GPL-2.0+
*/
/**
* Easily add items to the At a Glance Dashboard widget in WordPress 3.8+.
*
* @package Dashboard_Glancer
* @author Gary Jones
*/
class Dashboard_Glancer {
/**
* Hold all of the items to show.
*
* @since 1.0.0
*
* @type array
*/
protected $items;
/**
* Automatically show any registered items.
*
* With this, there's no need to explicitly call show() during the dashboard_glance_items hook,
* and items can be registered at any time before dashboard_glance_items priority 20 (including on earlier hooks).
*
* @since 1.0.0
*/
public function __construct() {
add_action( 'dashboard_glance_items', array( $this, 'show' ), 20 );
}
/**
* Register one or more post type items to be shown on the dashboard widget.
*
* @since 1.0.0
*
* @param array|string $post_types Post type name, or array of post type names.
* @param array|string $statuses Post status or array of different post type statuses
*
* @return Return early if action hook has already passed, or no valid post types were given.
*/
public function add( $post_types, $statuses = 'publish' ) {
// If relevant output action hook has already passed, then no point in proceeding.
if ( did_action( 'dashboard_glance_items' ) ) {
_doing_it_wrong( __CLASS__, __( 'Trying to add At a Glance items to dashboard widget afterhook already fired', 'gamajo-dashboard-glancer' ), '1.0.0' );
return;
}
$post_types = $this->unset_invalid_post_types( (array) $post_types );
// If all given post types were invalid, bail now
if ( ! $post_types ) {
return;
}
// Register each combination of given post type and status
foreach( $post_types as $post_type ) {
foreach ( (array) $statuses as $status ) {
$this->items[] = array(
'type' => $post_type,
'status' => $status, // No checks yet to see if status is valid
);
}
}
}
/**
* Show the items on the dashboard widget.
*
* @since 1.0.0
*/
public function show() {
foreach ( $this->items as $item ) {
echo $this->get_single_item( $item );
}
// Reset items, so items aren't shown again if show() is re-called
unset( $this->items );
}
/**
* Check one or more post types to see if they are valid.
*
* @since 1.0.0
*
* @param array $post_types Each of the post types to check.
*
* @return array List of the given post types that are valid.
*/
protected function unset_invalid_post_types( array $post_types ) {
foreach( $post_types as $index => $post_type ) {
$post_type_object = get_post_type_object( $post_type );
if ( is_null( $post_type_object ) ) {
unset( $post_types[ $index ] );
}
}
return $post_types;
}
/**
* Build and return the data and markup for a single item.
*
* If the item count is zero, return an empty string, to avoid visual clutter.
*
* @since 1.0.0
*
* @param array $item Registered item.
*
* @return string Markup, or empty string if item count is zero.
*/
protected function get_single_item( array $item ) {
$num_posts = wp_count_posts( $item['type'] );
$count = $num_posts->$item['status'];
if ( ! $count ) {
return '';
}
$href = $this->get_link_url( $item );
$text = number_format_i18n( $count ) . ' ' . $this->get_label( $item, $count );
$text = $this->maybe_link( $text, $href );
return $this->get_markup( $text, $item['type'] );
}
/**
* Get the singular or plural label for an item.
*
* @since 1.0.0
*
* @param array $item Registered item.
* @param int $count Number of items present in WP.
*
* @return string
*/
protected function get_label( array $item, $count ) {
$post_type_object = get_post_type_object( $item['type'] );
if ( 1 === $count ) {
$label = $post_type_object->labels->singular_name;
} else {
$label = $post_type_object->labels->name;
}
// Append status for non-publish statuses for disambiguation
if ( 'publish' !== $item['status'] ) {
$label .= ' (' . $item['status'] . ')';
}
return $label;
}
/**
* Build the URL that linked items use.
*
* @since 1.0.0
*
* @param array $item Registered item.
*
* @return string Admin URL to view the entries of the given post type with the given status
*/
public function get_link_url( array $item ) {
return 'edit.php?post_status=' . $item['status'] . '&post_type=' . $item['type'];
}
/**
* Wrap a glance item in a link, if the current user can edit posts.
*
* @since 1.0.0
*
* @param string $text Text to potentially wrap in a link.
* @param string $href Link target.
*
* @return string Text wrapped in a link if current user can edit posts, or original text otherwise.
*/
protected function maybe_link( $text, $href ) {
if ( current_user_can( 'edit_posts' ) ) {
return '<a href="' . esc_url( $href ) . '">' . $text . '</a>';
}
return $text;
}
/**
* Wrap number and text within list item markup.
*
* @since 1.0.0
*
* @param string $text Text to display. May be wrapped in a link.
*/
protected function get_markup( $text, $post_type ) {
return '<li class="' . sanitize_html_class( $post_type . '-count' ) . '">' . $text . '</li>' . "\n";
}
}

include/class-post-type-admin.php

<?php
/**
* Team Post Type
*
* @package Team_Post_Type
* @license GPL-2.0+
*/
/**
* Register post types and taxonomies.
*
* @package Team_Post_Type
*/
class Team_Post_Type_Admin {
protected $registration_handler;
public function __construct( $registration_handler ) {
$this->registration_handler = $registration_handler;
}
public function init() {
// Add thumbnail support for this post type
add_theme_support( 'post-thumbnails', array( $this->registration_handler->post_type ) );
// Add thumbnails to column view
add_filter( 'manage_edit-' . $this->registration_handler->post_type . '_columns', array( $this, 'add_image_column'), 10, 1 );
add_action( 'manage_' . $this->registration_handler->post_type . '_posts_custom_column', array( $this, 'display_image' ), 10, 1 );
// Allow filtering of posts by taxonomy in the admin view
add_action( 'restrict_manage_posts', array( $this, 'add_taxonomy_filters' ) );
// Show post counts in the dashboard
add_action( 'right_now_content_table_end', array( $this, 'add_rightnow_counts' ) );
add_action( 'dashboard_glance_items', array( $this, 'add_glance_counts' ) );
}
/**
* Add columns to post type list screen.
*
* @link http://wptheming.com/2010/07/column-edit-pages/
*
* @param array $columns Existing columns.
*
* @return array Amended columns.
*/
public function add_image_column( $columns ) {
$column_thumbnail = array( 'thumbnail' => __( 'Image', 'team-post-type' ) );
return array_slice( $columns, 0, 2, true ) + $column_thumbnail + array_slice( $columns, 1, null, true );
}
/**
* Custom column callback
*
* @global stdClass $post Post object.
*
* @param string $column Column ID.
*/
public function display_image( $column ) {
// global $post;
switch ( $column ) {
case 'thumbnail':
// echo get_the_post_thumbnail( $post->ID, array(35, 35) );
echo get_the_post_thumbnail( get_the_ID(), array( 35, 35 ) );
break;
}
}
/**
* Add taxonomy filters to the post type list page.
*
* Code artfully lifted from http://pippinsplugins.com/
*
* @global string $typenow
*/
public function add_taxonomy_filters() {
global $typenow;
// Must set this to the post type you want the filter(s) displayed on
if ( $this->registration_handler->post_type !== $typenow ) {
return;
}
foreach ( $this->registration_handler->taxonomies as $tax_slug ) {
echo $this->build_taxonomy_filter( $tax_slug );
}
}
/**
* Build an individual dropdown filter.
*
* @param string $tax_slug Taxonomy slug to build filter for.
*
* @return string Markup, or empty string if taxonomy has no terms.
*/
protected function build_taxonomy_filter( $tax_slug ) {
$terms = get_terms( $tax_slug );
if ( 0 == count( $terms ) ) {
return '';
}
$tax_name = $this->get_taxonomy_name_from_slug( $tax_slug );
$current_tax_slug = isset( $_GET[$tax_slug] ) ? $_GET[$tax_slug] : false;
$filter = '<select name="' . esc_attr( $tax_slug ) . '" id="' . esc_attr( $tax_slug ) . '" class="postform">';
$filter .= '<option value="0">' . esc_html( $tax_name ) .'</option>';
$filter .= $this->build_term_options( $terms, $current_tax_slug );
$filter .= '</select>';
return $filter;
}
/**
* Get the friendly taxonomy name, if given a taxonomy slug.
*
* @param string $tax_slug Taxonomy slug.
*
* @return string Friendly name of taxonomy, or empty string if not a valid taxonomy.
*/
protected function get_taxonomy_name_from_slug( $tax_slug ) {
$tax_obj = get_taxonomy( $tax_slug );
if ( ! $tax_obj )
return '';
return $tax_obj->labels->name;
}
/**
* Build a series of option elements from an array.
*
* Also checks to see if one of the options is selected.
*
* @param array $terms Array of term objects.
* @param string $current_tax_slug Slug of currently selected term.
*
* @return string Markup.
*/
protected function build_term_options( $terms, $current_tax_slug ) {
$options = '';
foreach ( $terms as $term ) {
$options .= sprintf(
'<option value="%s"%s />%s</option>',
esc_attr( $term->slug ),
selected( $current_tax_slug, $term->slug ),
esc_html( $term->name . '(' . $term->count . ')' )
);
}
return $options;
}
/**
* Add counts to "At a Glance" dashboard widget in WP 3.8+
*
* @since 0.1.0
*/
public function add_glance_counts() {
$glancer = new Dashboard_Glancer;
$glancer->add( $this->registration_handler->post_type, array( 'publish', 'pending' ) );
}
}

include/class-post-type-metaboxes.php

<?php
/**
* Team Post Type
*
* @package Team_Post_Type
* @license GPL-2.0+
*/
/**
* Register metaboxes.
*
* @package Team_Post_Type
*/
class Team_Post_Type_Metaboxes {
public function init() {
add_action( 'add_meta_boxes', array( $this, 'team_meta_boxes' ) );
add_action( 'save_post', array( $this, 'save_meta_boxes' ), 10, 2 );
}
/**
* Register the metaboxes to be used for the team post type
*
* @since 0.1.0
*/
public function team_meta_boxes() {
add_meta_box(
'profile_fields',
'Profile Fields',
array( $this, 'render_meta_boxes' ),
'team',
'normal',
'high'
);
}
/**
* The HTML for the fields
*
* @since 0.1.0
*/
function render_meta_boxes( $post ) {
$meta = get_post_custom( $post->ID );
$title = ! isset( $meta['profile_title'][0] ) ? '' : $meta['profile_title'][0];
$twitter = ! isset( $meta['profile_twitter'][0] ) ? '' : $meta['profile_twitter'][0];
$linkedin = ! isset( $meta['profile_linkedin'][0] ) ? '' : $meta['profile_linkedin'][0];
$facebook = ! isset( $meta['profile_facebook'][0] ) ? '' : $meta['profile_facebook'][0];
wp_nonce_field( basename( __FILE__ ), 'profile_fields' ); ?>
<table class="form-table">
<tr>
<td class="team_meta_box_td" colspan="2">
<label for="profile_title"><?php _e( 'Title', 'team-post-type' ); ?>
</label>
</td>
<td colspan="4">
<input type="text" name="profile_title" class="regular-text" value="<?php echo $title; ?>">
<p class="description"><?php _e( 'E.g. CEO, Sales Lead, Designer', 'team-post-type' ); ?></p>
</td>
</tr>
<tr>
<td class="team_meta_box_td" colspan="2">
<label for="profile_linkedin"><?php _e( 'LinkedIn URL', 'team-post-type' ); ?>
</label>
</td>
<td colspan="4">
<input type="text" name="profile_linkedin" class="regular-text" value="<?php echo $linkedin; ?>">
</td>
</tr>
<tr>
<td class="team_meta_box_td" colspan="2">
<label for="profile_twitter"><?php _e( 'Twitter URL', 'team-post-type' ); ?>
</label>
</td>
<td colspan="4">
<input type="text" name="profile_twitter" class="regular-text" value="<?php echo $twitter; ?>">
</td>
</tr>
<tr>
<td class="team_meta_box_td" colspan="2">
<label for="profile_facebook"><?php _e( 'Facebook URL', 'team-post-type' ); ?>
</label>
</td>
<td colspan="4">
<input type="text" name="profile_facebook" class="regular-text" value="<?php echo $facebook; ?>">
</td>
</tr>
</table>
<?php }
/**
* Save metaboxes
*
* @since 0.1.0
*/
function save_meta_boxes( $post_id ) {
global $post;
// Verify nonce
if ( !isset( $_POST['profile_fields'] ) || !wp_verify_nonce( $_POST['profile_fields'], basename(__FILE__) ) ) {
return $post_id;
}
// Check Autosave
if ( (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || ( defined('DOING_AJAX') && DOING_AJAX) || isset($_REQUEST['bulk_edit']) ) {
return $post_id;
}
// Don't save if only a revision
if ( isset( $post->post_type ) && $post->post_type == 'revision' ) {
return $post_id;
}
// Check permissions
if ( !current_user_can( 'edit_post', $post->ID ) ) {
return $post_id;
}
$meta['profile_title'] = ( isset( $_POST['profile_title'] ) ? esc_textarea( $_POST['profile_title'] ) : '' );
$meta['profile_linkedin'] = ( isset( $_POST['profile_linkedin'] ) ? esc_url( $_POST['profile_linkedin'] ) : '' );
$meta['profile_twitter'] = ( isset( $_POST['profile_twitter'] ) ? esc_url( $_POST['profile_twitter'] ) : '' );
$meta['profile_facebook'] = ( isset( $_POST['profile_facebook'] ) ? esc_url( $_POST['profile_facebook'] ) : '' );
foreach ( $meta as $key => $value ) {
update_post_meta( $post->ID, $key, $value );
}
}
}

include/class-post-type-registrations.php

<?php
/**
* Team Post Type
*
* @package Team_Post_Type
* @license GPL-2.0+
*/
/**
* Register post types and taxonomies.
*
* @package Team_Post_Type
*/
class Team_Post_Type_Registrations {
public $post_type = 'team';
public $taxonomies = array( 'team-category' );
public function init() {
// Add the team post type and taxonomies
add_action( 'init', array( $this, 'register' ) );
}
/**
* Initiate registrations of post type and taxonomies.
*
* @uses Team_Post_Type_Registrations::register_post_type()
* @uses Team_Post_Type_Registrations::register_taxonomy_category()
*/
public function register() {
$this->register_post_type();
$this->register_taxonomy_category();
}
/**
* Register the custom post type.
*
* @link http://codex.wordpress.org/Function_Reference/register_post_type
*/
protected function register_post_type() {
$labels = array(
'name' => __( 'Team', 'team-post-type' ),
'singular_name' => __( 'Team Member', 'team-post-type' ),
'add_new' => __( 'Add Profile', 'team-post-type' ),
'add_new_item' => __( 'Add Profile', 'team-post-type' ),
'edit_item' => __( 'Edit Profile', 'team-post-type' ),
'new_item' => __( 'New Team Member', 'team-post-type' ),
'view_item' => __( 'View Profile', 'team-post-type' ),
'search_items' => __( 'Search Team', 'team-post-type' ),
'not_found' => __( 'No profiles found', 'team-post-type' ),
'not_found_in_trash' => __( 'No profiles in the trash', 'team-post-type' ),
);
$supports = array(
'title',
'editor',
'thumbnail',
'custom-fields',
'revisions',
);
$args = array(
'labels' => $labels,
'supports' => $supports,
'public' => true,
'capability_type' => 'post',
'rewrite' => array( 'slug' => 'team', ), // Permalinks format
'menu_position' => 30,
'menu_icon' => 'dashicons-id',
);
$args = apply_filters( 'team_post_type_args', $args );
register_post_type( $this->post_type, $args );
}
/**
* Register a taxonomy for Team Categories.
*
* @link http://codex.wordpress.org/Function_Reference/register_taxonomy
*/
protected function register_taxonomy_category() {
$labels = array(
'name' => __( 'Team Categories', 'team-post-type' ),
'singular_name' => __( 'Team Category', 'team-post-type' ),
'menu_name' => __( 'Team Categories', 'team-post-type' ),
'edit_item' => __( 'Edit Team Category', 'team-post-type' ),
'update_item' => __( 'Update Team Category', 'team-post-type' ),
'add_new_item' => __( 'Add New Team Category', 'team-post-type' ),
'new_item_name' => __( 'New Team Category Name', 'team-post-type' ),
'parent_item' => __( 'Parent Team Category', 'team-post-type' ),
'parent_item_colon' => __( 'Parent Team Category:', 'team-post-type' ),
'all_items' => __( 'All Team Categories', 'team-post-type' ),
'search_items' => __( 'Search Team Categories', 'team-post-type' ),
'popular_items' => __( 'Popular Team Categories', 'team-post-type' ),
'separate_items_with_commas' => __( 'Separate team categories with commas', 'team-post-type' ),
'add_or_remove_items' => __( 'Add or remove team categories', 'team-post-type' ),
'choose_from_most_used' => __( 'Choose from the most used team categories', 'team-post-type' ),
'not_found' => __( 'No team categories found.', 'team-post-type' ),
);
$args = array(
'labels' => $labels,
'public' => true,
'show_in_nav_menus' => true,
'show_ui' => true,
'show_tagcloud' => true,
'hierarchical' => true,
'rewrite' => array( 'slug' => 'team-category' ),
'show_admin_column' => true,
'query_var' => true,
);
$args = apply_filters( 'team_post_type_category_args', $args );
register_taxonomy( $this->taxonomies[0], $this->post_type, $args );
}
}

include/class-post-type.php

<?php
/**
 * Team Post Type
 *
 * @package   Team_Post_Type
 * @license   GPL-2.0+
 */

/**
 * Registration of CPT and related taxonomies.
 *
 * @since 0.1.0
 */
class Team_Post_Type {

	/**
	 * Plugin version, used for cache-busting of style and script file references.
	 *
	 * @since 0.1.0
	 *
	 * @var string VERSION Plugin version.
	 */
	const VERSION = '0.1.0';

	/**
	 * Unique identifier for your plugin.
	 *
	 * Use this value (not the variable name) as the text domain when internationalizing strings of text. It should
	 * match the Text Domain file header in the main plugin file.
	 *
	 * @since 0.1.0
	 *
	 * @var string
	 */
	const PLUGIN_SLUG = 'team-post-type';

	protected $registration_handler;

	/**
	 * Initialize the plugin by setting localization and new site activation hooks.
	 *
	 * @since 0.1.0
	 */
	public function __construct( $registration_handler ) {

		$this->registration_handler = $registration_handler;

		// Load plugin text domain
		add_action( 'init', array( $this, 'load_plugin_textdomain' ) );;

	}

	/**
	 * Fired for each blog when the plugin is activated.
	 *
	 * @since 0.1.0
	 */
	public function activate() {
		$this->registration_handler->register();
		flush_rewrite_rules();
	}

	/**
	 * Fired for each blog when the plugin is deactivated.
	 *
	 * @since 0.1.0
	 */
	public function deactivate() {
		flush_rewrite_rules();
	}

	/**
	 * Load the plugin text domain for translation.
	 *
	 * @since 0.1.0
	 */
	public function load_plugin_textdomain() {
		$domain = self::PLUGIN_SLUG;
		load_plugin_textdomain( $domain, FALSE, dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages' );
	}

}

 

2 thoughts on “CPT (Custom Post Type) WordPress – Plugin boilerplate

Leave a Reply

Your email address will not be published. Required fields are marked *


8 × = cinquante six

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>