WordPress custom post types

Register WordPress custom post type with custom category, custom tag, default post category and tags:

<?php
if( ! function_exists( 'quote_create_post_type' ) ) :
	function quote_create_post_type() {
		$labels = array(
			'name' => 'Quote',
			'singular_name' => 'Quote',
			'add_new' => 'Add quote',
			'all_items' => 'All quotes',
			'add_new_item' => 'Add quote',
			'edit_item' => 'Edit quote',
			'new_item' => 'New quote',
			'view_item' => 'View quote',
			'search_items' => 'Search quotes',
			'not_found' => 'No quotes found',
			'not_found_in_trash' => 'No quotes found in trash',
			'parent_item_colon' => 'Parent quote'
			//'menu_name' => default to 'name'
		);
		$args = array(
			'labels' => $labels,
			'public' => true,
			'has_archive' => true,
			'publicly_queryable' => true,
			'query_var' => true,
			'rewrite' => true,
			'capability_type' => 'post',
			'hierarchical' => false,
			'supports' => array(
				'title',
				'editor',
				'excerpt',
				'thumbnail',
				//'author',
				//'trackbacks',
				//'custom-fields',
				//'comments',
				'revisions',
				//'page-attributes', // (menu order, hierarchical must be true to show Parent option)
				//'post-formats',
			),
			'taxonomies' => array( 'category', 'post_tag' ), // add default post categories and tags
			'menu_position' => 5,
			'register_meta_box_cb' => 'quote_add_post_type_metabox'
		);
		register_post_type( 'quote', $args );
		//flush_rewrite_rules();
 
		register_taxonomy( 'quote_category', // register custom taxonomy - category
			'quote',
			array(
				'hierarchical' => true,
				'label' => 'Quote categories'
			)
		);
		register_taxonomy( 'quote_tag', // register custom taxonomy - tag
			'quote',
			array(
				'hierarchical' => false,
				'label' => 'Quote tags'
			)
		);
	}
	add_action( 'init', 'quote_create_post_type' );
 
 
	function quote_add_post_type_metabox() { // add the meta box
		add_meta_box( 'quote_metabox', 'Meta', 'quote_metabox', 'quote', 'normal' );
	}
 
 
	function quote_metabox() {
		global $post;
		// Noncename needed to verify where the data originated
		echo '<input type="hidden" name="quote_post_noncename" value="' . wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
 
		// Get the data if its already been entered
		$quote_post_name = get_post_meta($post->ID, '_quote_post_name', true);
		$quote_post_desc = get_post_meta($post->ID, '_quote_post_desc', true);
 
		// Echo out the field
		?>
 
		<div class="width_full p_box">
			<p>
				<label>Name<br>
					<input type="text" name="quote_post_name" class="widefat" value="<?php echo $quote_post_name; ?>">
				</label>
			</p>
			<p><label>Description<br>
					<textarea name="quote_post_desc" class="widefat"><?php echo $quote_post_desc; ?></textarea>
				</label>
			</p>
		</div>
	<?php
	}
 
 
	function quote_post_save_meta( $post_id, $post ) { // save the data
		// verify this came from the our screen and with proper authorization,
		// because save_post can be triggered at other times
		if( !wp_verify_nonce( $_POST['quote_post_noncename'], plugin_basename(__FILE__) ) ) {
			return $post->ID;
		}
 
		// is the user allowed to edit the post or page?
		if( ! current_user_can( 'edit_post', $post->ID )){
			return $post->ID;
		}
		// ok, we're authenticated: we need to find and save the data
		// we'll put it into an array to make it easier to loop though
 
		$quote_post_meta['_quote_post_name'] = $_POST['quote_post_name'];
		$quote_post_meta['_quote_post_desc'] = $_POST['quote_post_desc'];
 
		// add values as custom fields
		foreach( $quote_post_meta as $key => $value ) { // cycle through the $quote_post_meta array
			// if( $post->post_type == 'revision' ) return; // don't store custom data twice
			$value = implode(',', (array)$value); // if $value is an array, make it a CSV (unlikely)
			if( get_post_meta( $post->ID, $key, FALSE ) ) { // if the custom field already has a value
				update_post_meta($post->ID, $key, $value);
			} else { // if the custom field doesn't have a value
				add_post_meta( $post->ID, $key, $value );
			}
			if( !$value ) { // delete if blank
				delete_post_meta( $post->ID, $key );
			}
		}
	}
	add_action( 'save_post', 'quote_post_save_meta', 1, 2 ); // save the custom fields
