PluginProbe
Edit Flow / 0.7.5
Edit Flow v0.7.5
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
edit-flow / modules / custom-status / custom-status.php

custom-status.php in Edit Flow 0.7.5, at modules/custom-status/custom-status.php

1,775 lines 66.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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( __( "&mdash; No Change &mdash;" ) ); ?>';
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&#39;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 &#8220;%s&#8221;' ), $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&nbsp;Edit' ) . '</a>';
1696 if ( $item->slug != $this->default_status )
1697 $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 ) ) );
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