status_taxonomy)) register_taxonomy( $this->status_taxonomy, 'post', array('hierarchical' => false, 'update_count_callback' => '_update_post_term_count', 'label' => false, 'query_var' => false, 'rewrite' => false) ); // Hooks to add "status" column to Edit Posts page // BUT, only add it if not being filtered by post_status if(!(isset($_GET['post_status'])) && !(isset($_POST['post_status']))) { add_filter('manage_posts_columns', array('custom_status', '_filter_manage_posts_columns')); add_action('manage_posts_custom_column', array('custom_status', '_filter_manage_posts_custom_column')); } // Add actions and filters for the Edit/Manage Posts page add_action('load-edit.php', array(&$this, 'load_edit_hooks')); } // END: __construct() /** * Hooks to make modifications to the Manage/Edit Posts */ function load_edit_hooks() { // Add custom stati to Edit/Manage Posts add_action('admin_notices',array(&$this, 'enable_custom_status_filters')); // Modify the posts_where query to include custom stati add_filter('posts_where', array(&$this, 'custom_status_where_filter')); } // END: load_edit_hooks() /** * Adds custom stati to the $post_stati array. * This is used to generate the list of statuses on the Edit/Manage Posts page. * */ function enable_custom_status_filters() { // This is the array WP uses to store custom stati (really? stati?) // The status list at the top of the Manage/Edit Posts page is generated using this array global $post_stati; if(is_array($post_stati)) { // @ TODO Don't return statuses that are empty (i.e. no posts) // Get a list of ALL the custom statuses $custom_statuses = $this->get_custom_statuses(); // Alright, now append them to the $post_stati array foreach($custom_statuses as $status) { if(!$this->is_restricted_status($status->slug)) { $slug = $status->slug; $post_stati[$slug] = array( $status->name, $status->description, array( $status->name.' (%s)', $status->name.' (%s)' ) ); } } } } // END: enable_custom_status_filters() /** * Edits the WHERE clause for the the get_post query. * This is used to show all the posts with custom statuses. * Why? Because WordPress automatically hides anything without an allowed status (e.g. "publish", "draft",, etc.) */ function custom_status_where_filter($where){ global $wpdb; if(is_admin()) { if(!(isset($_GET['post_status'])) && !(isset($_POST['post_status']))) { $custom_statuses = $this->get_custom_statuses(); foreach($custom_statuses as $status) { $where .= " OR ".$wpdb->posts.".post_status = '".$status->slug."'"; } } else { // Okay, we're filtering by statuses $status = $_GET['post_status']; // if not one of inbuilt custom statuses, delete query where AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'future' OR wp_posts.post_status = 'draft' OR wp_posts.post_status = 'pending' OR wp_posts.post_status = 'private') // append status to where if(is_term($status, $this->status_taxonomy)) { $where = "AND ".$wpdb->posts.".post_type = 'post' AND (".$wpdb->posts.".post_status = '".$status."')"; } } } return $where; } // END: custom_status_where_filter() /** * Adds a new custom status as a term in the wp_terms table. * Basically a wrapper for the wp_insert_term class. * * The arguments decide how the term is handled based on the $args parameter. * The following is a list of the available overrides and the defaults. * * 'description'. There is no default. If exists, will be added to the database * along with the term. Expected to be a string. * * 'slug'. Expected to be a string. There is no default. * * @param int|string $term The status to add or update * @param array|string $args Change the values of the inserted term * @return array|WP_Error The Term ID and Term Taxonomy ID * */ function add_custom_status ( $term, $args = array() ) { // $args = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); return wp_insert_term( $term, $this->status_taxonomy, $args ); } // END: add_custom_status /** * * Basically a wrapper for the wp_update_term function */ function update_custom_status ( $status_id, $args = array() ) { // Reassign posts to new status slug $old_status = get_term($status_id, $this->status_taxonomy)->slug; // if($old_status != 'draft' || $old_status != 'pending' || $old_status != 'publish') { if(!$this->is_restricted_status($old_status)) { // If new status not indicated, set to "draft", else get slug for new status $new_status = $args['slug']; $this->reassign_post_status( $old_status, $new_status ); } return wp_update_term( $status_id, $this->status_taxonomy, $args ); } // END: update_custom_status /** * Deletes a custom status from the wp_terms table. * * Partly a wrapper for the wp_delete_term function. * BUT, also reassigns posts that currently have the deleted status assigned. */ function delete_custom_status ( $status_id, $args = array(), $reassign = '' ) { // Reassign posts to alternate status // Get slug for the old status $old_status = get_term($status_id, $this->status_taxonomy)->slug; if(!$this->is_restricted_status($old_status)) { // If new status not indicated, set to "draft", else get slug for new status if(!$reassign) $new_status = 'draft'; else $new_status = get_term($reassign, $this->status_taxonomy)->slug; $this->reassign_post_status( $old_status, $new_status ); } return wp_delete_term( $status_id, $this->status_taxonomy, $args ); } // END: delete_custom_status function get_custom_statuses ($statuses = '', $args ='' ) { // @TODO: implement $args, to allow for pagination, etc. if(!$statuses) { // return all stati $statuses = get_terms( $this->status_taxonomy, array( 'get' => 'all' ) ); } else if (!is_array($statuses)) { // return a single status } else { // return multiple stati } return $statuses; } function reassign_post_status( $old_status, $new_status = 'draft' ) { global $wpdb; // Make the database call $result = $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'post_status' => $old_status ), array( '%s' )); } /** * Insert new column header for post status * * @param array $post_columns **/ function _filter_manage_posts_columns($posts_columns) { $result = array(); foreach ($posts_columns as $key => $value) { if ($key == 'author') { $result[$key] = $value; $result['status'] = __('Status', 'edit-flow'); } else $result[$key] = $value; } return $result; } // END: _filter_manage_posts_columns /** * Adds a Post's status to its row on the Edit page * * @param string $column_name **/ function _filter_manage_posts_custom_column($column_name) { if ($column_name == 'status') { global $post, $custom_status; echo get_status_name('slug' , $post->post_status); } } /** * Determines whether the slug indicated belongs to a restricted status or not * @param string Slug of the status * @return bool True if restricted, false if not */ function is_restricted_status ( $slug ) { switch($slug) { case 'publish': case 'draft': case 'private': case 'future': case 'pending': case 'new': case 'inherit': $return = true; break; default: $return = false; break; } return $return; } function admin_page ( ) { global $wpdb, $edit_flow; // @TODO JS form validation $nonce_fail_msg = __('There\'s something fishy going on! We don\'t like this type of nonce-sense. Hmph.'); $msg_class = 'updated'; $action = ($_POST['action']) ? $wpdb->escape($_POST['action']) : $wpdb->escape($_GET['action']); switch($action) { case 'add': // Verfiy nonce if(wp_verify_nonce($_POST['custom-status-add-nonce'], 'custom-status-add-nonce')) { // @TODO Make sure content is secure, ie: check inputs so nothing nasty gets through // Check if name field was filled in if(!($name = $_POST['status_name'])) { $error_details = __('Please enter a name for the status'); break; } // Check to make sure the name is not restricted if($this->is_restricted_status(strtolower(trim($name)))) { $error_details = __('That status name is restricted. Please use another name.'); break; } // Check to make sure the status doesn't already exist if(is_term(sanitize_title($_POST['status_name']))) { $error_details = __('That status already exists. Please use another name.'); break; } $args = array('description' => $_POST['status_description'], 'slug' => sanitize_title($_POST['status_name']) ); // Try to add the status $return = $this->add_custom_status($name, $args); if(!is_wp_error($return)) { $msg = __('Status successfully added.'); } else { $error_details = __('Could not add the status: ') . $return->get_error_message(); } } else { $error_details = $nonce_fail_msg; } break; case 'edit': $term_id = (int) $_GET['term_id']; if($term_id && $the_status = get_term($term_id, $this->status_taxonomy)) { // Stop users from editing restricted statuses if($this->is_restricted_status($the_status->slug)) { $error_details = __('That status is restricted and cannot be edited. You are welcome to delete it, however.'); $update = false; } else { $update = true; $edit_status = $the_status; } } break; case 'update': // @TODO if updated status is the same as the default_status, update the default // Verfiy nonce if(wp_verify_nonce($_POST['custom-status-add-nonce'], 'custom-status-add-nonce')) { $term_id = (int) $_POST['term_id']; $status_name = $_POST['status_name']; $status_description = $_POST['status_description']; $status_slug = sanitize_title($_POST['status_name']); // Check to make sure the status doesn't already exist if(is_term(sanitize_title($_POST['status_name'])) && (get_term($term_id, $this->status_taxonomy)->slug != $status_slug)) { $error_details = __('That status already exists. Please use another name.'); break; } // get status_name & status_description $args = array( 'name' => $status_name, 'description' => $status_description, 'slug' => $status_slug ); $return = $this->update_custom_status($term_id, $args); if(!is_wp_error($return)) { $msg = __('Status successfully updated.'); } else { $error_details = __('Could not update the status: ') . $return->get_error_message(); } } else { $error_details = $nonce_fail_msg; } break; case 'delete': // @TODO if updated status is the same as the default_status, update the default // Verfiy nonce if(wp_verify_nonce($_GET['_wpnonce'], 'custom-status-delete-nonce')) { $term_id = (int) $_GET['term_id']; // Check to make sure the status doesn't already exist if(!is_term($term_id, $this->status_taxonomy)) { $error_details = __('That status does not exist. Try again?'); break; } $return = $this->delete_custom_status($term_id); if(!is_wp_error($return)) { $msg = __('Status successfully deleted.'); } else { $error_details = __('Could not delete the status: ') . $return->get_error_message(); } } else { $error_details = $nonce_fail_msg; } break; default: $update = false; break; } if($error_details) { $msg = __('There was an error with your request. '); $msg .= '

'.$error_details.''; $msg_class = 'error'; } $statuses = $this->get_custom_statuses(); ?>

get_plugin_option('custom_statuses_enabled')) : ?>

enable them.') ?>

slug); $status_link = get_custom_status_filter_link($status->slug); $edit_link = get_custom_status_edit_link($status->term_id); $delete_link = EDIT_FLOW_CUSTOM_STATUS_PAGE.'&action=delete&term_id='.$status->term_id.'&_wpnonce='.$delete_nonce; ?>
name ?>
description ?>

Note:
Deleting a status does not delete the posts assigned that status. Instead, the posts will be set to the default status: Draft') ?>.

name; } // END: get_status_name() function get_custom_status_filter_link ( $slug ) { return 'edit.php?post_status='.$slug; } function get_custom_status_edit_link( $id ) { return EDIT_FLOW_CUSTOM_STATUS_PAGE.'&action=edit&term_id='.$id; } function get_custom_status_post_count ( $status ) { global $wpdb; if(is_int($status)) { $status = get_term_by('term_id', $status, 'post_status')->slug; } $query = $wpdb->prepare("SELECT count(ID) FROM $wpdb->posts WHERE post_status = %s", $status); return $wpdb->get_var($query); } ?>