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