+/**
+ * Retrieve a post status object by name.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_post_statuses List of post statuses.
+ *
+ * @see register_post_status()
+ *
+ * @param string $post_status The name of a registered post status.
+ * @return object A post status object.
+ */
+function get_post_status_object( $post_status ) {
+ global $wp_post_statuses;
+
+ if ( empty($wp_post_statuses[$post_status]) )
+ return null;
+
+ return $wp_post_statuses[$post_status];
+}
+
+/**
+ * Get a list of all registered post status objects.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_post_statuses List of post statuses.
+ *
+ * @see register_post_status()
+ *
+ * @param array|string $args Optional. Array or string of post status arguments. Default array.
+ * @param string $output Optional. The type of output to return. Accepts post status 'names'
+ * or 'objects'. Default 'names'.
+ * @param string $operator Optional. The logical operation to perform. 'or' means only one element
+ * from the array needs to match; 'and' means all elements must match.
+ * Default 'and'.
+ * @return array A list of post status names or objects.
+ */
+function get_post_stati( $args = array(), $output = 'names', $operator = 'and' ) {
+ global $wp_post_statuses;
+
+ $field = ('names' == $output) ? 'name' : false;
+
+ return wp_filter_object_list($wp_post_statuses, $args, $operator, $field);
+}
+
+/**
+ * Whether the post type is hierarchical.
+ *
+ * A false return value might also mean that the post type does not exist.
+ *
+ * @since 3.0.0
+ *
+ * @see get_post_type_object()
+ *
+ * @param string $post_type Post type name
+ * @return bool Whether post type is hierarchical.
+ */
+function is_post_type_hierarchical( $post_type ) {
+ if ( ! post_type_exists( $post_type ) )
+ return false;
+
+ $post_type = get_post_type_object( $post_type );
+ return $post_type->hierarchical;
+}
+
+/**
+ * Check if a post type is registered.
+ *
+ * @since 3.0.0
+ *
+ * @see get_post_type_object()
+ *
+ * @param string $post_type Post type name.
+ * @return bool Whether post type is registered.
+ */
+function post_type_exists( $post_type ) {
+ return (bool) get_post_type_object( $post_type );
+}
+
+/**
+ * Retrieve the post type of the current post or of a given post.
+ *
+ * @since 2.1.0
+ *
+ * @param int|WP_Post $post Optional. Post ID or post object. Default is global $post.
+ * @return string|bool Post type on success, false on failure.
+ */
+function get_post_type( $post = null ) {
+ if ( $post = get_post( $post ) )
+ return $post->post_type;
+
+ return false;
+}
+
+/**
+ * Retrieve a post type object by name.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_post_types List of post types.
+ *
+ * @see register_post_type()
+ *
+ * @param string $post_type The name of a registered post type.
+ * @return object A post type object.
+ */
+function get_post_type_object( $post_type ) {
+ global $wp_post_types;
+
+ if ( empty($wp_post_types[$post_type]) )
+ return null;
+
+ return $wp_post_types[$post_type];
+}
+
+/**
+ * Get a list of all registered post type objects.
+ *
+ * @since 2.9.0
+ *
+ * @global array $wp_post_types List of post types.
+ *
+ * @see register_post_type()
+ *
+ * @param array|string $args Optional. An array of key => value arguments to match against
+ * the post type objects. Default empty array.
+ * @param string $output Optional. The type of output to return. Accepts post type 'names'
+ * or 'objects'. Default 'names'.
+ * @param string $operator Optaionl. The logical operation to perform. 'or' means only one
+ * element from the array needs to match; 'and' means all elements
+ * must match. Default 'and'.
+ * @return array A list of post type names or objects.
+ */
+function get_post_types( $args = array(), $output = 'names', $operator = 'and' ) {
+ global $wp_post_types;
+
+ $field = ('names' == $output) ? 'name' : false;
+
+ return wp_filter_object_list($wp_post_types, $args, $operator, $field);
+}
+
+/**
+ * Register a post type. Do not use before init.
+ *
+ * A function for creating or modifying a post type based on the
+ * parameters given. The function will accept an array (second optional
+ * parameter), along with a string for the post type name.
+ *
+ * @since 2.9.0
+ *
+ * @global array $wp_post_types List of post types.
+ * @global WP_Rewrite $wp_rewrite Used for default feeds.
+ * @global WP $wp Used to add query vars.
+ *
+ * @param string $post_type Post type key, must not exceed 20 characters.
+ * @param array|string $args {
+ * Array or string of arguments for registering a post type.
+ *
+ * @type string $label Name of the post type shown in the menu. Usually plural.
+ * Default is value of $labels['name'].
+ * @type array $labels An array of labels for this post type. If not set, post
+ * labels are inherited for non-hierarchical types and page
+ * labels for hierarchical ones. {@see get_post_type_labels()}.
+ * @type string $description A short descriptive summary of what the post type is.
+ * Default empty.
+ * @type bool $public Whether a post type is intended for use publicly either via
+ * the admin interface or by front-end users. While the default
+ * settings of $exclude_from_search, $publicly_queryable, $show_ui,
+ * and $show_in_nav_menus are inherited from public, each does not
+ * rely on this relationship and controls a very specific intention.
+ * Default false.
+ * @type bool $hierarchical Whether the post type is hierarchical (e.g. page). Default false.
+ * @type bool $exclude_from_search Whether to exclude posts with this post type from front end search
+ * results. Default is the opposite value of $public.
+ * @type bool $publicly_queryable Whether queries can be performed on the front end for the post type
+ * as part of {@see parse_request()}. Endpoints would include:
+ * * ?post_type={post_type_key}
+ * * ?{post_type_key}={single_post_slug}
+ * * ?{post_type_query_var}={single_post_slug}
+ * If not set, the default is inherited from $public.
+ * @type bool $show_ui Whether to generate a default UI for managing this post type in the
+ * admin. Default is value of $public.
+ * @type bool $show_in_menu Where to show the post type in the admin menu. To work, $show_ui
+ * must be true. If true, the post type is shown in its own top level
+ * menu. If false, no menu is shown. If a string of an existing top
+ * level menu (eg. 'tools.php' or 'edit.php?post_type=page'), the post
+ * type will be placed as a sub-menu of that.
+ * Default is value of $show_ui.
+ * @type bool $show_in_nav_menus Makes this post type available for selection in navigation menus.
+ * Default is value $public.
+ * @type bool $show_in_admin_bar Makes this post type available via the admin bar. Default is value
+ * of $show_in_menu.
+ * @type int $menu_position The position in the menu order the post type should appear. To work,
+ * $show_in_menu must be true. Default null (at the bottom).
+ * @type string $menu_icon The url to the icon to be used for this menu. Pass a base64-encoded
+ * SVG using a data URI, which will be colored to match the color scheme
+ * -- this should begin with 'data:image/svg+xml;base64,'. Pass the name
+ * of a Dashicons helper class to use a font icon, e.g.
+ * 'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty
+ * so an icon can be added via CSS. Defaults to use the posts icon.
+ * @type string $capability_type The string to use to build the read, edit, and delete capabilities.
+ * May be passed as an array to allow for alternative plurals when using
+ * this argument as a base to construct the capabilities, e.g.
+ * array('story', 'stories'). Default 'post'.
+ * @type array $capabilities Array of capabilities for this post type. $capability_type is used
+ * as a base to construct capabilities by default.
+ * {@see get_post_type_capabilities()}.
+ * @type bool $map_meta_cap Whether to use the internal default meta capability handling.
+ * Default false.
+ * @type array $supports An alias for calling {@see add_post_type_support()} directly.
+ * Defaults to array containing 'title' & 'editor'.
+ * @type callback $register_meta_box_cb Provide a callback function that sets up the meta boxes for the
+ * edit form. Do remove_meta_box() and add_meta_box() calls in the
+ * callback. Default null.
+ * @type array $taxonomies An array of taxonomy identifiers that will be registered for the
+ * post type. Taxonomies can be registered later with
+ * {@see register_taxonomy()} or {@see register_taxonomy_for_object_type()}.
+ * Default empty array.
+ * @type bool|string $has_archive Whether there should be post type archives, or if a string, the
+ * archive slug to use. Will generate the proper rewrite rules if
+ * $rewrite is enabled. Default false.
+ * @type bool|array $rewrite {
+ * Triggers the handling of rewrites for this post type. To prevent rewrite, set to false.
+ * Defaults to true, using $post_type as slug. To specify rewrite rules, an array can be
+ * passed with any of these keys:
+ *
+ * @type string $slug Customize the permastruct slug. Defaults to $post_type key.
+ * @type bool $with_front Whether the permastruct should be prepended with WP_Rewrite::$front.
+ * Default true.
+ * @type bool $feeds Whether the feed permastruct should be built for this post type.
+ * Default is value of $has_archive.
+ * @type bool $pages Whether the permastruct should provide for pagination. Default true.
+ * @type const $ep_mask Endpoint mask to assign. If not specified and permalink_epmask is set,
+ * inherits from $permalink_epmask. If not specified and permalink_epmask
+ * is not set, defaults to EP_PERMALINK.
+ * }
+ * @type string|bool $query_var Sets the query_var key for this post type. Defaults to $post_type
+ * key. If false, a post type cannot be loaded at
+ * ?{query_var}={post_slug}. If specified as a string, the query
+ * ?{query_var_string}={post_slug} will be valid.
+ * @type bool $can_export Whether to allow this post type to be exported. Default true.
+ * @type bool $delete_with_user Whether to delete posts of this type when deleting a user. If true,
+ * posts of this type belonging to the user will be moved to trash
+ * when then user is deleted. If false, posts of this type belonging
+ * to the user will *not* be trashed or deleted. If not set (the default),
+ * posts are trashed if post_type_supports('author'). Otherwise posts
+ * are not trashed or deleted. Default null.
+ * @type bool $_builtin FOR INTERNAL USE ONLY! True if this post type is a native or
+ * "built-in" post_type. Default false.
+ * @type string $_edit_link FOR INTERNAL USE ONLY! URL segment to use for edit link of
+ * this post type. Default 'post.php?post=%d'.
+ * }
+ * @return object|WP_Error The registered post type object, or an error object.
+ */
+function register_post_type( $post_type, $args = array() ) {
+ global $wp_post_types, $wp_rewrite, $wp;
+
+ if ( ! is_array( $wp_post_types ) )
+ $wp_post_types = array();
+
+ // Args prefixed with an underscore are reserved for internal use.
+ $defaults = array(
+ 'labels' => array(),
+ 'description' => '',
+ 'public' => false,
+ 'hierarchical' => false,
+ 'exclude_from_search' => null,
+ 'publicly_queryable' => null,
+ 'show_ui' => null,
+ 'show_in_menu' => null,
+ 'show_in_nav_menus' => null,
+ 'show_in_admin_bar' => null,
+ 'menu_position' => null,
+ 'menu_icon' => null,
+ 'capability_type' => 'post',
+ 'capabilities' => array(),
+ 'map_meta_cap' => null,
+ 'supports' => array(),
+ 'register_meta_box_cb' => null,
+ 'taxonomies' => array(),
+ 'has_archive' => false,
+ 'rewrite' => true,
+ 'query_var' => true,
+ 'can_export' => true,
+ 'delete_with_user' => null,
+ '_builtin' => false,
+ '_edit_link' => 'post.php?post=%d',
+ );
+ $args = wp_parse_args( $args, $defaults );
+ $args = (object) $args;
+
+ $post_type = sanitize_key( $post_type );
+ $args->name = $post_type;
+
+ if ( strlen( $post_type ) > 20 ) {
+ _doing_it_wrong( __FUNCTION__, __( 'Post types cannot exceed 20 characters in length' ), '4.0' );
+ return new WP_Error( 'post_type_too_long', __( 'Post types cannot exceed 20 characters in length' ) );
+ }
+
+ // If not set, default to the setting for public.
+ if ( null === $args->publicly_queryable )
+ $args->publicly_queryable = $args->public;
+
+ // If not set, default to the setting for public.
+ if ( null === $args->show_ui )
+ $args->show_ui = $args->public;
+
+ // If not set, default to the setting for show_ui.
+ if ( null === $args->show_in_menu || ! $args->show_ui )
+ $args->show_in_menu = $args->show_ui;
+
+ // If not set, default to the whether the full UI is shown.
+ if ( null === $args->show_in_admin_bar )
+ $args->show_in_admin_bar = true === $args->show_in_menu;
+
+ // If not set, default to the setting for public.
+ if ( null === $args->show_in_nav_menus )
+ $args->show_in_nav_menus = $args->public;
+
+ // If not set, default to true if not public, false if public.
+ if ( null === $args->exclude_from_search )
+ $args->exclude_from_search = !$args->public;
+
+ // Back compat with quirky handling in version 3.0. #14122.
+ if ( empty( $args->capabilities ) && null === $args->map_meta_cap && in_array( $args->capability_type, array( 'post', 'page' ) ) )
+ $args->map_meta_cap = true;
+
+ // If not set, default to false.
+ if ( null === $args->map_meta_cap )
+ $args->map_meta_cap = false;
+
+ $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 );
+ } elseif ( false !== $args->supports ) {
+ // 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;
+ else
+ $args->query_var = sanitize_title_with_dashes( $args->query_var );
+ $wp->add_query_var( $args->query_var );
+ }
+
+ if ( false !== $args->rewrite && ( is_admin() || '' != 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 ( ! isset( $args->rewrite['ep_mask'] ) ) {
+ if ( isset( $args->permalink_epmask ) )
+ $args->rewrite['ep_mask'] = $args->permalink_epmask;
+ else
+ $args->rewrite['ep_mask'] = EP_PERMALINK;
+ }
+
+ if ( $args->hierarchical )
+ add_rewrite_tag( "%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&pagename=" );
+ else
+ 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;
+
+ add_rewrite_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
+ if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
+ $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
+ add_rewrite_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
+ add_rewrite_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
+ }
+ if ( $args->rewrite['pages'] )
+ add_rewrite_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
+ }
+
+ $permastruct_args = $args->rewrite;
+ $permastruct_args['feed'] = $permastruct_args['feeds'];
+ add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $permastruct_args );
+ }
+
+ // Register the post type meta box if a custom callback was specified.
+ 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 );
+ }
+
+ /**
+ * Fires after a post type is registered.
+ *
+ * @since 3.3.0
+ *
+ * @param string $post_type Post type.
+ * @param object $args Arguments used to register the post type.
+ */
+ do_action( 'registered_post_type', $post_type, $args );
+
+ return $args;
+}
+
+/**
+ * Build 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 edited.
+ *
+ * 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).
+ *
+ * @since 3.0.0
+ *
+ * @see register_post_type()
+ * @see map_meta_cap()
+ *
+ * @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 );
+
+ // Post creation capability simply maps to edit_posts by default:
+ if ( ! isset( $capabilities['create_posts'] ) )
+ $capabilities['create_posts'] = $capabilities['edit_posts'];
+
+ // Remember meta capabilities for future reference.
+ if ( $args->map_meta_cap )
+ _post_type_meta_capabilities( $capabilities );
+
+ return (object) $capabilities;
+}
+
+/**
+ * Store or return a list of post type meta caps for map_meta_cap().
+ *
+ * @since 3.1.0
+ * @access private
+ *
+ * @param null|array $capabilities Post type meta capabilities.
+ */
+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;
+ }
+}
+
+/**
+ * Build 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 overridden
+ * 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 gettext context
+ * {@see http://codex.wordpress.org/I18n_for_WordPress_Developers#Disambiguation_by_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:'.
+ * - all_items - String for the submenu. Default is All Posts/All Pages.
+ * - menu_name - Default is the same as <code>name</code>.
+ *
+ * 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
+ * @access private
+ *
+ * @param object $post_type_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:') ),
+ 'all_items' => array( __( 'All Posts' ), __( 'All Pages' ) )
+ );
+ $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
+
+ $labels = _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults );
+
+ $post_type = $post_type_object->name;
+
+ /**
+ * Filter the labels of a specific post type.
+ *
+ * The dynamic portion of the hook name, $post_type, refers to
+ * the post type slug.
+ *
+ * @since 3.5.0
+ *
+ * @see get_post_type_labels() for the full list of labels.
+ *
+ * @param array $labels Array of labels for the given post type.
+ */
+ return apply_filters( "post_type_labels_{$post_type}", $labels );
+}
+
+/**
+ * Build an object with custom-something object (post type, taxonomy) labels
+ * out of a custom-something object
+ *
+ * @since 3.0.0
+ * @access private
+ *
+ * @param object $object A custom-something object.
+ * @param array $nohier_vs_hier_defaults Hierarchical vs non-hierarchical default labels.
+ */
+function _get_custom_object_labels( $object, $nohier_vs_hier_defaults ) {
+ $object->labels = (array) $object->labels;
+
+ 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['name_admin_bar'] ) )
+ $object->labels['name_admin_bar'] = isset( $object->labels['singular_name'] ) ? $object->labels['singular_name'] : $object->name;
+
+ if ( !isset( $object->labels['menu_name'] ) && isset( $object->labels['name'] ) )
+ $object->labels['menu_name'] = $object->labels['name'];
+
+ if ( !isset( $object->labels['all_items'] ) && isset( $object->labels['menu_name'] ) )
+ $object->labels['all_items'] = $object->labels['menu_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;
+}
+
+/**
+ * Add 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 );
+ // Sub-menus 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->all_items, $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 core features are directly associated with a functional area of the edit
+ * screen, such as the editor or a meta box. Features include: 'title', 'editor',
+ * 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', 'page-attributes',
+ * 'thumbnail', 'custom-fields', and 'post-formats'.
+ *
+ * Additionally, the 'revisions' feature dictates whether the post type will
+ * store revisions, and the 'comments' feature dictates 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, accepts 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][$feature] ) )
+ unset( $_wp_post_type_features[$post_type][$feature] );
+}
+
+/**
+ * Get all the post type features
+ *
+ * @since 3.4.0
+ *
+ * @param string $post_type The post type.
+ * @return array Post type supports list.
+ */
+function get_all_post_type_supports( $post_type ) {
+ global $_wp_post_type_features;
+
+ if ( isset( $_wp_post_type_features[$post_type] ) )
+ return $_wp_post_type_features[$post_type];
+
+ return array();
+}
+
+/**
+ * Check 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 bool Whether the post type supports the given feature.
+ */
+function post_type_supports( $post_type, $feature ) {
+ global $_wp_post_type_features;
+
+ return ( isset( $_wp_post_type_features[$post_type][$feature] ) );
+}
+
+/**
+ * Update the post type for the post ID.
+ *
+ * The page or post cache will be cleaned for the post ID.
+ *
+ * @since 2.5.0
+ *
+ * @global wpdb $wpdb WordPress database access abstraction object.
+ *
+ * @param int $post_id Optional. Post ID to change post type. Default 0.
+ * @param string $post_type Optional. Post type. Accepts 'post' or 'page' to
+ * name a few. Default 'post'.
+ * @return int Amount of rows changed. Should be 1 for success and 0 for failure.
+ */
+function set_post_type( $post_id = 0, $post_type = 'post' ) {
+ global $wpdb;
+
+ $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db');
+ $return = $wpdb->update( $wpdb->posts, array('post_type' => $post_type), array('ID' => $post_id) );
+
+ clean_post_cache( $post_id );
+
+ return $return;
+}
+
+/**
+ * Retrieve list of latest posts or posts matching criteria.
+ *
+ * The defaults are as follows:
+ *
+ * @since 1.2.0
+ *
+ * @see WP_Query::parse_query()
+ *
+ * @param array $args {
+ * Optional. Arguments to retrieve posts. {@see WP_Query::parse_query()} for more
+ * available arguments.
+ *
+ * @type int $numberposts Total number of posts to retrieve. Is an alias of $posts_per_page
+ * in WP_Query. Accepts 1+ and -1 for all. Default 5.
+ * @type int $offset The number of posts to offset before retrieval. Default 0.
+ * @type int|string $category Category ID or comma-separated list of IDs (this or any children).
+ * Is an alias of $cat in WP_Query. Default 0.
+ * @type string $orderby Which field to order posts by. Accepts post fields. Default 'date'.
+ * @type array $include An array of post IDs to retrieve, sticky posts will be included.
+ * Is an alias of $post__in in WP_Query. Default empty array.
+ * @type array $exclude An array of post IDs not to retrieve. Default empty array.
+ * @type string $meta_key Custom field key. Default empty.
+ * @type mixed $meta_value Custom field value. Default empty string.
+ * @type string $post_type Post type. Default 'post'.
+ * @type bool $suppress_filters Whether to suppress filters. Default true.
+ * }
+ * @return array List of posts.
+ */
+function get_posts( $args = null ) {
+ $defaults = array(
+ 'numberposts' => 5, 'offset' => 0,
+ 'category' => 0, 'orderby' => 'date',
+ 'order' => 'DESC', 'include' => array(),
+ 'exclude' => array(), 'meta_key' => '',
+ 'meta_value' =>'', 'post_type' => 'post',
+ 'suppress_filters' => true
+ );
+
+ $r = wp_parse_args( $args, $defaults );
+ if ( empty( $r['post_status'] ) )
+ $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
+ if ( ! empty($r['numberposts']) && empty($r['posts_per_page']) )
+ $r['posts_per_page'] = $r['numberposts'];
+ if ( ! empty($r['category']) )
+ $r['cat'] = $r['category'];
+ if ( ! empty($r['include']) ) {
+ $incposts = wp_parse_id_list( $r['include'] );
+ $r['posts_per_page'] = count($incposts); // only the number of posts included
+ $r['post__in'] = $incposts;
+ } elseif ( ! empty($r['exclude']) )
+ $r['post__not_in'] = wp_parse_id_list( $r['exclude'] );
+
+ $r['ignore_sticky_posts'] = true;
+ $r['no_found_rows'] = true;
+
+ $get_posts = new WP_Query;
+ return $get_posts->query($r);
+
+}
+
+//
+// Post meta functions
+//
+
+/**
+ * Add meta data field to a post.
+ *
+ * Post meta data is called "Custom Fields" on the Administration Screen.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param bool $unique Optional. Whether the same key should not be added.
+ * Default false.
+ * @return int|bool Meta ID on success, false on failure.
+ */
+function add_post_meta( $post_id, $meta_key, $meta_value, $unique = false ) {
+ // Make sure meta is added to the post, not a revision.
+ if ( $the_post = wp_is_post_revision($post_id) )
+ $post_id = $the_post;
+
+ return add_metadata('post', $post_id, $meta_key, $meta_value, $unique);
+}
+
+/**
+ * Remove metadata matching criteria from a post.
+ *
+ * You can match based on the key, or key and value. Removing based on key and
+ * value, will keep from removing duplicate metadata with the same key. It also
+ * allows removing all metadata matching key, if needed.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Optional. Metadata value. Must be serializable if
+ * non-scalar. Default empty.
+ * @return bool True on success, false on failure.
+ */
+function delete_post_meta( $post_id, $meta_key, $meta_value = '' ) {
+ // Make sure meta is added to the post, not a revision.
+ if ( $the_post = wp_is_post_revision($post_id) )
+ $post_id = $the_post;
+
+ return delete_metadata('post', $post_id, $meta_key, $meta_value);
+}
+
+/**
+ * Retrieve post meta field for a post.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $key Optional. The meta key to retrieve. By default, returns
+ * data for all keys. Default empty.
+ * @param bool $single Optional. Whether to return a single value. Default false.
+ * @return mixed Will be an array if $single is false. Will be value of meta data
+ * field if $single is true.
+ */
+function get_post_meta( $post_id, $key = '', $single = false ) {
+ return get_metadata('post', $post_id, $key, $single);
+}
+
+/**
+ * Update post meta field based on post ID.
+ *
+ * Use the $prev_value parameter to differentiate between meta fields with the
+ * same key and post ID.
+ *
+ * If the meta field for the post does not exist, it will be added.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $meta_key Metadata key.
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param mixed $prev_value Optional. Previous value to check before removing.
+ * Default empty.
+ * @return int|bool Meta ID if the key didn't exist, true on successful update,
+ * false on failure.
+ */
+function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) {
+ // Make sure meta is added to the post, not a revision.
+ if ( $the_post = wp_is_post_revision($post_id) )
+ $post_id = $the_post;
+
+ return update_metadata('post', $post_id, $meta_key, $meta_value, $prev_value);
+}
+
+/**
+ * Delete everything from post meta matching meta key.
+ *
+ * @since 2.3.0
+ *
+ * @param string $post_meta_key Key to search for when deleting.
+ * @return bool Whether the post meta key was deleted from the database.
+ */
+function delete_post_meta_by_key( $post_meta_key ) {
+ return delete_metadata( 'post', null, $post_meta_key, '', true );
+}
+
+/**
+ * Retrieve post meta fields, based on post ID.
+ *
+ * The post meta fields are retrieved from the cache where possible,
+ * so the function is optimized to be called more than once.
+ *
+ * @since 1.2.0
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return array Post meta for the given post.
+ */
+function get_post_custom( $post_id = 0 ) {
+ $post_id = absint( $post_id );
+ if ( ! $post_id )
+ $post_id = get_the_ID();
+
+ return get_post_meta( $post_id );
+}
+
+/**
+ * Retrieve meta field names for a post.
+ *
+ * If there are no meta fields, then nothing (null) will be returned.
+ *
+ * @since 1.2.0
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return array|null Either array of the keys, or null if keys could not be
+ * retrieved.
+ */
+function get_post_custom_keys( $post_id = 0 ) {
+ $custom = get_post_custom( $post_id );
+
+ if ( !is_array($custom) )
+ return;
+
+ if ( $keys = array_keys($custom) )
+ return $keys;
+}
+
+/**
+ * Retrieve values for a custom post field.
+ *
+ * The parameters must not be considered optional. All of the post meta fields
+ * will be retrieved and only the meta field key values returned.
+ *
+ * @since 1.2.0
+ *
+ * @param string $key Optional. Meta field key. Default empty.
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return array Meta field values.
+ */
+function get_post_custom_values( $key = '', $post_id = 0 ) {
+ if ( !$key )
+ return null;
+
+ $custom = get_post_custom($post_id);
+
+ return isset($custom[$key]) ? $custom[$key] : null;
+}
+
+/**
+ * Check if post is sticky.
+ *
+ * Sticky posts should remain at the top of The Loop. If the post ID is not
+ * given, then The Loop ID for the current post will be used.
+ *
+ * @since 2.7.0
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return bool Whether post is sticky.
+ */
+function is_sticky( $post_id = 0 ) {
+ $post_id = absint( $post_id );
+
+ if ( ! $post_id )
+ $post_id = get_the_ID();
+
+ $stickies = get_option( 'sticky_posts' );
+
+ if ( ! is_array( $stickies ) )
+ return false;
+
+ if ( in_array( $post_id, $stickies ) )
+ return true;
+
+ return false;
+}
+
+/**
+ * Sanitize every post field.
+ *
+ * If the context is 'raw', then the post object or array will get minimal
+ * sanitization of the integer fields.
+ *
+ * @since 2.3.0
+ *
+ * @see sanitize_post_field()
+ *
+ * @param object|WP_Post|array $post The Post Object or Array
+ * @param string $context Optional. How to sanitize post fields.
+ * Accepts 'raw', 'edit', 'db', or 'display'.
+ * Default 'display'.
+ * @return object|WP_Post|array The now sanitized Post Object or Array (will be the
+ * same type as $post).
+ */
+function sanitize_post( $post, $context = 'display' ) {
+ if ( is_object($post) ) {
+ // Check if post already filtered for this context.
+ if ( isset($post->filter) && $context == $post->filter )
+ return $post;
+ if ( !isset($post->ID) )
+ $post->ID = 0;
+ foreach ( array_keys(get_object_vars($post)) as $field )
+ $post->$field = sanitize_post_field($field, $post->$field, $post->ID, $context);
+ $post->filter = $context;
+ } else {
+ // Check if post already filtered for this context.
+ if ( isset($post['filter']) && $context == $post['filter'] )
+ return $post;
+ if ( !isset($post['ID']) )
+ $post['ID'] = 0;
+ foreach ( array_keys($post) as $field )
+ $post[$field] = sanitize_post_field($field, $post[$field], $post['ID'], $context);
+ $post['filter'] = $context;
+ }
+ return $post;
+}
+
+/**
+ * Sanitize post field based on context.
+ *
+ * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and
+ * 'js'. The 'display' context is used by default. 'attribute' and 'js' contexts
+ * are treated like 'display' when calling filters.
+ *
+ * @since 2.3.0
+ *
+ * @param string $field The Post Object field name.
+ * @param mixed $value The Post Object value.
+ * @param int $post_id Post ID.
+ * @param string $context How to sanitize post fields. Looks for 'raw', 'edit',
+ * 'db', 'display', 'attribute' and 'js'.
+ * @return mixed Sanitized value.
+ */
+function sanitize_post_field($field, $value, $post_id, $context) {
+ $int_fields = array('ID', 'post_parent', 'menu_order');
+ if ( in_array($field, $int_fields) )
+ $value = (int) $value;
+
+ // Fields which contain arrays of integers.
+ $array_int_fields = array( 'ancestors' );
+ if ( in_array($field, $array_int_fields) ) {
+ $value = array_map( 'absint', $value);
+ return $value;
+ }