| 1 |
<?php |
| 2 |
/** |
| 3 |
* class EF_Custom_Status |
| 4 |
* Custom statuses make it simple to define the different stages in your publishing workflow. |
| 5 |
* |
| 6 |
* @todo for v0.7 |
| 7 |
* - Improve the copy |
| 8 |
* - Thoroughly test what happens when the default post statuses 'Draft' and 'Pending Review' no longer exist |
| 9 |
* - Ensure all of the form processing uses our messages functionality |
| 10 |
*/ |
| 11 |
if ( !class_exists( 'EF_Custom_Status' ) ) { |
| 12 |
|
| 13 |
class EF_Custom_Status extends EF_Module { |
| 14 |
|
| 15 |
var $module; |
| 16 |
|
| 17 |
// This is taxonomy name used to store all our custom statuses |
| 18 |
const taxonomy_key = 'post_status'; |
| 19 |
|
| 20 |
/** |
| 21 |
* Register the module with Edit Flow but don't do anything else |
| 22 |
*/ |
| 23 |
function __construct() { |
| 24 |
global $edit_flow; |
| 25 |
|
| 26 |
$this->module_url = $this->get_module_url( __FILE__ ); |
| 27 |
// Register the module with Edit Flow |
| 28 |
$args = array( |
| 29 |
'title' => __( 'Custom Statuses', 'edit-flow' ), |
| 30 |
'short_description' => __( 'Create custom post statuses to define the stages of your workflow.', 'edit-flow' ), |
| 31 |
'extended_description' => __( 'Create your own post statuses to add structure your publishing workflow. You can change existing or add new ones anytime, and drag and drop to change their order.', 'edit-flow' ), |
| 32 |
'module_url' => $this->module_url, |
| 33 |
'img_url' => $this->module_url . 'lib/custom_status_s128.png', |
| 34 |
'slug' => 'custom-status', |
| 35 |
'default_options' => array( |
| 36 |
'enabled' => 'on', |
| 37 |
'default_status' => 'pitch', |
| 38 |
'always_show_dropdown' => 'off', |
| 39 |
'post_types' => array( |
| 40 |
'post' => 'on', |
| 41 |
'page' => 'on', |
| 42 |
), |
| 43 |
), |
| 44 |
'post_type_support' => 'ef_custom_statuses', // This has been plural in all of our docs |
| 45 |
'configure_page_cb' => 'print_configure_view', |
| 46 |
'configure_link_text' => __( 'Edit Statuses', 'edit-flow' ), |
| 47 |
'messages' => array( |
| 48 |
'status-added' => __( 'Post status created.', 'edit-flow' ), |
| 49 |
'status-missing' => __( "Post status doesn't exist.", 'edit-flow' ), |
| 50 |
'default-status-changed' => __( 'Default post status has been changed.', 'edit-flow'), |
| 51 |
'term-updated' => __( "Post status updated.", 'edit-flow' ), |
| 52 |
'status-deleted' => __( 'Post status deleted.', 'edit-flow' ), |
| 53 |
'status-position-updated' => __( "Status order updated.", 'edit-flow' ), |
| 54 |
), |
| 55 |
'autoload' => false, |
| 56 |
'settings_help_tab' => array( |
| 57 |
'id' => 'ef-custom-status-overview', |
| 58 |
'title' => __('Overview', 'edit-flow'), |
| 59 |
'content' => __('<p>Edit Flow’s custom statuses allow you to define the most important stages of your editorial workflow. Out of the box, WordPress only offers “Draft” and “Pending Review” as post states. With custom statuses, you can create your own post states like “In Progress”, “Pitch”, or “Waiting for Edit” and keep or delete the originals. You can also drag and drop statuses to set the best order for your workflow.</p><p>Custom statuses are fully integrated into the rest of Edit Flow and the WordPress admin. On the calendar and story budget, you can filter your view to see only posts of a specific post state. Furthermore, email notifications can be sent to a specific group of users when a post changes state.</p>', 'edit-flow'), |
| 60 |
), |
| 61 |
'settings_help_sidebar' => __( '<p><strong>For more information:</strong></p><p><a href="http://editflow.org/features/custom-statuses/">Custom Status Documentation</a></p><p><a href="http://wordpress.org/tags/edit-flow?forum_id=10">Edit Flow Forum</a></p><p><a href="https://github.com/danielbachhuber/Edit-Flow">Edit Flow on Github</a></p>', 'edit-flow' ), |
| 62 |
); |
| 63 |
$this->module = EditFlow()->register_module( 'custom_status', $args ); |
| 64 |
|
| 65 |
} |
| 66 |
|
| 67 |
/** |
| 68 |
* Initialize the EF_Custom_Status class if the module is active |
| 69 |
*/ |
| 70 |
function init() { |
| 71 |
global $edit_flow; |
| 72 |
|
| 73 |
// Register custom statuses as a taxonomy |
| 74 |
$this->register_custom_statuses(); |
| 75 |
|
| 76 |
// Register our settings |
| 77 |
add_action( 'admin_init', array( $this, 'register_settings' ) ); |
| 78 |
|
| 79 |
// Load CSS and JS resources that we probably need |
| 80 |
add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) ); |
| 81 |
add_action( 'admin_notices', array( $this, 'no_js_notice' ) ); |
| 82 |
add_action( 'admin_print_scripts', array( $this, 'post_admin_header' ) ); |
| 83 |
|
| 84 |
// Methods for handling the actions of creating, making default, and deleting post stati |
| 85 |
add_action( 'admin_init', array( $this, 'handle_add_custom_status' ) ); |
| 86 |
add_action( 'admin_init', array( $this, 'handle_edit_custom_status' ) ); |
| 87 |
add_action( 'admin_init', array( $this, 'handle_make_default_custom_status' ) ); |
| 88 |
add_action( 'admin_init', array( $this, 'handle_delete_custom_status' ) ); |
| 89 |
add_action( 'wp_ajax_update_status_positions', array( $this, 'handle_ajax_update_status_positions' ) ); |
| 90 |
add_action( 'wp_ajax_inline_save_status', array( $this, 'ajax_inline_save_status' ) ); |
| 91 |
|
| 92 |
// Hook to add the status column to Manage Posts |
| 93 |
|
| 94 |
add_filter( 'manage_posts_columns', array( $this, '_filter_manage_posts_columns') ); |
| 95 |
add_action( 'manage_posts_custom_column', array( $this, '_filter_manage_posts_custom_column') ); |
| 96 |
|
| 97 |
// We need these for pages (http://core.trac.wordpress.org/browser/tags/3.3.1/wp-admin/includes/class-wp-posts-list-table.php#L283) |
| 98 |
add_filter( 'manage_pages_columns', array( $this, '_filter_manage_posts_columns' ) ); |
| 99 |
add_action( 'manage_pages_custom_column', array( $this, '_filter_manage_posts_custom_column' ) ); |
| 100 |
|
| 101 |
// These seven-ish methods are hacks for fixing bugs in WordPress core |
| 102 |
add_action( 'admin_init', array( $this, 'check_timestamp_on_publish' ) ); |
| 103 |
add_filter( 'wp_insert_post_data', array( $this, 'fix_custom_status_timestamp' ) ); |
| 104 |
add_action( 'wp_insert_post', array( $this, 'fix_post_name' ), 10, 2 ); |
| 105 |
add_filter( 'editable_slug', array( $this, 'fix_editable_slug' ) ); |
| 106 |
add_filter( 'preview_post_link', array( $this, 'fix_preview_link_part_one' ) ); |
| 107 |
add_filter( 'post_link', array( $this, 'fix_preview_link_part_two' ), 10, 3 ); |
| 108 |
add_filter( 'page_link', array( $this, 'fix_preview_link_part_two' ), 10, 3 ); |
| 109 |
add_filter( 'post_type_link', array( $this, 'fix_preview_link_part_two' ), 10, 3 ); |
| 110 |
add_filter( 'post_row_actions', array( $this, 'fix_post_row_actions' ), 10, 2 ); |
| 111 |
add_filter( 'page_row_actions', array( $this, 'fix_post_row_actions' ), 10, 2 ); |
| 112 |
|
| 113 |
} |
| 114 |
|
| 115 |
/** |
| 116 |
* Create the default set of custom statuses the first time the module is loaded |
| 117 |
* |
| 118 |
* @since 0.7 |
| 119 |
*/ |
| 120 |
function install() { |
| 121 |
|
| 122 |
$default_terms = array( |
| 123 |
array( |
| 124 |
'term' => __( 'Pitch', 'edit-flow' ), |
| 125 |
'args' => array( |
| 126 |
'slug' => 'pitch', |
| 127 |
'description' => __( 'Idea proposed; waiting for acceptance.', 'edit-flow' ), |
| 128 |
'position' => 1, |
| 129 |
), |
| 130 |
), |
| 131 |
array( |
| 132 |
'term' => __( 'Assigned', 'edit-flow' ), |
| 133 |
'args' => array( |
| 134 |
'slug' => 'assigned', |
| 135 |
'description' => __( 'Post idea assigned to writer.', 'edit-flow' ), |
| 136 |
'position' => 2, |
| 137 |
), |
| 138 |
), |
| 139 |
array( |
| 140 |
'term' => __( 'In Progress', 'edit-flow' ), |
| 141 |
'args' => array( |
| 142 |
'slug' => 'in-progress', |
| 143 |
'description' => __( 'Writer is working on the post.', 'edit-flow' ), |
| 144 |
'position' => 3, |
| 145 |
), |
| 146 |
), |
| 147 |
array( |
| 148 |
'term' => __( 'Draft', 'edit-flow' ), |
| 149 |
'args' => array( |
| 150 |
'slug' => 'draft', |
| 151 |
'description' => __( 'Post is a draft; not ready for review or publication.', 'edit-flow' ), |
| 152 |
'position' => 4, |
| 153 |
), |
| 154 |
), |
| 155 |
array( |
| 156 |
'term' => __( 'Pending Review' ), |
| 157 |
'args' => array( |
| 158 |
'slug' => 'pending', |
| 159 |
'description' => __( 'Post needs to be reviewed by an editor.', 'edit-flow' ), |
| 160 |
'position' => 5, |
| 161 |
), |
| 162 |
), |
| 163 |
); |
| 164 |
|
| 165 |
// Okay, now add the default statuses to the db if they don't already exist |
| 166 |
foreach( $default_terms as $term ) { |
| 167 |
if( !term_exists( $term['term'] ) ) |
| 168 |
$this->add_custom_status( $term['term'], $term['args'] ); |
| 169 |
} |
| 170 |
|
| 171 |
} |
| 172 |
|
| 173 |
/** |
| 174 |
* Upgrade our data in case we need to |
| 175 |
* |
| 176 |
* @since 0.7 |
| 177 |
*/ |
| 178 |
function upgrade( $previous_version ) { |
| 179 |
global $edit_flow; |
| 180 |
|
| 181 |
// Upgrade path to v0.7 |
| 182 |
if ( version_compare( $previous_version, '0.7' , '<' ) ) { |
| 183 |
// Migrate dropdown visibility option |
| 184 |
if ( $dropdown_visible = get_option( 'edit_flow_status_dropdown_visible' ) ) |
| 185 |
$dropdown_visible = 'on'; |
| 186 |
else |
| 187 |
$dropdown_visible = 'off'; |
| 188 |
$edit_flow->update_module_option( $this->module->name, 'always_show_dropdown', $dropdown_visible ); |
| 189 |
delete_option( 'edit_flow_status_dropdown_visible' ); |
| 190 |
// Migrate default status option |
| 191 |
if ( $default_status = get_option( 'edit_flow_custom_status_default_status' ) ) |
| 192 |
$edit_flow->update_module_option( $this->module->name, 'default_status', $default_status ); |
| 193 |
delete_option( 'edit_flow_custom_status_default_status' ); |
| 194 |
|
| 195 |
// Technically we've run this code before so we don't want to auto-install new data |
| 196 |
$edit_flow->update_module_option( $this->module->name, 'loaded_once', true ); |
| 197 |
} |
| 198 |
// Upgrade path to v0.7.4 |
| 199 |
if ( version_compare( $previous_version, '0.7.4', '<' ) ) { |
| 200 |
// Custom status descriptions become base64_encoded, instead of maybe json_encoded. |
| 201 |
$this->upgrade_074_term_descriptions( self::taxonomy_key ); |
| 202 |
} |
| 203 |
|
| 204 |
} |
| 205 |
|
| 206 |
/** |
| 207 |
* Makes the call to register_post_status to register the user's custom statuses. |
| 208 |
* Also unregisters draft and pending, in case the user doesn't want them. |
| 209 |
*/ |
| 210 |
function register_custom_statuses() { |
| 211 |
global $wp_post_statuses; |
| 212 |
|
| 213 |
if ( $this->disable_custom_statuses_for_post_type() ) |
| 214 |
return; |
| 215 |
|
| 216 |
// Register new taxonomy so that we can store all our fancy new custom statuses (or is it stati?) |
| 217 |
if ( !taxonomy_exists( self::taxonomy_key ) ) { |
| 218 |
$args = array( 'hierarchical' => false, |
| 219 |
'update_count_callback' => '_update_post_term_count', |
| 220 |
'label' => false, |
| 221 |
'query_var' => false, |
| 222 |
'rewrite' => false, |
| 223 |
'show_ui' => false |
| 224 |
); |
| 225 |
register_taxonomy( self::taxonomy_key, 'post', $args ); |
| 226 |
} |
| 227 |
|
| 228 |
if ( function_exists( 'register_post_status' ) ) { |
| 229 |
// Users can delete draft and pending statuses if they want, so let's get rid of them |
| 230 |
// They'll get re-added if the user hasn't "deleted" them |
| 231 |
unset( $wp_post_statuses[ 'draft' ] ); |
| 232 |
unset( $wp_post_statuses[ 'pending' ] ); |
| 233 |
|
| 234 |
$custom_statuses = $this->get_custom_statuses(); |
| 235 |
|
| 236 |
// Unfortunately, register_post_status() doesn't accept a |
| 237 |
// post type argument, so we have to register the post |
| 238 |
// statuses for all post types. This results in |
| 239 |
// all post statuses for a post type appearing at the top |
| 240 |
// of manage posts if there is a post with the status |
| 241 |
foreach ( $custom_statuses as $status ) { |
| 242 |
register_post_status( $status->slug, array( |
| 243 |
'label' => $status->name |
| 244 |
, 'protected' => true |
| 245 |
, '_builtin' => false |
| 246 |
, 'label_count' => _n_noop( "{$status->name} <span class='count'>(%s)</span>", "{$status->name} <span class='count'>(%s)</span>" ) |
| 247 |
) ); |
| 248 |
} |
| 249 |
} |
| 250 |
} |
| 251 |
|
| 252 |
/** |
| 253 |
* Whether custom post statuses should be disabled for this post type. |
| 254 |
* Used to stop custom statuses from being registered for post types that don't support them. |
| 255 |
* |
| 256 |
* @since 0.7.5 |
| 257 |
* |
| 258 |
* @return bool |
| 259 |
*/ |
| 260 |
function disable_custom_statuses_for_post_type( $post_type = null ) { |
| 261 |
global $pagenow; |
| 262 |
|
| 263 |
// Only allow deregistering on 'edit.php' and 'post.php' |
| 264 |
if ( ! in_array( $pagenow, array( 'edit.php', 'post.php', 'post-new.php' ) ) ) |
| 265 |
return false; |
| 266 |
|
| 267 |
if ( is_null( $post_type ) ) |
| 268 |
$post_type = $this->get_current_post_type(); |
| 269 |
|
| 270 |
if ( $post_type && ! in_array( $post_type, $this->get_post_types_for_module( $this->module ) ) ) |
| 271 |
return true; |
| 272 |
|
| 273 |
return false; |
| 274 |
} |
| 275 |
|
| 276 |
/** |
| 277 |
* Enqueue Javascript resources that we need in the admin: |
| 278 |
* - Primary use of Javascript is to manipulate the post status dropdown on Edit Post and Manage Posts |
| 279 |
* - jQuery Sortable plugin is used for drag and dropping custom statuses |
| 280 |
* - We have other custom code for Quick Edit and JS niceties |
| 281 |
*/ |
| 282 |
function action_admin_enqueue_scripts() { |
| 283 |
global $pagenow; |
| 284 |
|
| 285 |
if ( $this->disable_custom_statuses_for_post_type() ) |
| 286 |
return; |
| 287 |
|
| 288 |
// Load Javascript we need to use on the configuration views (jQuery Sortable and Quick Edit) |
| 289 |
if ( $this->is_whitelisted_settings_view( $this->module->name ) ) { |
| 290 |
wp_enqueue_script( 'jquery-ui-sortable' ); |
| 291 |
wp_enqueue_script( 'edit-flow-custom-status-configure', $this->module_url . 'lib/custom-status-configure.js', array( 'jquery', 'jquery-ui-sortable', 'edit-flow-settings-js' ), EDIT_FLOW_VERSION, true ); |
| 292 |
} |
| 293 |
|
| 294 |
// Custom javascript to modify the post status dropdown where it shows up |
| 295 |
if ( $this->is_whitelisted_page() ) { |
| 296 |
wp_enqueue_script( 'edit_flow-custom_status', $this->module_url . 'lib/custom-status.js', array( 'jquery','post' ), EDIT_FLOW_VERSION, true ); |
| 297 |
wp_enqueue_style( 'edit_flow-custom_status', $this->module_url . 'lib/custom-status.css', false, EDIT_FLOW_VERSION, 'all' ); |
| 298 |
} |
| 299 |
} |
| 300 |
|
| 301 |
/** |
| 302 |
* Displays a notice to users if they have JS disabled |
| 303 |
* Javascript is needed for custom statuses to be fully functional |
| 304 |
*/ |
| 305 |
function no_js_notice() { |
| 306 |
if( $this->is_whitelisted_page() ) : |
| 307 |
?> |
| 308 |
<style type="text/css"> |
| 309 |
/* Hide post status dropdown by default in case of JS issues **/ |
| 310 |
label[for=post_status], |
| 311 |
#post-status-display, |
| 312 |
#post-status-select, |
| 313 |
#publish { |
| 314 |
display: none; |
| 315 |
} |
| 316 |
</style> |
| 317 |
<div class="update-nag hide-if-js"> |
| 318 |
<?php _e( '<strong>Note:</strong> Your browser does not support JavaScript or has JavaScript disabled. You will not be able to access or change the post status.', 'edit-flow' ); ?> |
| 319 |
</div> |
| 320 |
<?php |
| 321 |
endif; |
| 322 |
} |
| 323 |
|
| 324 |
/** |
| 325 |
* Check whether custom status stuff should be loaded on this page |
| 326 |
* |
| 327 |
* @todo migrate this to the base module class |
| 328 |
*/ |
| 329 |
function is_whitelisted_page() { |
| 330 |
global $pagenow; |
| 331 |
|
| 332 |
if ( !in_array( $this->get_current_post_type(), $this->get_post_types_for_module( $this->module ) ) ) |
| 333 |
return false; |
| 334 |
|
| 335 |
$post_type_obj = get_post_type_object( $this->get_current_post_type() ); |
| 336 |
|
| 337 |
if( ! current_user_can( $post_type_obj->cap->edit_posts ) ) |
| 338 |
return false; |
| 339 |
|
| 340 |
// Only add the script to Edit Post and Edit Page pages -- don't want to bog down the rest of the admin with unnecessary javascript |
| 341 |
return in_array( $pagenow, array( 'post.php', 'edit.php', 'post-new.php', 'page.php', 'edit-pages.php', 'page-new.php' ) ); |
| 342 |
} |
| 343 |
|
| 344 |
/** |
| 345 |
* Adds all necessary javascripts to make custom statuses work |
| 346 |
* |
| 347 |
* @todo Support private and future posts on edit.php view |
| 348 |
*/ |
| 349 |
function post_admin_header() { |
| 350 |
global $post, $edit_flow, $pagenow, $current_user; |
| 351 |
|
| 352 |
if ( $this->disable_custom_statuses_for_post_type() ) |
| 353 |
return; |
| 354 |
|
| 355 |
// Get current user |
| 356 |
get_currentuserinfo() ; |
| 357 |
|
| 358 |
// Only add the script to Edit Post and Edit Page pages -- don't want to bog down the rest of the admin with unnecessary javascript |
| 359 |
if ( !empty( $post ) && $this->is_whitelisted_page() ) { |
| 360 |
|
| 361 |
$custom_statuses = $this->get_custom_statuses(); |
| 362 |
|
| 363 |
// Get the status of the current post |
| 364 |
if ( $post->ID == 0 || $post->post_status == 'auto-draft' || $pagenow == 'edit.php' ) { |
| 365 |
// TODO: check to make sure that the default exists |
| 366 |
$selected = $this->get_default_custom_status()->slug; |
| 367 |
|
| 368 |
} else { |
| 369 |
$selected = $post->post_status; |
| 370 |
} |
| 371 |
|
| 372 |
// Get the current post status name |
| 373 |
$selected_name = ''; |
| 374 |
|
| 375 |
foreach ($custom_statuses as $status) { |
| 376 |
if ($status->slug == $selected) { |
| 377 |
$selected_name = $status->name; |
| 378 |
} |
| 379 |
} |
| 380 |
|
| 381 |
$custom_statuses = apply_filters( 'ef_custom_status_list', $custom_statuses, $post ); |
| 382 |
|
| 383 |
// All right, we want to set up the JS var which contains all custom statuses |
| 384 |
$all_statuses = array(); |
| 385 |
|
| 386 |
// The default statuses from WordPress |
| 387 |
$all_statuses[] = array( |
| 388 |
'name' => __( 'Published', 'edit-flow' ), |
| 389 |
'slug' => 'publish', |
| 390 |
'description' => '', |
| 391 |
); |
| 392 |
$all_statuses[] = array( |
| 393 |
'name' => __( 'Privately Published', 'edit-flow' ), |
| 394 |
'slug' => 'private', |
| 395 |
'description' => '', |
| 396 |
); |
| 397 |
$all_statuses[] = array( |
| 398 |
'name' => __( 'Scheduled', 'edit-flow' ), |
| 399 |
'slug' => 'future', |
| 400 |
'description' => '', |
| 401 |
); |
| 402 |
|
| 403 |
// Load the custom statuses |
| 404 |
foreach( $custom_statuses as $status ) { |
| 405 |
$all_statuses[] = array( |
| 406 |
'name' => esc_js( $status->name ), |
| 407 |
'slug' => esc_js( $status->slug ), |
| 408 |
'description' => esc_js( $status->description ), |
| 409 |
); |
| 410 |
} |
| 411 |
|
| 412 |
$always_show_dropdown = ( $this->module->options->always_show_dropdown == 'on' ) ? 1 : 0; |
| 413 |
|
| 414 |
$post_type_obj = get_post_type_object( $this->get_current_post_type() ); |
| 415 |
|
| 416 |
// Now, let's print the JS vars |
| 417 |
?> |
| 418 |
<script type="text/javascript"> |
| 419 |
var custom_statuses = <?php echo json_encode( $all_statuses ); ?>; |
| 420 |
var ef_text_no_change = '<?php echo esc_js( __( "— No Change —" ) ); ?>'; |
| 421 |
var ef_default_custom_status = '<?php echo esc_js( $this->get_default_custom_status()->slug ); ?>'; |
| 422 |
var current_status = '<?php echo esc_js( $selected ); ?>'; |
| 423 |
var current_status_name = '<?php echo esc_js( $selected_name ); ?>'; |
| 424 |
var status_dropdown_visible = <?php echo esc_js( $always_show_dropdown ); ?>; |
| 425 |
var current_user_can_publish_posts = <?php echo current_user_can( $post_type_obj->cap->publish_posts ) ? 1 : 0; ?>; |
| 426 |
var current_user_can_edit_published_posts = <?php echo current_user_can( $post_type_obj->cap->edit_published_posts ) ? 1 : 0; ?>; |
| 427 |
</script> |
| 428 |
|
| 429 |
<?php |
| 430 |
|
| 431 |
} |
| 432 |
|
| 433 |
} |
| 434 |
|
| 435 |
/** |
| 436 |
* Adds a new custom status as a term in the wp_terms table. |
| 437 |
* Basically a wrapper for the wp_insert_term class. |
| 438 |
* |
| 439 |
* The arguments decide how the term is handled based on the $args parameter. |
| 440 |
* The following is a list of the available overrides and the defaults. |
| 441 |
* |
| 442 |
* 'description'. There is no default. If exists, will be added to the database |
| 443 |
* along with the term. Expected to be a string. |
| 444 |
* |
| 445 |
* 'slug'. Expected to be a string. There is no default. |
| 446 |
* |
| 447 |
* @param int|string $term The status to add or update |
| 448 |
* @param array|string $args Change the values of the inserted term |
| 449 |
* @return array|WP_Error $response The Term ID and Term Taxonomy ID |
| 450 |
*/ |
| 451 |
function add_custom_status( $term, $args = array() ) { |
| 452 |
$slug = ( ! empty( $args['slug'] ) ) ? $args['slug'] : sanitize_title( $term ); |
| 453 |
unset( $args['slug'] ); |
| 454 |
$encoded_description = $this->get_encoded_description( $args ); |
| 455 |
$response = wp_insert_term( $term, self::taxonomy_key, array( 'slug' => $slug, 'description' => $encoded_description ) ); |
| 456 |
return $response; |
| 457 |
|
| 458 |
} |
| 459 |
|
| 460 |
/** |
| 461 |
* Update an existing custom status |
| 462 |
* |
| 463 |
* @param int @status_id ID for the status |
| 464 |
* @param array $args Any arguments to be updated |
| 465 |
* @return object $updated_status Newly updated status object |
| 466 |
*/ |
| 467 |
function update_custom_status( $status_id, $args = array() ) { |
| 468 |
global $edit_flow; |
| 469 |
|
| 470 |
$old_status = $this->get_custom_status_by( 'id', $status_id ); |
| 471 |
if ( !$old_status || is_wp_error( $old_status ) ) |
| 472 |
return new WP_Error( 'invalid', __( "Custom status doesn't exist.", 'edit-flow' ) ); |
| 473 |
|
| 474 |
// If the name was changed, we need to change the slug |
| 475 |
if ( isset( $args['name'] ) && $args['name'] != $old_status->name ) |
| 476 |
$args['slug'] = sanitize_title( $args['name'] ); |
| 477 |
|
| 478 |
// Reassign posts to new status slug if the slug changed and isn't restricted |
| 479 |
if ( isset( $args['slug'] ) && $args['slug'] != $old_status->slug && !$this->is_restricted_status( $old_status->slug ) ) { |
| 480 |
$new_status = $args['slug']; |
| 481 |
$this->reassign_post_status( $old_status->slug, $new_status ); |
| 482 |
|
| 483 |
$default_status = $this->get_default_custom_status()->slug; |
| 484 |
if ( $old_status->slug == $default_status ) |
| 485 |
$edit_flow->update_module_option( $this->module->name, 'default_status', $new_status ); |
| 486 |
} |
| 487 |
// We're encoding metadata that isn't supported by default in the term's description field |
| 488 |
$args_to_encode = array(); |
| 489 |
$args_to_encode['description'] = ( isset( $args['description'] ) ) ? $args['description'] : $old_status->description; |
| 490 |
$args_to_encode['position'] = ( isset( $args['position'] ) ) ? $args['position'] : $old_status->position; |
| 491 |
$encoded_description = $this->get_encoded_description( $args_to_encode ); |
| 492 |
$args['description'] = $encoded_description; |
| 493 |
|
| 494 |
$updated_status_array = wp_update_term( $status_id, self::taxonomy_key, $args ); |
| 495 |
$updated_status = $this->get_custom_status_by( 'id', $updated_status_array['term_id'] ); |
| 496 |
|
| 497 |
return $updated_status; |
| 498 |
|
| 499 |
} |
| 500 |
|
| 501 |
/** |
| 502 |
* Deletes a custom status from the wp_terms table. |
| 503 |
* |
| 504 |
* Partly a wrapper for the wp_delete_term function. |
| 505 |
* BUT, also reassigns posts that currently have the deleted status assigned. |
| 506 |
*/ |
| 507 |
function delete_custom_status( $status_id, $args = array(), $reassign = '' ) { |
| 508 |
global $edit_flow; |
| 509 |
// Reassign posts to alternate status |
| 510 |
|
| 511 |
// Get slug for the old status |
| 512 |
$old_status = $this->get_custom_status_by( 'id', $status_id )->slug; |
| 513 |
|
| 514 |
if ( $reassign == $old_status ) |
| 515 |
return new WP_Error( 'invalid', __( 'Cannot reassign to the status you want to delete', 'edit-flow' ) ); |
| 516 |
|
| 517 |
if( !$this->is_restricted_status( $old_status ) ) { |
| 518 |
$default_status = $this->get_default_custom_status()->slug; |
| 519 |
// If new status in $reassign, use that for all posts of the old_status |
| 520 |
if( !empty( $reassign ) ) |
| 521 |
$new_status = $this->get_custom_status_by( 'id', $reassign )->slug; |
| 522 |
else |
| 523 |
$new_status = $default_status; |
| 524 |
if ( $old_status == $default_status && $this->get_custom_status_by( 'slug', 'draft' ) ) { // Deleting default status |
| 525 |
$new_status = 'draft'; |
| 526 |
$edit_flow->update_module_option( $this->module->name, 'default_status', $new_status ); |
| 527 |
} |
| 528 |
|
| 529 |
$this->reassign_post_status( $old_status, $new_status ); |
| 530 |
|
| 531 |
return wp_delete_term( $status_id, self::taxonomy_key, $args ); |
| 532 |
} else |
| 533 |
return new WP_Error( 'restricted', __( 'Restricted status ', 'edit-flow' ) . '(' . $this->get_custom_status_by( 'id', $status_id )->name . ')' ); |
| 534 |
|
| 535 |
} |
| 536 |
|
| 537 |
/** |
| 538 |
* Get all custom statuses as an ordered array |
| 539 |
* |
| 540 |
* @param array|string $statuses |
| 541 |
* @param array $args |
| 542 |
* @return array $statuses All of the statuses |
| 543 |
*/ |
| 544 |
function get_custom_statuses( $args = array() ) { |
| 545 |
global $wp_post_statuses; |
| 546 |
|
| 547 |
// If this post type doesn't support custom statuses, we should return WP |
| 548 |
// default in our format. |
| 549 |
if ( $this->disable_custom_statuses_for_post_type() ) { |
| 550 |
$draft = $wp_post_statuses['draft']; |
| 551 |
$draft->slug = 'draft'; |
| 552 |
$draft->position = 1; |
| 553 |
|
| 554 |
$pending = $wp_post_statuses['pending']; |
| 555 |
$pending->slug = 'pending'; |
| 556 |
$pending->position = 2; |
| 557 |
return array( |
| 558 |
$draft, |
| 559 |
$pending |
| 560 |
); |
| 561 |
} |
| 562 |
|
| 563 |
$default = array( |
| 564 |
'hide_empty' => false, |
| 565 |
); |
| 566 |
$args = array_merge( $default, $args ); |
| 567 |
$statuses = get_terms( self::taxonomy_key, $args ); |
| 568 |
// Expand and order the statuses |
| 569 |
$ordered_statuses = array(); |
| 570 |
$hold_to_end = array(); |
| 571 |
foreach ( $statuses as $key => $status ) { |
| 572 |
// Unencode and set all of our psuedo term meta because we need the position if it exists |
| 573 |
$unencoded_description = $this->get_unencoded_description( $status->description ); |
| 574 |
if ( is_array( $unencoded_description ) ) { |
| 575 |
foreach( $unencoded_description as $key => $value ) { |
| 576 |
$status->$key = $value; |
| 577 |
} |
| 578 |
} |
| 579 |
// We require the position key later on (e.g. management table) |
| 580 |
if ( !isset( $status->position ) ) |
| 581 |
$status->position = false; |
| 582 |
// Only add the status to the ordered array if it has a set position and doesn't conflict with another key |
| 583 |
// Otherwise, hold it for later |
| 584 |
if ( $status->position && !array_key_exists( $status->position, $ordered_statuses ) ) { |
| 585 |
$ordered_statuses[(int)$status->position] = $status; |
| 586 |
} else { |
| 587 |
$hold_to_end[] = $status; |
| 588 |
} |
| 589 |
} |
| 590 |
// Sort the items numerically by key |
| 591 |
ksort( $ordered_statuses, SORT_NUMERIC ); |
| 592 |
// Append all of the statuses that didn't have an existing position |
| 593 |
foreach( $hold_to_end as $unpositioned_status ) |
| 594 |
$ordered_statuses[] = $unpositioned_status; |
| 595 |
return $ordered_statuses; |
| 596 |
} |
| 597 |
|
| 598 |
/** |
| 599 |
* Returns the a single status object based on ID, title, or slug |
| 600 |
* |
| 601 |
* @param string|int $string_or_int The status to search for, either by slug or ID |
| 602 |
* @return object|WP_Error $status The object for the matching status |
| 603 |
*/ |
| 604 |
function get_custom_status_by( $field, $value ) { |
| 605 |
|
| 606 |
$status = get_term_by( $field, $value, self::taxonomy_key ); |
| 607 |
if ( !$status || is_wp_error( $status ) ) |
| 608 |
return $status; |
| 609 |
// Unencode and set all of our psuedo term meta because we need the position if it exists |
| 610 |
$status->position = false; |
| 611 |
$unencoded_description = $this->get_unencoded_description( $status->description ); |
| 612 |
if ( is_array( $unencoded_description ) ) { |
| 613 |
foreach( $unencoded_description as $key => $value ) { |
| 614 |
$status->$key = $value; |
| 615 |
} |
| 616 |
} |
| 617 |
return $status; |
| 618 |
|
| 619 |
} |
| 620 |
|
| 621 |
/** |
| 622 |
* Get the term object for the default custom post status |
| 623 |
* |
| 624 |
* @return object $default_status Default post status object |
| 625 |
*/ |
| 626 |
function get_default_custom_status() { |
| 627 |
$default_status = $this->get_custom_status_by( 'slug', $this->module->options->default_status ); |
| 628 |
if ( ! $default_status ) |
| 629 |
$default_status = array_shift( $this->get_custom_statuses() ); |
| 630 |
return $default_status; |
| 631 |
|
| 632 |
} |
| 633 |
|
| 634 |
/** |
| 635 |
* Assign new statuses to posts using value provided or the default |
| 636 |
* |
| 637 |
* @param string $old_status Slug for the old status |
| 638 |
* @param string $new_status Slug for the new status |
| 639 |
*/ |
| 640 |
function reassign_post_status( $old_status, $new_status = '' ) { |
| 641 |
global $wpdb; |
| 642 |
|
| 643 |
if ( empty( $new_status ) ) |
| 644 |
$new_status = $this->get_default_custom_status()->slug; |
| 645 |
|
| 646 |
// Make the database call |
| 647 |
$result = $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'post_status' => $old_status ), array( '%s' )); |
| 648 |
} |
| 649 |
|
| 650 |
/** |
| 651 |
* Insert new column header for post status after the title column |
| 652 |
* |
| 653 |
* @param array $posts_columns Columns currently shown on the Edit Posts screen |
| 654 |
* @return array Same array as the input array with a "status" column added after the "title" column |
| 655 |
*/ |
| 656 |
function _filter_manage_posts_columns( $posts_columns ) { |
| 657 |
// Return immediately if the supplied parameter isn't an array (which shouldn't happen in practice?) |
| 658 |
// http://wordpress.org/support/topic/plugin-edit-flow-bug-shows-2-drafts-when-there-are-none-leads-to-error-messages |
| 659 |
if ( !is_array( $posts_columns ) ) |
| 660 |
return $posts_columns; |
| 661 |
|
| 662 |
// Only do it for the post types this module is activated for |
| 663 |
if ( !in_array( $this->get_current_post_type(), $this->get_post_types_for_module( $this->module ) ) ) |
| 664 |
return $posts_columns; |
| 665 |
|
| 666 |
$result = array(); |
| 667 |
foreach ( $posts_columns as $key => $value ) { |
| 668 |
if ($key == 'title') { |
| 669 |
$result[$key] = $value; |
| 670 |
$result['status'] = __('Status', 'edit-flow'); |
| 671 |
} else $result[$key] = $value; |
| 672 |
} |
| 673 |
return $result; |
| 674 |
|
| 675 |
} |
| 676 |
|
| 677 |
/** |
| 678 |
* Adds a Post's status to its row on the Edit page |
| 679 |
* |
| 680 |
* @param string $column_name |
| 681 |
**/ |
| 682 |
function _filter_manage_posts_custom_column( $column_name ) { |
| 683 |
|
| 684 |
if ( $column_name == 'status' ) { |
| 685 |
global $post; |
| 686 |
echo $this->get_post_status_friendly_name( $post->post_status ); |
| 687 |
} |
| 688 |
|
| 689 |
} |
| 690 |
|
| 691 |
|
| 692 |
/** |
| 693 |
* Determines whether the slug indicated belongs to a restricted status or not |
| 694 |
* |
| 695 |
* @param string $slug Slug of the status |
| 696 |
* @return bool $restricted True if restricted, false if not |
| 697 |
*/ |
| 698 |
function is_restricted_status( $slug ) { |
| 699 |
|
| 700 |
switch( $slug ) { |
| 701 |
case 'publish': |
| 702 |
case 'private': |
| 703 |
case 'future': |
| 704 |
case 'new': |
| 705 |
case 'inherit': |
| 706 |
case 'auto-draft': |
| 707 |
case 'trash': |
| 708 |
$restricted = true; |
| 709 |
break; |
| 710 |
|
| 711 |
default: |
| 712 |
$restricted = false; |
| 713 |
break; |
| 714 |
} |
| 715 |
return $restricted; |
| 716 |
|
| 717 |
} |
| 718 |
|
| 719 |
/** |
| 720 |
* Handles a form's POST request to add a custom status |
| 721 |
* |
| 722 |
* @since 0.7 |
| 723 |
*/ |
| 724 |
function handle_add_custom_status() { |
| 725 |
|
| 726 |
// Check that the current POST request is our POST request |
| 727 |
if ( !isset( $_POST['submit'], $_GET['page'], $_POST['action'] ) |
| 728 |
|| $_GET['page'] != $this->module->settings_slug || $_POST['action'] != 'add-new' ) |
| 729 |
return; |
| 730 |
|
| 731 |
if ( !wp_verify_nonce( $_POST['_wpnonce'], 'custom-status-add-nonce' ) ) |
| 732 |
wp_die( $this->module->messages['nonce-failed'] ); |
| 733 |
|
| 734 |
// Validate and sanitize the form data |
| 735 |
$status_name = strip_tags( trim( $_POST['status_name'] ) ); |
| 736 |
$status_slug = sanitize_title( $status_name ); |
| 737 |
$status_description = strip_tags( trim( $_POST['status_description'] ) ); |
| 738 |
|
| 739 |
/** |
| 740 |
* Form validation |
| 741 |
* - Name is required and can't conflict with an existing name or slug |
| 742 |
* - Description is optional |
| 743 |
*/ |
| 744 |
$_REQUEST['form-errors'] = array(); |
| 745 |
// Check if name field was filled in |
| 746 |
if( empty( $status_name ) ) |
| 747 |
$_REQUEST['form-errors']['name'] = __( 'Please enter a name for the status', 'edit-flow' ); |
| 748 |
// Check that the name isn't numeric |
| 749 |
if ( (int)$status_name != 0 ) |
| 750 |
$_REQUEST['form-errors']['name'] = __( 'Please enter a valid, non-numeric name for the status.', 'edit-flow' ); |
| 751 |
// Check that the status name doesn't exceed 20 chars |
| 752 |
if ( strlen( $status_name ) > 20 ) |
| 753 |
$_REQUEST['form-errors']['name'] = __( 'Status name cannot exceed 20 characters. Please try a shorter name.', 'edit-flow' ); |
| 754 |
// Check to make sure the status doesn't already exist as another term because otherwise we'd get a weird slug |
| 755 |
if ( term_exists( $status_slug ) ) |
| 756 |
$_REQUEST['form-errors']['name'] = __( 'Status name conflicts with existing term. Please choose another.', 'edit-flow' ); |
| 757 |
// Check to make sure the name is not restricted |
| 758 |
if ( $this->is_restricted_status( strtolower( $status_slug ) ) ) |
| 759 |
$_REQUEST['form-errors']['name'] = __( 'Status name is restricted. Please choose another name.', 'edit-flow' ); |
| 760 |
|
| 761 |
// If there were any form errors, kick out and return them |
| 762 |
if ( count( $_REQUEST['form-errors'] ) ) { |
| 763 |
$_REQUEST['error'] = 'form-error'; |
| 764 |
return; |
| 765 |
} |
| 766 |
|
| 767 |
// Try to add the status |
| 768 |
$status_args = array( |
| 769 |
'description' => $status_description, |
| 770 |
'slug' => $status_slug, |
| 771 |
); |
| 772 |
$return = $this->add_custom_status( $status_name, $status_args ); |
| 773 |
if ( is_wp_error( $return ) ) |
| 774 |
wp_die( __( 'Could not add status: ', 'edit-flow' ) . $return->get_error_message() ); |
| 775 |
// Redirect if successful |
| 776 |
$redirect_url = $this->get_link( array( 'message' => 'status-added' ) ); |
| 777 |
wp_redirect( $redirect_url ); |
| 778 |
exit; |
| 779 |
|
| 780 |
} |
| 781 |
|
| 782 |
/** |
| 783 |
* Handles a POST request to edit an custom status |
| 784 |
* |
| 785 |
* @since 0.7 |
| 786 |
*/ |
| 787 |
function handle_edit_custom_status() { |
| 788 |
if ( !isset( $_POST['submit'], $_GET['page'], $_GET['action'], $_GET['term-id'] ) |
| 789 |
|| $_GET['page'] != $this->module->settings_slug || $_GET['action'] != 'edit-status' ) |
| 790 |
return; |
| 791 |
|
| 792 |
if ( !wp_verify_nonce( $_POST['_wpnonce'], 'edit-status' ) ) |
| 793 |
wp_die( $this->module->messages['nonce-failed'] ); |
| 794 |
|
| 795 |
if ( !current_user_can( 'manage_options' ) ) |
| 796 |
wp_die( $this->module->messages['invalid-permissions'] ); |
| 797 |
|
| 798 |
if ( !$existing_status = $this->get_custom_status_by( 'id', (int)$_GET['term-id'] ) ) |
| 799 |
wp_die( $this->module->messsage['term-error'] ); |
| 800 |
|
| 801 |
$name = strip_tags( trim( $_POST['name'] ) ); |
| 802 |
$description = strip_tags( trim( $_POST['description'] ) ); |
| 803 |
|
| 804 |
/** |
| 805 |
* Form validation for editing custom status |
| 806 |
* |
| 807 |
* Details |
| 808 |
* - 'name' is a required field and can't conflict with existing name or slug |
| 809 |
* - 'description' is optional |
| 810 |
*/ |
| 811 |
$_REQUEST['form-errors'] = array(); |
| 812 |
// Check if name field was filled in |
| 813 |
if( empty( $name ) ) |
| 814 |
$_REQUEST['form-errors']['name'] = __( 'Please enter a name for the status', 'edit-flow' ); |
| 815 |
// Check that the name isn't numeric |
| 816 |
if ( is_numeric( $name ) ) |
| 817 |
$_REQUEST['form-errors']['name'] = __( 'Please enter a valid, non-numeric name for the status.', 'edit-flow' ); |
| 818 |
// Check that the status name doesn't exceed 20 chars |
| 819 |
if ( strlen( $name ) > 20 ) |
| 820 |
$_REQUEST['form-errors']['name'] = __( 'Status name cannot exceed 20 characters. Please try a shorter name.', 'edit-flow' ); |
| 821 |
// Check to make sure the status doesn't already exist as another term because otherwise we'd get a weird slug |
| 822 |
$term_exists = term_exists( sanitize_title( $name ) ); |
| 823 |
if ( $term_exists && $term_exists != $existing_status->term_id ) |
| 824 |
$_REQUEST['form-errors']['name'] = __( 'Status name conflicts with existing term. Please choose another.', 'edit-flow' ); |
| 825 |
// Check to make sure the status doesn't already exist |
| 826 |
$search_status = $this->get_custom_status_by( 'slug', sanitize_title( $name ) ); |
| 827 |
if ( $search_status && $search_status->term_id != $existing_status->term_id ) |
| 828 |
$_REQUEST['form-errors']['name'] = __( 'Status name conflicts with existing status. Please choose another.', 'edit-flow' ); |
| 829 |
// Check to make sure the name is not restricted |
| 830 |
if ( $this->is_restricted_status( strtolower( sanitize_title( $name ) ) ) ) |
| 831 |
$_REQUEST['form-errors']['name'] = __( 'Status name is restricted. Please choose another name.', 'edit-flow' ); |
| 832 |
|
| 833 |
// Kick out if there are any errors |
| 834 |
if ( count( $_REQUEST['form-errors'] ) ) { |
| 835 |
$_REQUEST['error'] = 'form-error'; |
| 836 |
return; |
| 837 |
} |
| 838 |
|
| 839 |
// Try to add the new post status |
| 840 |
$args = array( |
| 841 |
'name' => $name, |
| 842 |
'slug' => sanitize_title( $name ), |
| 843 |
'description' => $description, |
| 844 |
); |
| 845 |
$return = $this->update_custom_status( $existing_status->term_id, $args ); |
| 846 |
if ( is_wp_error( $return ) ) |
| 847 |
wp_die( __( 'Error updating post status.', 'edit-flow' ) ); |
| 848 |
|
| 849 |
$redirect_url = $this->get_link( array( 'message' => 'status-updated' ) ); |
| 850 |
wp_redirect( $redirect_url ); |
| 851 |
exit; |
| 852 |
} |
| 853 |
|
| 854 |
/** |
| 855 |
* Handles a GET request to make the identified status default |
| 856 |
* |
| 857 |
* @since 0.7 |
| 858 |
*/ |
| 859 |
function handle_make_default_custom_status() { |
| 860 |
global $edit_flow; |
| 861 |
|
| 862 |
// Check that the current GET request is our GET request |
| 863 |
if ( !isset( $_GET['page'], $_GET['action'], $_GET['term-id'], $_GET['nonce'] ) |
| 864 |
|| $_GET['page'] != $this->module->settings_slug || $_GET['action'] != 'make-default' ) |
| 865 |
return; |
| 866 |
|
| 867 |
// Check for proper nonce |
| 868 |
if ( !wp_verify_nonce( $_GET['nonce'], 'make-default' ) ) |
| 869 |
wp_die( __( 'Invalid nonce for submission.', 'edit-flow' ) ); |
| 870 |
|
| 871 |
// Only allow users with the proper caps |
| 872 |
if ( !current_user_can( 'manage_options' ) ) |
| 873 |
wp_die( __( 'Sorry, you do not have permission to edit custom statuses.', 'edit-flow' ) ); |
| 874 |
|
| 875 |
$term_id = (int)$_GET['term-id']; |
| 876 |
$term = $this->get_custom_status_by( 'id', $term_id ); |
| 877 |
if ( is_object( $term ) ) { |
| 878 |
$edit_flow->update_module_option( $this->module->name, 'default_status', $term->slug ); |
| 879 |
// @todo How do we want to handle users who click the link from "Add New Status" |
| 880 |
$redirect_url = $this->get_link( array( 'message' => 'default-status-changed' ) ); |
| 881 |
wp_redirect( $redirect_url ); |
| 882 |
exit; |
| 883 |
} else { |
| 884 |
wp_die( __( 'Status doesn't exist.', 'edit-flow' ) ); |
| 885 |
} |
| 886 |
|
| 887 |
} |
| 888 |
|
| 889 |
/** |
| 890 |
* Handles a GET request to delete a specific term |
| 891 |
* |
| 892 |
* @since 0.7 |
| 893 |
*/ |
| 894 |
function handle_delete_custom_status() { |
| 895 |
|
| 896 |
// Check that this GET request is our GET request |
| 897 |
if ( !isset( $_GET['page'], $_GET['action'], $_GET['term-id'], $_GET['nonce'] ) |
| 898 |
|| $_GET['page'] != $this->module->settings_slug || $_GET['action'] != 'delete-status' ) |
| 899 |
return; |
| 900 |
|
| 901 |
// Check for proper nonce |
| 902 |
if ( !wp_verify_nonce( $_GET['nonce'], 'delete-status' ) ) |
| 903 |
wp_die( __( 'Invalid nonce for submission.', 'edit-flow' ) ); |
| 904 |
|
| 905 |
// Only allow users with the proper caps |
| 906 |
if ( !current_user_can( 'manage_options' ) ) |
| 907 |
wp_die( __( 'Sorry, you do not have permission to edit custom statuses.', 'edit-flow' ) ); |
| 908 |
|
| 909 |
// Check to make sure the status isn't already deleted |
| 910 |
$term_id = (int)$_GET['term-id']; |
| 911 |
$term = $this->get_custom_status_by( 'id', $term_id ); |
| 912 |
if( !$term ) |
| 913 |
wp_die( __( 'Status does not exist.', 'edit-flow' ) ); |
| 914 |
|
| 915 |
// Don't allow deletion of default status |
| 916 |
if ( $term->slug == $this->get_default_custom_status()->slug ) |
| 917 |
wp_die( __( 'Cannot delete default status.', 'edit-flow' ) ); |
| 918 |
|
| 919 |
$return = $this->delete_custom_status( $term_id ); |
| 920 |
if ( is_wp_error( $return ) ) |
| 921 |
wp_die( __( 'Could not delete the status: ', 'edit-flow' ) . $return->get_error_message() ); |
| 922 |
|
| 923 |
$redirect_url = $this->get_link( array( 'message' => 'status-deleted' ) ); |
| 924 |
wp_redirect( $redirect_url ); |
| 925 |
exit; |
| 926 |
|
| 927 |
} |
| 928 |
|
| 929 |
/** |
| 930 |
* Generate a link to one of the custom status actions |
| 931 |
* |
| 932 |
* @since 0.7 |
| 933 |
* |
| 934 |
* @param array $args (optional) Action and any query args to add to the URL |
| 935 |
* @return string $link Direct link to complete the action |
| 936 |
*/ |
| 937 |
function get_link( $args = array() ) { |
| 938 |
if ( !isset( $args['action'] ) ) |
| 939 |
$args['action'] = ''; |
| 940 |
if ( !isset( $args['page'] ) ) |
| 941 |
$args['page'] = $this->module->settings_slug; |
| 942 |
// Add other things we may need depending on the action |
| 943 |
switch( $args['action'] ) { |
| 944 |
case 'make-default': |
| 945 |
case 'delete-status': |
| 946 |
$args['nonce'] = wp_create_nonce( $args['action'] ); |
| 947 |
break; |
| 948 |
default: |
| 949 |
break; |
| 950 |
} |
| 951 |
return add_query_arg( $args, get_admin_url( null, 'admin.php' ) ); |
| 952 |
} |
| 953 |
|
| 954 |
/** |
| 955 |
* Handle an ajax request to update the order of custom statuses |
| 956 |
* |
| 957 |
* @since 0.7 |
| 958 |
*/ |
| 959 |
function handle_ajax_update_status_positions() { |
| 960 |
|
| 961 |
if ( !wp_verify_nonce( $_POST['custom_status_sortable_nonce'], 'custom-status-sortable' ) ) |
| 962 |
$this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); |
| 963 |
|
| 964 |
if ( !current_user_can( 'manage_options') ) |
| 965 |
$this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); |
| 966 |
|
| 967 |
if ( !isset( $_POST['status_positions'] ) || !is_array( $_POST['status_positions'] ) ) |
| 968 |
$this->print_ajax_response( 'error', __( 'Terms not set.', 'edit-flow' ) ); |
| 969 |
|
| 970 |
// Update each custom status with its new position |
| 971 |
foreach ( $_POST['status_positions'] as $position => $term_id ) { |
| 972 |
|
| 973 |
// Have to add 1 to the position because the index started with zero |
| 974 |
$args = array( |
| 975 |
'position' => (int)$position + 1, |
| 976 |
); |
| 977 |
$return = $this->update_custom_status( (int)$term_id, $args ); |
| 978 |
// @todo check that this was a valid return |
| 979 |
} |
| 980 |
$this->print_ajax_response( 'success', $this->module->messages['status-position-updated'] ); |
| 981 |
} |
| 982 |
|
| 983 |
/** |
| 984 |
* Handle an Inline Edit POST request to update status values |
| 985 |
* |
| 986 |
* @since 0.7 |
| 987 |
*/ |
| 988 |
function ajax_inline_save_status() { |
| 989 |
global $edit_flow; |
| 990 |
|
| 991 |
if ( !wp_verify_nonce( $_POST['inline_edit'], 'custom-status-inline-edit-nonce' ) ) |
| 992 |
die( $this->module->messages['nonce-failed'] ); |
| 993 |
|
| 994 |
if ( !current_user_can( 'manage_options') ) |
| 995 |
die( $this->module->messages['invalid-permissions'] ); |
| 996 |
|
| 997 |
$term_id = (int) $_POST['status_id']; |
| 998 |
$status_name = strip_tags( trim( $_POST['name'] ) ); |
| 999 |
$status_slug = sanitize_title( $_POST['name'] ); |
| 1000 |
$status_description = strip_tags( trim( $_POST['description'] ) ); |
| 1001 |
|
| 1002 |
// Check if name field was filled in |
| 1003 |
if ( empty( $status_name ) ) { |
| 1004 |
$change_error = new WP_Error( 'invalid', __( 'Please enter a name for the status.', 'edit-flow' ) ); |
| 1005 |
die( $change_error->get_error_message() ); |
| 1006 |
} |
| 1007 |
|
| 1008 |
// Check that the name isn't numeric |
| 1009 |
if( is_numeric( $status_name) ) { |
| 1010 |
$change_error = new WP_Error( 'invalid', __( 'Please enter a valid, non-numeric name for the status.', 'edit-flow' ) ); |
| 1011 |
die( $change_error->get_error_message() ); |
| 1012 |
} |
| 1013 |
|
| 1014 |
// Check that the status name doesn't exceed 20 chars |
| 1015 |
if ( strlen( $status_name ) > 20 ) { |
| 1016 |
$change_error = new WP_Error( 'invalid', __( 'Status name cannot exceed 20 characters. Please try a shorter name.', 'edit-flow' ) ); |
| 1017 |
die( $change_error->get_error_message() ); |
| 1018 |
} |
| 1019 |
|
| 1020 |
// Check to make sure the name is not restricted |
| 1021 |
if ( $edit_flow->custom_status->is_restricted_status( strtolower( $status_name ) ) ) { |
| 1022 |
$change_error = new WP_Error( 'invalid', __( 'Status name is restricted. Please chose another name.', 'edit-flow' ) ); |
| 1023 |
die( $change_error->get_error_message() ); |
| 1024 |
} |
| 1025 |
|
| 1026 |
// Check to make sure the status doesn't already exist |
| 1027 |
if ( $this->get_custom_status_by( 'slug', $status_slug ) && ( $this->get_custom_status_by( 'id', $term_id )->slug != $status_slug ) ) { |
| 1028 |
$change_error = new WP_Error( 'invalid', __( 'Status already exists. Please choose another name.', 'edit-flow' ) ); |
| 1029 |
die( $change_error->get_error_message() ); |
| 1030 |
} |
| 1031 |
|
| 1032 |
// Check to make sure the status doesn't already exist as another term because otherwise we'd get a fatal error |
| 1033 |
$term_exists = term_exists( sanitize_title( $status_name ) ); |
| 1034 |
if ( $term_exists && $term_exists != $term_id ) { |
| 1035 |
$change_error = new WP_Error( 'invalid', __( 'Status name conflicts with existing term. Please choose another.', 'edit-flow' ) ); |
| 1036 |
die( $change_error->get_error_message() ); |
| 1037 |
} |
| 1038 |
|
| 1039 |
// get status_name & status_description |
| 1040 |
$args = array( |
| 1041 |
'name' => $status_name, |
| 1042 |
'description' => $status_description, |
| 1043 |
'slug' => $status_slug, |
| 1044 |
); |
| 1045 |
$return = $this->update_custom_status( $term_id, $args ); |
| 1046 |
if( !is_wp_error( $return ) ) { |
| 1047 |
set_current_screen( 'edit-custom-status' ); |
| 1048 |
$wp_list_table = new EF_Custom_Status_List_Table(); |
| 1049 |
$wp_list_table->prepare_items(); |
| 1050 |
echo $wp_list_table->single_row( $return ); |
| 1051 |
die(); |
| 1052 |
} else { |
| 1053 |
$change_error = new WP_Error( 'invalid', sprintf( __( 'Could not update the status: <strong>%s</strong>', 'edit-flow' ), $status_name ) ); |
| 1054 |
die( $change_error->get_error_message() ); |
| 1055 |
} |
| 1056 |
|
| 1057 |
} |
| 1058 |
|
| 1059 |
/** |
| 1060 |
* Register settings for notifications so we can partially use the Settings API |
| 1061 |
* (We use the Settings API for form generation, but not saving) |
| 1062 |
* |
| 1063 |
* @since 0.7 |
| 1064 |
*/ |
| 1065 |
function register_settings() { |
| 1066 |
|
| 1067 |
add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name ); |
| 1068 |
add_settings_field( 'post_types', __( 'Use on these post types:', 'edit-flow' ), array( $this, 'settings_post_types_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' ); |
| 1069 |
add_settings_field( 'always_show_dropdown', __( 'Always show dropdown:', 'edit-flow' ), array( $this, 'settings_always_show_dropdown_option'), $this->module->options_group_name, $this->module->options_group_name . '_general' ); |
| 1070 |
|
| 1071 |
} |
| 1072 |
|
| 1073 |
/** |
| 1074 |
* Choose the post types that should be displayed on the calendar |
| 1075 |
* |
| 1076 |
* @since 0.7 |
| 1077 |
*/ |
| 1078 |
function settings_post_types_option() { |
| 1079 |
global $edit_flow; |
| 1080 |
$edit_flow->settings->helper_option_custom_post_type( $this->module ); |
| 1081 |
} |
| 1082 |
|
| 1083 |
/** |
| 1084 |
* Option for whether the blog admin email address should be always notified or not |
| 1085 |
* |
| 1086 |
* @since 0.7 |
| 1087 |
*/ |
| 1088 |
function settings_always_show_dropdown_option() { |
| 1089 |
$options = array( |
| 1090 |
'off' => __( 'Disabled', 'edit-flow' ), |
| 1091 |
'on' => __( 'Enabled', 'edit-flow' ), |
| 1092 |
); |
| 1093 |
echo '<select id="always_show_dropdown" name="' . $this->module->options_group_name . '[always_show_dropdown]">'; |
| 1094 |
foreach ( $options as $value => $label ) { |
| 1095 |
echo '<option value="' . esc_attr( $value ) . '"'; |
| 1096 |
echo selected( $this->module->options->always_show_dropdown, $value ); |
| 1097 |
echo '>' . esc_html( $label ) . '</option>'; |
| 1098 |
} |
| 1099 |
echo '</select>'; |
| 1100 |
} |
| 1101 |
|
| 1102 |
/** |
| 1103 |
* Validate input from the end user |
| 1104 |
* |
| 1105 |
* @since 0.7 |
| 1106 |
*/ |
| 1107 |
function settings_validate( $new_options ) { |
| 1108 |
|
| 1109 |
// Whitelist validation for the post type options |
| 1110 |
if ( !isset( $new_options['post_types'] ) ) |
| 1111 |
$new_options['post_types'] = array(); |
| 1112 |
$new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support ); |
| 1113 |
|
| 1114 |
// Whitelist validation for the 'always_show_dropdown' optoins |
| 1115 |
if ( !isset( $new_options['always_show_dropdown'] ) || $new_options['always_show_dropdown'] != 'on' ) |
| 1116 |
$new_options['always_show_dropdown'] = 'off'; |
| 1117 |
|
| 1118 |
return $new_options; |
| 1119 |
} |
| 1120 |
|
| 1121 |
/** |
| 1122 |
* Primary configuration page for custom status class. |
| 1123 |
* Shows form to add new custom statuses on the left and a |
| 1124 |
* WP_List_Table with the custom status terms on the right |
| 1125 |
*/ |
| 1126 |
function print_configure_view() { |
| 1127 |
global $edit_flow; |
| 1128 |
|
| 1129 |
/** Full width view for editing a custom status **/ |
| 1130 |
if ( isset( $_GET['action'], $_GET['term-id'] ) && $_GET['action'] == 'edit-status' ): ?> |
| 1131 |
<?php |
| 1132 |
// Check whether the term exists |
| 1133 |
$term_id = (int)$_GET['term-id']; |
| 1134 |
$status = $this->get_custom_status_by( 'id', $term_id ); |
| 1135 |
if ( !$status ) { |
| 1136 |
echo '<div class="error"><p>' . $this->module->messages['status-missing'] . '</p></div>'; |
| 1137 |
return; |
| 1138 |
} |
| 1139 |
$edit_status_link = $this->get_link( array( 'action' => 'edit-status', 'term-id' => $term_id ) ); |
| 1140 |
|
| 1141 |
$name = ( isset( $_POST['name'] ) ) ? stripslashes( $_POST['name'] ) : $status->name; |
| 1142 |
$description = ( isset( $_POST['description'] ) ) ? stripslashes( strip_tags( $_POST['description'] ) ) : $status->description; |
| 1143 |
?> |
| 1144 |
|
| 1145 |
<div id="ajax-response"></div> |
| 1146 |
<form method="post" action="<?php echo esc_attr( $edit_status_link ); ?>" > |
| 1147 |
<input type="hidden" name="term-id" value="<?php echo esc_attr( $term_id ); ?>" /> |
| 1148 |
<?php |
| 1149 |
wp_original_referer_field(); |
| 1150 |
wp_nonce_field( 'edit-status' ); |
| 1151 |
?> |
| 1152 |
<table class="form-table"> |
| 1153 |
<tr class="form-field form-required"> |
| 1154 |
<th scope="row" valign="top"><label for="name"><?php _e( 'Custom Status', 'edit-flow' ); ?></label></th> |
| 1155 |
<td><input name="name" id="name" type="text" value="<?php echo esc_attr( $name ); ?>" size="40" aria-required="true" /> |
| 1156 |
<?php $edit_flow->settings->helper_print_error_or_description( 'name', __( 'The name is used to identify the status. (Max: 20 characters)', 'edit-flow' ) ); ?> |
| 1157 |
</td> |
| 1158 |
</tr> |
| 1159 |
<tr class="form-field"> |
| 1160 |
<th scope="row" valign="top"><?php _e( 'Slug', 'edit-flow' ); ?></th> |
| 1161 |
<td> |
| 1162 |
<input type="text" disabled="disabled" value="<?php echo esc_attr( $status->slug ); ?>" /> |
| 1163 |
<?php $edit_flow->settings->helper_print_error_or_description( 'slug', __( 'The slug is the unique ID for the status and is changed when the name is changed.', 'edit-flow' ) ); ?> |
| 1164 |
</td> |
| 1165 |
</tr> |
| 1166 |
<tr class="form-field"> |
| 1167 |
<th scope="row" valign="top"><label for="description"><?php _e( 'Description', 'edit-flow' ); ?></label></th> |
| 1168 |
<td> |
| 1169 |
<textarea name="description" id="description" rows="5" cols="50" style="width: 97%;"><?php echo esc_textarea( $description ); ?></textarea> |
| 1170 |
<?php $edit_flow->settings->helper_print_error_or_description( 'description', __( 'The description is primarily for administrative use, to give you some context on what the custom status is to be used for.', 'edit-flow' ) ); ?> |
| 1171 |
</td> |
| 1172 |
</tr> |
| 1173 |
</table> |
| 1174 |
<p class="submit"> |
| 1175 |
<?php submit_button( __( 'Update Status', 'edit-flow' ), 'primary', 'submit', false ); ?> |
| 1176 |
<a class="cancel-settings-link" href="<?php echo esc_url( $this->get_link() ); ?>"><?php _e( 'Cancel', 'edit-flow' ); ?></a> |
| 1177 |
</p> |
| 1178 |
</form> |
| 1179 |
|
| 1180 |
<?php else: ?> |
| 1181 |
<?php |
| 1182 |
$wp_list_table = new EF_Custom_Status_List_Table(); |
| 1183 |
$wp_list_table->prepare_items(); |
| 1184 |
?> |
| 1185 |
<script type="text/javascript"> |
| 1186 |
var ef_confirm_delete_status_string = "<?php echo esc_js( __( 'Are you sure you want to delete the post status? All posts with this status will be assigned to the default status.', 'edit-flow' ) ); ?>"; |
| 1187 |
</script> |
| 1188 |
<div id="col-right"> |
| 1189 |
<div class="col-wrap"> |
| 1190 |
<?php $wp_list_table->display(); ?> |
| 1191 |
<?php wp_nonce_field( 'custom-status-sortable', 'custom-status-sortable' ); ?> |
| 1192 |
<p class="description" style="padding-top:10px;"><?php _e( 'Deleting a post status will assign all posts to the default post status.', 'edit-flow' ); ?></p> |
| 1193 |
</div> |
| 1194 |
</div> |
| 1195 |
<div id="col-left"> |
| 1196 |
<div class="col-wrap"> |
| 1197 |
<div class="form-wrap"> |
| 1198 |
<h3 class="nav-tab-wrapper"> |
| 1199 |
<a href="<?php echo esc_url( $this->get_link() ); ?>" class="nav-tab<?php if ( !isset( $_GET['action'] ) || $_GET['action'] != 'change-options' ) echo ' nav-tab-active'; ?>"><?php _e( 'Add New', 'edit-flow' ); ?></a> |
| 1200 |
<a href="<?php echo esc_url( $this->get_link( array( 'action' => 'change-options' ) ) ); ?>" class="nav-tab<?php if ( isset( $_GET['action'] ) && $_GET['action'] == 'change-options' ) echo ' nav-tab-active'; ?>"><?php _e( 'Options', 'edit-flow' ); ?></a> |
| 1201 |
</h3> |
| 1202 |
<?php if ( isset( $_GET['action'] ) && $_GET['action'] == 'change-options' ): ?> |
| 1203 |
<form class="basic-settings" action="<?php echo esc_url( $this->get_link( array( 'action' => 'change-options' ) ) ); ?>" method="post"> |
| 1204 |
<?php settings_fields( $this->module->options_group_name ); ?> |
| 1205 |
<?php do_settings_sections( $this->module->options_group_name ); ?> |
| 1206 |
<?php echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />'; ?> |
| 1207 |
<?php submit_button(); ?> |
| 1208 |
</form> |
| 1209 |
<?php else: ?> |
| 1210 |
<?php /** Custom form for adding a new Custom Status term **/ ?> |
| 1211 |
<form class="add:the-list:" action="<?php echo esc_url( $this->get_link() ); ?>" method="post" id="addstatus" name="addstatus"> |
| 1212 |
<div class="form-field form-required"> |
| 1213 |
<label for="status_name"><?php _e( 'Name', 'edit-flow' ); ?></label> |
| 1214 |
<input type="text" aria-required="true" size="20" maxlength="20" id="status_name" name="status_name" value="<?php if ( !empty( $_POST['status_name'] ) ) echo esc_attr( $_POST['status_name'] ) ?>" /> |
| 1215 |
<?php $edit_flow->settings->helper_print_error_or_description( 'name', __( 'The name is used to identify the status. (Max: 20 characters)', 'edit-flow' ) ); ?> |
| 1216 |
</div> |
| 1217 |
<div class="form-field"> |
| 1218 |
<label for="status_description"><?php _e( 'Description', 'edit-flow' ); ?></label> |
| 1219 |
<textarea cols="40" rows="5" id="status_description" name="status_description"><?php if ( !empty( $_POST['status_description'] ) ) echo esc_textarea( $_POST['status_description'] ) ?></textarea> |
| 1220 |
<?php $edit_flow->settings->helper_print_error_or_description( 'description', __( 'The description is primarily for administrative use, to give you some context on what the custom status is to be used for.', 'edit-flow' ) ); ?> |
| 1221 |
</div> |
| 1222 |
<?php wp_nonce_field( 'custom-status-add-nonce' ); ?> |
| 1223 |
<?php echo '<input id="action" name="action" type="hidden" value="add-new" />'; ?> |
| 1224 |
<p class="submit"><?php submit_button( __( 'Add New Status', 'edit-flow' ), 'primary', 'submit', false ); ?><a class="cancel-settings-link" href="<?php echo EDIT_FLOW_SETTINGS_PAGE; ?>"><?php _e( 'Back to Edit Flow', 'edit-flow' ); ?></a></p> |
| 1225 |
</form> |
| 1226 |
<?php endif; ?> |
| 1227 |
</div> |
| 1228 |
</div> |
| 1229 |
</div> |
| 1230 |
<?php $wp_list_table->inline_edit(); ?> |
| 1231 |
<?php endif; ?> |
| 1232 |
<?php |
| 1233 |
} |
| 1234 |
|
| 1235 |
/** |
| 1236 |
* This is a hack! hack! hack! until core is fixed/better supports custom statuses |
| 1237 |
* |
| 1238 |
* When publishing a post with a custom status, set the status to 'pending' temporarily |
| 1239 |
* @see Works around this limitation: http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/post.php#L2694 |
| 1240 |
* @see Original thread: http://wordpress.org/support/topic/plugin-edit-flow-custom-statuses-create-timestamp-problem |
| 1241 |
* @see Core ticket: http://core.trac.wordpress.org/ticket/18362 |
| 1242 |
*/ |
| 1243 |
function check_timestamp_on_publish() { |
| 1244 |
global $edit_flow, $pagenow, $wpdb; |
| 1245 |
|
| 1246 |
if ( $this->disable_custom_statuses_for_post_type() ) |
| 1247 |
return; |
| 1248 |
|
| 1249 |
// Handles the transition to 'publish' on edit.php |
| 1250 |
if ( isset( $edit_flow ) && $pagenow == 'edit.php' && isset( $_REQUEST['bulk_edit'] ) ) { |
| 1251 |
// For every post_id, set the post_status as 'pending' only when there's no timestamp set for $post_date_gmt |
| 1252 |
if ( $_REQUEST['_status'] == 'publish' ) { |
| 1253 |
$post_ids = array_map( 'intval', (array) $_REQUEST['post'] ); |
| 1254 |
foreach ( $post_ids as $post_id ) { |
| 1255 |
$wpdb->update( $wpdb->posts, array( 'post_status' => 'pending' ), array( 'ID' => $post_id, 'post_date_gmt' => '0000-00-00 00:00:00' ) ); |
| 1256 |
clean_post_cache( $post_id ); |
| 1257 |
} |
| 1258 |
} |
| 1259 |
} |
| 1260 |
|
| 1261 |
// Handles the transition to 'publish' on post.php |
| 1262 |
if ( isset( $edit_flow ) && $pagenow == 'post.php' && isset( $_POST['publish'] ) ) { |
| 1263 |
// Set the post_status as 'pending' only when there's no timestamp set for $post_date_gmt |
| 1264 |
if ( isset( $_POST['post_ID'] ) ) { |
| 1265 |
$post_id = (int) $_POST['post_ID']; |
| 1266 |
$ret = $wpdb->update( $wpdb->posts, array( 'post_status' => 'pending' ), array( 'ID' => $post_id, 'post_date_gmt' => '0000-00-00 00:00:00' ) ); |
| 1267 |
clean_post_cache( $post_id ); |
| 1268 |
foreach ( array('aa', 'mm', 'jj', 'hh', 'mn') as $timeunit ) { |
| 1269 |
if ( !empty( $_POST['hidden_' . $timeunit] ) && $_POST['hidden_' . $timeunit] != $_POST[$timeunit] ) { |
| 1270 |
$edit_date = '1'; |
| 1271 |
break; |
| 1272 |
} |
| 1273 |
} |
| 1274 |
if ( $ret && empty( $edit_date ) ) { |
| 1275 |
add_filter( 'pre_post_date', array( $this, 'helper_timestamp_hack' ) ); |
| 1276 |
add_filter( 'pre_post_date_gmt', array( $this, 'helper_timestamp_hack' ) ); |
| 1277 |
} |
| 1278 |
} |
| 1279 |
} |
| 1280 |
|
| 1281 |
} |
| 1282 |
/** |
| 1283 |
* PHP < 5.3.x doesn't support anonymous functions |
| 1284 |
* This helper is only used for the check_timestamp_on_publish method above |
| 1285 |
* |
| 1286 |
* @since 0.7.3 |
| 1287 |
*/ |
| 1288 |
function helper_timestamp_hack() { |
| 1289 |
return ( 'pre_post_date' == current_filter() ) ? current_time('mysql') : ''; |
| 1290 |
} |
| 1291 |
/** |
| 1292 |
* This is a hack! hack! hack! until core is fixed/better supports custom statuses |
| 1293 |
* |
| 1294 |
* @since 0.6.5 |
| 1295 |
* |
| 1296 |
* Normalize post_date_gmt if it isn't set to the past or the future |
| 1297 |
* @see Works around this limitation: http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/post.php#L2506 |
| 1298 |
* @see Original thread: http://wordpress.org/support/topic/plugin-edit-flow-custom-statuses-create-timestamp-problem |
| 1299 |
* @see Core ticket: http://core.trac.wordpress.org/ticket/18362 |
| 1300 |
*/ |
| 1301 |
function fix_custom_status_timestamp( $data ) { |
| 1302 |
global $edit_flow; |
| 1303 |
// Don't run this if Edit Flow isn't active, or we're on some other page |
| 1304 |
if ( $this->disable_custom_statuses_for_post_type() |
| 1305 |
|| ! isset( $edit_flow ) |
| 1306 |
|| ! isset( $_POST ) ) |
| 1307 |
return $data; |
| 1308 |
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' ); |
| 1309 |
$ef_normalize_post_date_gmt = true; |
| 1310 |
// We're only going to normalize the post_date_gmt if the user hasn't set a custom date in the metabox |
| 1311 |
// and the current post_date_gmt isn't already future or past-ized |
| 1312 |
foreach ( array('aa', 'mm', 'jj', 'hh', 'mn') as $timeunit ) { |
| 1313 |
if ( !empty( $_POST['hidden_' . $timeunit] ) && (($_POST['hidden_' . $timeunit] != $_POST[$timeunit] ) || ( $_POST['hidden_' . $timeunit] != $_POST['cur_' . $timeunit] )) ) { |
| 1314 |
$ef_normalize_post_date_gmt = false; |
| 1315 |
break; |
| 1316 |
} |
| 1317 |
} |
| 1318 |
if ( $ef_normalize_post_date_gmt ) |
| 1319 |
if ( in_array( $data['post_status'], $status_slugs ) ) |
| 1320 |
$data['post_date_gmt'] = '0000-00-00 00:00:00'; |
| 1321 |
return $data; |
| 1322 |
} |
| 1323 |
|
| 1324 |
/** |
| 1325 |
* Another hack! hack! hack! until core better supports custom statuses |
| 1326 |
* |
| 1327 |
* @since 0.7.4 |
| 1328 |
* |
| 1329 |
* Keep the post_name value empty for posts with custom statuses |
| 1330 |
* Unless they've set it customly |
| 1331 |
* @see https://github.com/danielbachhuber/Edit-Flow/issues/123 |
| 1332 |
* @see http://core.trac.wordpress.org/browser/tags/3.4.2/wp-includes/post.php#L2530 |
| 1333 |
* @see http://core.trac.wordpress.org/browser/tags/3.4.2/wp-includes/post.php#L2646 |
| 1334 |
*/ |
| 1335 |
public function fix_post_name( $post_id, $post ) { |
| 1336 |
global $pagenow; |
| 1337 |
|
| 1338 |
// Only modify if we're using a pre-publish status on a supported custom post type |
| 1339 |
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' ); |
| 1340 |
if ( 'post.php' != $pagenow |
| 1341 |
|| ! in_array( $post->post_status, $status_slugs ) |
| 1342 |
|| ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) ) |
| 1343 |
return; |
| 1344 |
|
| 1345 |
// The slug has been set by the meta box |
| 1346 |
if ( ! empty( $_POST['post_name'] ) ) |
| 1347 |
return; |
| 1348 |
|
| 1349 |
global $wpdb; |
| 1350 |
|
| 1351 |
$wpdb->update( $wpdb->posts, array( 'post_name' => '' ), array( 'ID' => $post_id ) ); |
| 1352 |
clean_post_cache( $post_id ); |
| 1353 |
} |
| 1354 |
/** |
| 1355 |
* Another hack! hack! hack! until core better supports custom statuses |
| 1356 |
* |
| 1357 |
* @since 0.7.4 |
| 1358 |
* |
| 1359 |
* If the post_name for the object is empty, set it to sanitize_title() of post_title |
| 1360 |
* However, we only want to do this the first time it's called on the page (slug editor) |
| 1361 |
* and not in the post metabox, because the latter will set the value |
| 1362 |
*/ |
| 1363 |
public function fix_editable_slug( $slug ) { |
| 1364 |
global $pagenow; |
| 1365 |
|
| 1366 |
// If 'all_admin_notices' hasn't happened yet, we're not even close to the UI yet. |
| 1367 |
if ( ! did_action( 'all_admin_notices' ) ) |
| 1368 |
return $slug; |
| 1369 |
|
| 1370 |
// The AJAX-y edit your slug UI happens right below the title, |
| 1371 |
// so we can remove the filter after it's done. |
| 1372 |
if ( did_action( 'edit_form_after_title' ) ) { |
| 1373 |
remove_filter( current_filter(), array( $this, __FUNCTION__ ) ); |
| 1374 |
return $slug; |
| 1375 |
} |
| 1376 |
|
| 1377 |
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) |
| 1378 |
return $slug; |
| 1379 |
|
| 1380 |
$post = get_post(); |
| 1381 |
|
| 1382 |
// Only modify if we're using a pre-publish status on a supported custom post type |
| 1383 |
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' ); |
| 1384 |
if ( ! $post |
| 1385 |
|| 'post.php' != $pagenow |
| 1386 |
|| ! in_array( $post->post_status, $status_slugs ) |
| 1387 |
|| ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) |
| 1388 |
|| ! empty( $post->post_name ) ) |
| 1389 |
return $slug; |
| 1390 |
|
| 1391 |
// For hierarchical post types, we only want to modify the last time 'editable_slug' filter runs. |
| 1392 |
$ptype = get_post_type_object( $post->post_type ); |
| 1393 |
if ( $ptype->hierarchical ) { |
| 1394 |
static $i; |
| 1395 |
$i++; |
| 1396 |
if ( $i > 1 ) |
| 1397 |
return sanitize_title( $post->post_title ); |
| 1398 |
else |
| 1399 |
return $slug; |
| 1400 |
} |
| 1401 |
|
| 1402 |
$slug = sanitize_title( $post->post_title ); |
| 1403 |
|
| 1404 |
return $slug; |
| 1405 |
|
| 1406 |
} |
| 1407 |
|
| 1408 |
/** |
| 1409 |
* Another hack! hack! hack! until core better supports custom statuses |
| 1410 |
* |
| 1411 |
* @since 0.7.4 |
| 1412 |
* |
| 1413 |
* The preview link for an unpublished post should always be ?p= |
| 1414 |
*/ |
| 1415 |
public function fix_preview_link_part_one( $preview_link ) { |
| 1416 |
global $pagenow; |
| 1417 |
|
| 1418 |
$post = get_post(); |
| 1419 |
|
| 1420 |
// Only modify if we're using a pre-publish status on a supported custom post type |
| 1421 |
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' ); |
| 1422 |
if ( ! $post |
| 1423 |
|| ! is_admin() |
| 1424 |
|| 'post.php' != $pagenow |
| 1425 |
|| ! in_array( $post->post_status, $status_slugs ) |
| 1426 |
|| ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) ) |
| 1427 |
return $preview_link; |
| 1428 |
|
| 1429 |
if ( 'page' == $post->post_type ) { |
| 1430 |
$args = array( |
| 1431 |
'page_id' => $post->ID, |
| 1432 |
); |
| 1433 |
} else if ( 'post' == $post->post_type ) { |
| 1434 |
$args = array( |
| 1435 |
'p' => $post->ID, |
| 1436 |
); |
| 1437 |
} else { |
| 1438 |
$args = array( |
| 1439 |
'p' => $post->ID, |
| 1440 |
'post_type' => $post->post_type, |
| 1441 |
); |
| 1442 |
} |
| 1443 |
$args['preview'] = 'true'; |
| 1444 |
$preview_link = add_query_arg( $args, home_url() ); |
| 1445 |
return $preview_link; |
| 1446 |
} |
| 1447 |
|
| 1448 |
/** |
| 1449 |
* Another hack! hack! hack! until core better supports custom statuses |
| 1450 |
* |
| 1451 |
* @since 0.7.4 |
| 1452 |
* |
| 1453 |
* The preview link for an unpublished post should always be ?p= |
| 1454 |
* The code used to trigger a post preview doesn't also apply the 'preview_post_link' filter |
| 1455 |
* So we can't do a targeted filter. Instead, we can even more hackily filter get_permalink |
| 1456 |
* @see http://core.trac.wordpress.org/ticket/19378 |
| 1457 |
*/ |
| 1458 |
public function fix_preview_link_part_two( $permalink, $post, $leavename ) { |
| 1459 |
global $pagenow; |
| 1460 |
|
| 1461 |
// Core has apply_filters( 'page_link', $link, $post->ID, $sample ); too :( |
| 1462 |
if ( is_int( $post ) ) |
| 1463 |
$post = get_post( $post ); |
| 1464 |
|
| 1465 |
// Only modify if we're using a pre-publish status on a supported custom post type |
| 1466 |
// while doing the preview POST action |
| 1467 |
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' ); |
| 1468 |
if ( ! is_admin() |
| 1469 |
|| 'post.php' != $pagenow |
| 1470 |
|| ( empty( $_POST['wp-preview'] ) || 'dopreview' != $_POST['wp-preview'] ) |
| 1471 |
|| ! in_array( $post->post_status, $status_slugs ) |
| 1472 |
|| ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) ) |
| 1473 |
return $permalink; |
| 1474 |
|
| 1475 |
if ( 'page' == $post->post_type ) { |
| 1476 |
$args = array( |
| 1477 |
'page_id' => $post->ID, |
| 1478 |
); |
| 1479 |
} else if ( 'post' == $post->post_type ) { |
| 1480 |
$args = array( |
| 1481 |
'p' => $post->ID, |
| 1482 |
); |
| 1483 |
} else { |
| 1484 |
$args = array( |
| 1485 |
'p' => $post->ID, |
| 1486 |
'post_type' => $post->post_type, |
| 1487 |
); |
| 1488 |
} |
| 1489 |
$permalink = add_query_arg( $args, home_url() ); |
| 1490 |
return $permalink; |
| 1491 |
} |
| 1492 |
|
| 1493 |
/** |
| 1494 |
* Another hack! hack! hack! until core better supports custom statuses |
| 1495 |
* |
| 1496 |
* @since 0.7.4 |
| 1497 |
* |
| 1498 |
* The preview link for an unpublished post should always be ?p=, even in the list table |
| 1499 |
* @see http://core.trac.wordpress.org/ticket/19378 |
| 1500 |
*/ |
| 1501 |
public function fix_post_row_actions( $actions, $post ) { |
| 1502 |
global $pagenow; |
| 1503 |
|
| 1504 |
// Only modify if we're using a pre-publish status on a supported custom post type |
| 1505 |
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' ); |
| 1506 |
if ( 'edit.php' != $pagenow |
| 1507 |
|| ! in_array( $post->post_status, $status_slugs ) |
| 1508 |
|| ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) ) |
| 1509 |
return $actions; |
| 1510 |
|
| 1511 |
// 'view' is only set if the user has permission to post |
| 1512 |
if ( empty( $actions['view'] ) ) |
| 1513 |
return $actions; |
| 1514 |
|
| 1515 |
if ( 'page' == $post->post_type ) { |
| 1516 |
$args = array( |
| 1517 |
'page_id' => $post->ID, |
| 1518 |
); |
| 1519 |
} else if ( 'post' == $post->post_type ) { |
| 1520 |
$args = array( |
| 1521 |
'p' => $post->ID, |
| 1522 |
); |
| 1523 |
} else { |
| 1524 |
$args = array( |
| 1525 |
'p' => $post->ID, |
| 1526 |
'post_type' => $post->post_type, |
| 1527 |
); |
| 1528 |
} |
| 1529 |
$args['preview'] = 'true'; |
| 1530 |
$preview_link = add_query_arg( $args, home_url() ); |
| 1531 |
|
| 1532 |
$actions['view'] = '<a href="' . esc_url( $preview_link ) . '" title="' . esc_attr( sprintf( __( 'Preview “%s”' ), $post->post_title ) ) . '" rel="permalink">' . __( 'Preview' ) . '</a>'; |
| 1533 |
return $actions; |
| 1534 |
} |
| 1535 |
} |
| 1536 |
|
| 1537 |
} |
| 1538 |
|
| 1539 |
/** |
| 1540 |
* Custom Statuses uses WordPress' List Table API for generating the custom status management table |
| 1541 |
* |
| 1542 |
* @since 0.7 |
| 1543 |
*/ |
| 1544 |
class EF_Custom_Status_List_Table extends WP_List_Table |
| 1545 |
{ |
| 1546 |
|
| 1547 |
var $callback_args; |
| 1548 |
var $default_status; |
| 1549 |
|
| 1550 |
/** |
| 1551 |
* Construct the extended class |
| 1552 |
*/ |
| 1553 |
function __construct() { |
| 1554 |
|
| 1555 |
parent::__construct( array( |
| 1556 |
'plural' => 'custom statuses', |
| 1557 |
'singular' => 'custom status', |
| 1558 |
'ajax' => true |
| 1559 |
) ); |
| 1560 |
|
| 1561 |
} |
| 1562 |
|
| 1563 |
/** |
| 1564 |
* Pull in the data we'll be displaying on the table |
| 1565 |
* |
| 1566 |
* @since 0.7 |
| 1567 |
*/ |
| 1568 |
function prepare_items() { |
| 1569 |
global $edit_flow; |
| 1570 |
|
| 1571 |
$columns = $this->get_columns(); |
| 1572 |
$hidden = array( |
| 1573 |
'position', |
| 1574 |
); |
| 1575 |
$sortable = array(); |
| 1576 |
$this->_column_headers = array($columns, $hidden, $sortable); |
| 1577 |
|
| 1578 |
$this->items = $edit_flow->custom_status->get_custom_statuses(); |
| 1579 |
$total_items = count( $this->items ); |
| 1580 |
$this->default_status = $edit_flow->custom_status->get_default_custom_status()->slug; |
| 1581 |
|
| 1582 |
$this->set_pagination_args( array( |
| 1583 |
'total_items' => $total_items, |
| 1584 |
'per_page' => $total_items, |
| 1585 |
) ); |
| 1586 |
} |
| 1587 |
|
| 1588 |
/** |
| 1589 |
* Message to be displayed when there are no custom statuses. Should never be displayed, but we'll customize it |
| 1590 |
* just in case. |
| 1591 |
* |
| 1592 |
* @since 0.7 |
| 1593 |
*/ |
| 1594 |
function no_items() { |
| 1595 |
_e( 'No custom statuses found.', 'edit-flow' ); |
| 1596 |
} |
| 1597 |
|
| 1598 |
/** |
| 1599 |
* Table shows (hidden) position, status name, status description, and the post count for each activated |
| 1600 |
* post type |
| 1601 |
* |
| 1602 |
* @since 0.7 |
| 1603 |
* |
| 1604 |
* @return array $columns Columns to be registered with the List Table |
| 1605 |
*/ |
| 1606 |
function get_columns() { |
| 1607 |
global $edit_flow; |
| 1608 |
|
| 1609 |
$columns = array( |
| 1610 |
'position' => __( 'Position', 'edit-flow' ), |
| 1611 |
'name' => __( 'Name', 'edit-flow' ), |
| 1612 |
'description' => __( 'Description', 'edit-flow' ), |
| 1613 |
); |
| 1614 |
|
| 1615 |
$post_types = get_post_types( '', 'objects' ); |
| 1616 |
$supported_post_types = $edit_flow->helpers->get_post_types_for_module( $edit_flow->custom_status->module ); |
| 1617 |
foreach ( $post_types as $post_type ) |
| 1618 |
if ( in_array( $post_type->name, $supported_post_types ) ) |
| 1619 |
$columns[$post_type->name] = $post_type->label; |
| 1620 |
|
| 1621 |
return $columns; |
| 1622 |
} |
| 1623 |
|
| 1624 |
/** |
| 1625 |
* Fallback column callback. |
| 1626 |
* Primarily used to display post count for each post type |
| 1627 |
* |
| 1628 |
* @since 0.7 |
| 1629 |
* |
| 1630 |
* @param object $item Custom status as an object |
| 1631 |
* @param string $column_name Name of the column as registered in $this->prepare_items() |
| 1632 |
* @return string $output What will be rendered |
| 1633 |
*/ |
| 1634 |
function column_default( $item, $column_name ) { |
| 1635 |
global $edit_flow; |
| 1636 |
|
| 1637 |
// Handle custom post counts for different post types |
| 1638 |
$post_types = get_post_types( '', 'names' ); |
| 1639 |
if ( in_array( $column_name, $post_types ) ) { |
| 1640 |
|
| 1641 |
// @todo Cachify this |
| 1642 |
$post_count = wp_cache_get( "ef_custom_status_count_$column_name" ); |
| 1643 |
if ( false === $post_count ) { |
| 1644 |
$posts = wp_count_posts( $column_name ); |
| 1645 |
$post_status = $item->slug; |
| 1646 |
// To avoid error notices when changing the name of non-standard statuses |
| 1647 |
if ( isset( $posts->$post_status ) ) |
| 1648 |
$post_count = $posts->$post_status; |
| 1649 |
else |
| 1650 |
$post_count = 0; |
| 1651 |
//wp_cache_set( "ef_custom_status_count_$column_name", $post_count ); |
| 1652 |
} |
| 1653 |
$output = sprintf( '<a title="See all %1$ss saved as \'%2$s\'" href="%3$s">%4$s</a>', $column_name, $item->name, $edit_flow->helpers->filter_posts_link( $item->slug, $column_name ), $post_count ); |
| 1654 |
return $output; |
| 1655 |
} |
| 1656 |
|
| 1657 |
} |
| 1658 |
|
| 1659 |
/** |
| 1660 |
* Hidden column for storing the status position |
| 1661 |
* |
| 1662 |
* @since 0.7 |
| 1663 |
* |
| 1664 |
* @param object $item Custom status as an object |
| 1665 |
* @return string $output What will be rendered |
| 1666 |
*/ |
| 1667 |
function column_position( $item ) { |
| 1668 |
return esc_html( $item->position ); |
| 1669 |
} |
| 1670 |
|
| 1671 |
/** |
| 1672 |
* Displayed column showing the name of the status |
| 1673 |
* |
| 1674 |
* @since 0.7 |
| 1675 |
* |
| 1676 |
* @param object $item Custom status as an object |
| 1677 |
* @return string $output What will be rendered |
| 1678 |
*/ |
| 1679 |
function column_name( $item ) { |
| 1680 |
global $edit_flow; |
| 1681 |
|
| 1682 |
$item_edit_link = esc_url( $edit_flow->custom_status->get_link( array( 'action' => 'edit-status', 'term-id' => $item->term_id ) ) ); |
| 1683 |
|
| 1684 |
$output = '<strong><a href="' . $item_edit_link . '">' . esc_html( $item->name ) . '</a>'; |
| 1685 |
if ( $item->slug == $this->default_status ) |
| 1686 |
$output .= ' - ' . __( 'Default', 'edit-flow' ); |
| 1687 |
$output .= '</strong>'; |
| 1688 |
|
| 1689 |
// Don't allow for any of these status actions when adding a new custom status |
| 1690 |
if ( isset( $_GET['action'] ) && $_GET['action'] == 'add' ) |
| 1691 |
return $output; |
| 1692 |
|
| 1693 |
$actions = array(); |
| 1694 |
$actions['edit'] = "<a href='$item_edit_link'>" . __( 'Edit', 'edit-flow' ) . "</a>"; |
| 1695 |
$actions['inline hide-if-no-js'] = '<a href="#" class="editinline">' . __( 'Quick Edit' ) . '</a>'; |
| 1696 |
if ( $item->slug != $this->default_status ) |
| 1697 |
$actions['make_default'] = sprintf( '<a href="%1$s">' . __( 'Make Default', 'edit-flow' ) . '</a>', $edit_flow->custom_status->get_link( array( 'action' => 'make-default', 'term-id' => $item->term_id ) ) ); |
| 1698 |
|
| 1699 |
if ( $item->slug != $this->default_status ) |
| 1700 |
$actions['delete delete-status'] = sprintf( '<a href="%1$s">' . __( 'Delete', 'edit-flow' ) . '</a>', $edit_flow->custom_status->get_link( array( 'action' => 'delete-status', 'term-id' => $item->term_id ) ) ); |
| 1701 |
|
| 1702 |
$output .= $this->row_actions( $actions, false ); |
| 1703 |
$output .= '<div class="hidden" id="inline_' . esc_attr( $item->term_id ) . '">'; |
| 1704 |
$output .= '<div class="name">' . esc_html( $item->name ) . '</div>'; |
| 1705 |
$output .= '<div class="description">' . esc_html( $item->description ) . '</div>'; |
| 1706 |
$output .= '</div>'; |
| 1707 |
|
| 1708 |
return $output; |
| 1709 |
|
| 1710 |
} |
| 1711 |
|
| 1712 |
/** |
| 1713 |
* Displayed column showing the description of the status |
| 1714 |
* |
| 1715 |
* @since 0.7 |
| 1716 |
* |
| 1717 |
* @param object $item Custom status as an object |
| 1718 |
* @return string $output What will be rendered |
| 1719 |
*/ |
| 1720 |
function column_description( $item ) { |
| 1721 |
return esc_html( $item->description ); |
| 1722 |
} |
| 1723 |
|
| 1724 |
/** |
| 1725 |
* Prepare and echo a single custom status row |
| 1726 |
* |
| 1727 |
* @since 0.7 |
| 1728 |
*/ |
| 1729 |
function single_row( $item ) { |
| 1730 |
static $alternate_class = ''; |
| 1731 |
$alternate_class = ( $alternate_class == '' ? ' alternate' : '' ); |
| 1732 |
$row_class = ' class="term-static' . $alternate_class . '"'; |
| 1733 |
|
| 1734 |
echo '<tr id="term-' . $item->term_id . '"' . $row_class . '>'; |
| 1735 |
echo $this->single_row_columns( $item ); |
| 1736 |
echo '</tr>'; |
| 1737 |
} |
| 1738 |
|
| 1739 |
/** |
| 1740 |
* Hidden form used for inline editing functionality |
| 1741 |
* |
| 1742 |
* @since 0.7 |
| 1743 |
*/ |
| 1744 |
function inline_edit() { |
| 1745 |
global $edit_flow; |
| 1746 |
?> |
| 1747 |
<form method="get" action=""><table style="display: none"><tbody id="inlineedit"> |
| 1748 |
<tr id="inline-edit" class="inline-edit-row" style="display: none"><td colspan="<?php echo $this->get_column_count(); ?>" class="colspanchange"> |
| 1749 |
<fieldset><div class="inline-edit-col"> |
| 1750 |
<h4><?php _e( 'Quick Edit' ); ?></h4> |
| 1751 |
<label> |
| 1752 |
<span class="title"><?php _e( 'Name', 'edit-flow' ); ?></span> |
| 1753 |
<span class="input-text-wrap"><input type="text" name="name" class="ptitle" value="" maxlength="20" /></span> |
| 1754 |
</label> |
| 1755 |
<label> |
| 1756 |
<span class="title"><?php _e( 'Description', 'edit-flow' ); ?></span> |
| 1757 |
<span class="input-text-wrap"><input type="text" name="description" class="pdescription" value="" /></span> |
| 1758 |
</label> |
| 1759 |
</div></fieldset> |
| 1760 |
<p class="inline-edit-save submit"> |
| 1761 |
<a accesskey="c" href="#inline-edit" title="<?php _e( 'Cancel' ); ?>" class="cancel button-secondary alignleft"><?php _e( 'Cancel' ); ?></a> |
| 1762 |
<?php $update_text = __( 'Update Status', 'edit-flow' ); ?> |
| 1763 |
<a accesskey="s" href="#inline-edit" title="<?php echo esc_attr( $update_text ); ?>" class="save button-primary alignright"><?php echo $update_text; ?></a> |
| 1764 |
<img class="waiting" style="display:none;" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" /> |
| 1765 |
<span class="error" style="display:none;"></span> |
| 1766 |
<?php wp_nonce_field( 'custom-status-inline-edit-nonce', 'inline_edit', false ); ?> |
| 1767 |
<br class="clear" /> |
| 1768 |
</p> |
| 1769 |
</td></tr> |
| 1770 |
</tbody></table></form> |
| 1771 |
<?php |
| 1772 |
} |
| 1773 |
|
| 1774 |
} |
| 1775 |
|