endif; // end of function_exists()
 
 
if( ! function_exists( 'view_quotes_posts' ) ) : // output
	function view_quotes_posts( $num = 4, $do_shortcode = 1, $strip_shortcodes = 0 ) {
 
		$args = array(
			'numberposts'     => $num,
			'offset'          => 0,
			//'category'        => ,
			'orderby'         => 'menu_order, post_title', // post_date, rand
			'order'           => 'DESC',
			//'include'         => ,
			//'exclude'         => ,
			//'meta_key'        => ,
			//'meta_value'      => ,
			'post_type'       => 'quote',
			//'post_mime_type'  => ,
			//'post_parent'     => ,
			'post_status'     => 'publish',
			'suppress_filters' => true
		);
 
		$posts = get_posts( $args );
 
		$html = '';
		foreach ( $posts as $post ) {
			$meta_name = get_post_meta( $post->ID, '_quote_post_name', true );
			$meta_desc = get_post_meta( $post->ID, '_quote_post_desc', true );
			$img = get_the_post_thumbnail( $post->ID, 'medium' );
			if( empty( $img ) ) {
				$img = '<img src="'.plugins_url( '/img/default.png', __FILE__ ).'">';
			}
 
 
			if( has_post_thumbnail( $post->ID ) ) {
				$img = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'thumbnail' );
				$img_url = $img[0];
 
				//the_post_thumbnail( 'thumbnail' ); /* thumbnail, medium, large, full, thumb-100, thumb-200, thumb-400, array(100,100) */
			}
 
			$content = $post->post_content;
			if( $do_shortcode == 1 ) {
				$content = do_shortcode( $content );
			}
			if( $strip_shortcodes == 1 ) {
				$content = strip_shortcodes( $content );
			}
			$content = wp_trim_words( $content, 30, '...');
			$content = wpautop( $content );
 
			$html .= '
			<div>
				<h3>'.$post->post_title.'</h3>
				<div>
					<p>Name: '.$meta_name.'</p>
					<p>Description: '.$meta_desc.'</p>
				</div>
				<div>'.$img.'</div>
				<div>'.$content.'</div>
			</div>
    		';
		}
		$html = '<div class="wrapper">'.$html.'</div>';
		return $html;
	}
endif; // end of function_exists()
?>

Usage in templates:

<?php
if( function_exists( 'view_quotes_posts' ) ) {
    echo view_quotes_posts();
}
?>

Show list of all not empty custom taxonomies (custom post type categories):

<?php
$tax_name = 'quote_category';
$args = array(
	'hide_empty' => false,
	'hierarchical' => false,
	'parent' => 0
);
$taxonomies = get_terms($tax_name, $args);
echo '<ul class="taxonomy">';
foreach( $taxonomies as $taxonomy ){
	$link = get_term_link( $taxonomy, $tax_name );
	echo '<li>';
	echo '<a href="'.$link.'">';
	echo $taxonomy->name;
	echo '</a>';
	$sub_args = array(
		'hide_empty' => false,
		'hierarchical' => false,
		'parent' => 0
	);
	$sub_taxonomies = get_terms($tax_name, $sub_args);
	$children = get_term_children( $taxonomy->term_id, $tax_name );
	echo ' c= '.count($children);
	echo '</li>';
}
echo '</ul>';
?>

List of custom post types (archive) can be viewed by such link: http://site.com/quote/
WordPress by default uses the archive template of your theme to display the custom post type archive page - archive.php.
If you want to create a custom archive page for your custom post type, then you would need to create a new file called archive-quote.php.

Example of archive-quote.php file:

<?php
get_header();
if(have_posts()) : while(have_posts()) : the_post();
	the_title();
	echo '<div class="entry-content">';
	the_content();
	echo '</div>';
endwhile; endif;
get_footer();
?>

You may contact via feedback form.