+ $args->cap = get_post_type_capabilities( $args );
+ unset($args->capabilities);
+
+ if ( is_array( $args->capability_type ) )
+ $args->capability_type = $args->capability_type[0];
+
+ if ( ! empty($args->supports) ) {
+ add_post_type_support($post_type, $args->supports);
+ unset($args->supports);
+ } else {
+ // Add default features
+ add_post_type_support($post_type, array('title', 'editor'));
+ }
+
+ if ( false !== $args->query_var && !empty($wp) ) {
+ if ( true === $args->query_var )
+ $args->query_var = $post_type;
+ $args->query_var = sanitize_title_with_dashes($args->query_var);
+ $wp->add_query_var($args->query_var);
+ }
+
+ if ( false !== $args->rewrite && '' != get_option('permalink_structure') ) {
+ if ( ! is_array( $args->rewrite ) )
+ $args->rewrite = array();
+ if ( empty( $args->rewrite['slug'] ) )
+ $args->rewrite['slug'] = $post_type;
+ if ( ! isset( $args->rewrite['with_front'] ) )
+ $args->rewrite['with_front'] = true;
+ if ( ! isset( $args->rewrite['pages'] ) )
+ $args->rewrite['pages'] = true;
+ if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive )
+ $args->rewrite['feeds'] = (bool) $args->has_archive;
+
+ if ( $args->hierarchical )
+ $wp_rewrite->add_rewrite_tag("%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=");
+ else
+ $wp_rewrite->add_rewrite_tag("%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=");
+
+ if ( $args->has_archive ) {
+ $archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive;
+ if ( $args->rewrite['with_front'] )
+ $archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
+ else
+ $archive_slug = $wp_rewrite->root . $archive_slug;
+
+ $wp_rewrite->add_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
+ if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
+ $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
+ $wp_rewrite->add_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
+ $wp_rewrite->add_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
+ }
+ if ( $args->rewrite['pages'] )
+ $wp_rewrite->add_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
+ }
+
+ $wp_rewrite->add_permastruct($post_type, "{$args->rewrite['slug']}/%$post_type%", $args->rewrite['with_front'], $args->permalink_epmask);
+ }
+
+ if ( $args->register_meta_box_cb )
+ add_action('add_meta_boxes_' . $post_type, $args->register_meta_box_cb, 10, 1);
+
+ $args->labels = get_post_type_labels( $args );
+ $args->label = $args->labels->name;
+
+ $wp_post_types[$post_type] = $args;
+
+ add_action( 'future_' . $post_type, '_future_post_hook', 5, 2 );
+
+ foreach ( $args->taxonomies as $taxonomy ) {
+ register_taxonomy_for_object_type( $taxonomy, $post_type );
+ }
+
+ return $args;
+}
+
+/**
+ * Builds an object with all post type capabilities out of a post type object
+ *
+ * Post type capabilities use the 'capability_type' argument as a base, if the
+ * capability is not set in the 'capabilities' argument array or if the
+ * 'capabilities' argument is not supplied.
+ *
+ * The capability_type argument can optionally be registered as an array, with
+ * the first value being singular and the second plural, e.g. array('story, 'stories')
+ * Otherwise, an 's' will be added to the value for the plural form. After
+ * registration, capability_type will always be a string of the singular value.
+ *
+ * By default, seven keys are accepted as part of the capabilities array:
+ *
+ * - edit_post, read_post, and delete_post are meta capabilities, which are then
+ * generally mapped to corresponding primitive capabilities depending on the
+ * context, which would be the post being edited/read/deleted and the user or
+ * role being checked. Thus these capabilities would generally not be granted
+ * directly to users or roles.
+ *
+ * - edit_posts - Controls whether objects of this post type can be edited.
+ * - edit_others_posts - Controls whether objects of this type owned by other users
+ * can be edited. If the post type does not support an author, then this will
+ * behave like edit_posts.
+ * - publish_posts - Controls publishing objects of this post type.
+ * - read_private_posts - Controls whether private objects can be read.
+
+ * These four primitive capabilities are checked in core in various locations.
+ * There are also seven other primitive capabilities which are not referenced
+ * directly in core, except in map_meta_cap(), which takes the three aforementioned
+ * meta capabilities and translates them into one or more primitive capabilities
+ * that must then be checked against the user or role, depending on the context.
+ *
+ * - read - Controls whether objects of this post type can be read.
+ * - delete_posts - Controls whether objects of this post type can be deleted.
+ * - delete_private_posts - Controls whether private objects can be deleted.
+ * - delete_published_posts - Controls whether published objects can be deleted.
+ * - delete_others_posts - Controls whether objects owned by other users can be
+ * can be deleted. If the post type does not support an author, then this will
+ * behave like delete_posts.
+ * - edit_private_posts - Controls whether private objects can be edited.
+ * - edit_published_posts - Controls whether published objects can be deleted.
+ *
+ * These additional capabilities are only used in map_meta_cap(). Thus, they are
+ * only assigned by default if the post type is registered with the 'map_meta_cap'
+ * argument set to true (default is false).
+ *
+ * @see map_meta_cap()
+ * @since 3.0.0
+ *
+ * @param object $args Post type registration arguments
+ * @return object object with all the capabilities as member variables
+ */
+function get_post_type_capabilities( $args ) {
+ if ( ! is_array( $args->capability_type ) )
+ $args->capability_type = array( $args->capability_type, $args->capability_type . 's' );
+
+ // Singular base for meta capabilities, plural base for primitive capabilities.
+ list( $singular_base, $plural_base ) = $args->capability_type;
+
+ $default_capabilities = array(
+ // Meta capabilities
+ 'edit_post' => 'edit_' . $singular_base,
+ 'read_post' => 'read_' . $singular_base,
+ 'delete_post' => 'delete_' . $singular_base,
+ // Primitive capabilities used outside of map_meta_cap():
+ 'edit_posts' => 'edit_' . $plural_base,
+ 'edit_others_posts' => 'edit_others_' . $plural_base,
+ 'publish_posts' => 'publish_' . $plural_base,
+ 'read_private_posts' => 'read_private_' . $plural_base,
+ );
+
+ // Primitive capabilities used within map_meta_cap():
+ if ( $args->map_meta_cap ) {
+ $default_capabilities_for_mapping = array(
+ 'read' => 'read',
+ 'delete_posts' => 'delete_' . $plural_base,
+ 'delete_private_posts' => 'delete_private_' . $plural_base,
+ 'delete_published_posts' => 'delete_published_' . $plural_base,
+ 'delete_others_posts' => 'delete_others_' . $plural_base,
+ 'edit_private_posts' => 'edit_private_' . $plural_base,
+ 'edit_published_posts' => 'edit_published_' . $plural_base,
+ );
+ $default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping );
+ }
+
+ $capabilities = array_merge( $default_capabilities, $args->capabilities );
+
+ // Remember meta capabilities for future reference.
+ if ( $args->map_meta_cap )
+ _post_type_meta_capabilities( $capabilities );
+
+ return (object) $capabilities;
+}
+
+/**
+ * Stores or returns a list of post type meta caps for map_meta_cap().
+ *
+ * @since 3.1.0
+ * @access private
+ */
+function _post_type_meta_capabilities( $capabilities = null ) {
+ static $meta_caps = array();
+ if ( null === $capabilities )
+ return $meta_caps;
+ foreach ( $capabilities as $core => $custom ) {
+ if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) )
+ $meta_caps[ $custom ] = $core;
+ }
+}
+
+/**
+ * Builds an object with all post type labels out of a post type object
+ *
+ * Accepted keys of the label array in the post type object:
+ * - name - general name for the post type, usually plural. The same and overriden by $post_type_object->label. Default is Posts/Pages
+ * - singular_name - name for one object of this post type. Default is Post/Page
+ * - add_new - Default is Add New for both hierarchical and non-hierarchical types. When internationalizing this string, please use a {@link http://codex.wordpress.org/I18n_for_WordPress_Developers#Disambiguation_by_context gettext context} matching your post type. Example: <code>_x('Add New', 'product');</code>
+ * - add_new_item - Default is Add New Post/Add New Page
+ * - edit_item - Default is Edit Post/Edit Page
+ * - new_item - Default is New Post/New Page
+ * - view_item - Default is View Post/View Page
+ * - search_items - Default is Search Posts/Search Pages
+ * - not_found - Default is No posts found/No pages found
+ * - not_found_in_trash - Default is No posts found in Trash/No pages found in Trash
+ * - parent_item_colon - This string isn't used on non-hierarchical types. In hierarchical ones the default is Parent Page:
+ *
+ * Above, the first default value is for non-hierarchical post types (like posts) and the second one is for hierarchical post types (like pages).
+ *
+ * @since 3.0.0
+ * @param object $post_type_object
+ * @return object object with all the labels as member variables
+ */
+function get_post_type_labels( $post_type_object ) {
+ $nohier_vs_hier_defaults = array(
+ 'name' => array( _x('Posts', 'post type general name'), _x('Pages', 'post type general name') ),
+ 'singular_name' => array( _x('Post', 'post type singular name'), _x('Page', 'post type singular name') ),
+ 'add_new' => array( _x('Add New', 'post'), _x('Add New', 'page') ),
+ 'add_new_item' => array( __('Add New Post'), __('Add New Page') ),
+ 'edit_item' => array( __('Edit Post'), __('Edit Page') ),
+ 'new_item' => array( __('New Post'), __('New Page') ),
+ 'view_item' => array( __('View Post'), __('View Page') ),
+ 'search_items' => array( __('Search Posts'), __('Search Pages') ),
+ 'not_found' => array( __('No posts found.'), __('No pages found.') ),
+ 'not_found_in_trash' => array( __('No posts found in Trash.'), __('No pages found in Trash.') ),
+ 'parent_item_colon' => array( null, __('Parent Page:') ),
+ );
+ $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
+ return _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults );
+}
+
+/**
+ * Builds an object with custom-something object (post type, taxonomy) labels out of a custom-something object
+ *
+ * @access private
+ * @since 3.0.0
+ */
+function _get_custom_object_labels( $object, $nohier_vs_hier_defaults ) {
+
+ if ( isset( $object->label ) && empty( $object->labels['name'] ) )
+ $object->labels['name'] = $object->label;
+
+ if ( !isset( $object->labels['singular_name'] ) && isset( $object->labels['name'] ) )
+ $object->labels['singular_name'] = $object->labels['name'];
+
+ if ( !isset( $object->labels['menu_name'] ) && isset( $object->labels['name'] ) )
+ $object->labels['menu_name'] = $object->labels['name'];
+
+ foreach ( $nohier_vs_hier_defaults as $key => $value )
+ $defaults[$key] = $object->hierarchical ? $value[1] : $value[0];
+
+ $labels = array_merge( $defaults, $object->labels );
+ return (object)$labels;
+}
+
+/**
+ * Adds submenus for post types.
+ *
+ * @access private
+ * @since 3.1.0
+ */
+function _add_post_type_submenus() {
+ foreach ( get_post_types( array( 'show_ui' => true ) ) as $ptype ) {
+ $ptype_obj = get_post_type_object( $ptype );
+ // Submenus only.
+ if ( ! $ptype_obj->show_in_menu || $ptype_obj->show_in_menu === true )
+ continue;
+ add_submenu_page( $ptype_obj->show_in_menu, $ptype_obj->labels->name, $ptype_obj->labels->menu_name, $ptype_obj->cap->edit_posts, "edit.php?post_type=$ptype" );
+ }
+}
+add_action( 'admin_menu', '_add_post_type_submenus' );
+
+/**
+ * Register support of certain features for a post type.
+ *
+ * All features are directly associated with a functional area of the edit screen, such as the
+ * editor or a meta box: 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author',
+ * 'excerpt', 'page-attributes', 'thumbnail', and 'custom-fields'.
+ *
+ * Additionally, the 'revisions' feature dictates whether the post type will store revisions,
+ * and the 'comments' feature dicates whether the comments count will show on the edit screen.
+ *
+ * @since 3.0.0
+ * @param string $post_type The post type for which to add the feature
+ * @param string|array $feature the feature being added, can be an array of feature strings or a single string
+ */
+function add_post_type_support( $post_type, $feature ) {
+ global $_wp_post_type_features;
+
+ $features = (array) $feature;
+ foreach ($features as $feature) {
+ if ( func_num_args() == 2 )
+ $_wp_post_type_features[$post_type][$feature] = true;
+ else
+ $_wp_post_type_features[$post_type][$feature] = array_slice( func_get_args(), 2 );
+ }
+}
+
+/**
+ * Remove support for a feature from a post type.
+ *
+ * @since 3.0.0
+ * @param string $post_type The post type for which to remove the feature
+ * @param string $feature The feature being removed
+ */
+function remove_post_type_support( $post_type, $feature ) {
+ global $_wp_post_type_features;
+
+ if ( !isset($_wp_post_type_features[$post_type]) )
+ return;
+
+ if ( isset($_wp_post_type_features[$post_type][$feature]) )
+ unset($_wp_post_type_features[$post_type][$feature]);
+}
+
+/**
+ * Checks a post type's support for a given feature
+ *
+ * @since 3.0.0
+ * @param string $post_type The post type being checked
+ * @param string $feature the feature being checked
+ * @return boolean
+ */
+
+function post_type_supports( $post_type, $feature ) {
+ global $_wp_post_type_features;
+
+ if ( !isset( $_wp_post_type_features[$post_type][$feature] ) )
+ return false;
+
+ // If no args passed then no extra checks need be performed
+ if ( func_num_args() <= 2 )
+ return true;
+
+ // @todo Allow pluggable arg checking
+ //$args = array_slice( func_get_args(), 2 );
+
+ return true;