PluginProbe
Edit Flow / 0.7.2
Edit Flow v0.7.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
edit-flow / modules / editorial-metadata / editorial-metadata.php

editorial-metadata.php in Edit Flow 0.7.2, at modules/editorial-metadata/editorial-metadata.php

1,697 lines 66.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class EF_Editorial_Metadata
4 * This class gives publishers arbitrary structured content details to go along with every post
5 *
6 * @author sbressler, danielbachhuber
7 *
8 * Ways to test and play with this class:
9 * 1) Create a new term by selecting Editorial Metadata from the Edit Flow settings
10 * 2) Edit an existing term (slug, description, etc.)
11 * 3) Create a post and assign metadata to it
12 * 4) Look at the list of terms again - the count should go up!
13 * 5) Play with adding more metadata to a post
14 * 6) Clear the metadata for a single term in a post and watch the count go down!
15 * 6) Delete a term and note the metadata disappears from posts
16 * 7) Re-add the term (same slug) and the metadata returns!
17 *
18 * Improvements to make:
19 * @todo Abstract the permissions check for management to class level
20 */
21 if ( !class_exists('EF_Editorial_Metadata') ) {
22
23 class EF_Editorial_Metadata extends EF_Module {
24
25 /**
26 * The name of the taxonomy we're going to register for editorial metadata.
27 */
28 const metadata_taxonomy = 'ef_editorial_meta';
29 const metadata_postmeta_key = "_ef_editorial_meta";
30
31 var $module_name = 'editorial_metadata';
32
33 /**
34 * Construct the EF_Editorial_Metadata class
35 */
36 function __construct() {
37 global $edit_flow;
38
39 $this->module_url = $this->get_module_url( __FILE__ );
40 // Register the module with Edit Flow
41 $args = array(
42 'title' => __( 'Editorial Metadata', 'edit-flow' ),
43 'short_description' => __( 'Track details about your posts in progress.', 'edit-flow' ),
44 'extended_description' => __( 'Log details on every assignment using configurable editorial metadata. It’s completely customizable; create fields for everything from due date to location to contact information to role assignments.', 'edit-flow' ),
45 'module_url' => $this->module_url,
46 'img_url' => $this->module_url . 'lib/editorial_metadata_s128.png',
47 'slug' => 'editorial-metadata',
48 'default_options' => array(
49 'enabled' => 'on',
50 'post_types' => array(
51 'post' => 'on',
52 'page' => 'off',
53 ),
54 ),
55 'messages' => array(
56 'term-added' => __( "Metadata term added.", 'edit-flow' ),
57 'term-updated' => __( "Metadata term updated.", 'edit-flow' ),
58 'term-missing' => __( "Metadata term doesn't exist.", 'edit-flow' ),
59 'term-deleted' => __( "Metadata term deleted.", 'edit-flow' ),
60 'term-position-updated' => __( "Term order updated.", 'edit-flow' ),
61 'term-visibility-changed' => __( "Term visibility changed.", 'edit-flow' ),
62 ),
63 'configure_page_cb' => 'print_configure_view',
64 'settings_help_tab' => array(
65 'id' => 'ef-editorial-metadata-overview',
66 'title' => __('Overview', 'edit-flow'),
67 'content' => __('<p>Keep track of important details about your content with editorial metadata. This feature allows you to create as many date, text, number, etc. fields as you like, and then use them to store information like contact details, required word count, or the location of an interview.</p><p>Once you’ve set your fields up, editorial metadata integrates with both the calendar and the story budget. Make an editorial metadata item visible to have it appear to the rest of your team. Keep it hidden to restrict the information between the writer and their editor.</p>', 'edit-flow'),
68 ),
69 'settings_help_sidebar' => __( '<p><strong>For more information:</strong></p><p><a href="http://editflow.org/features/editorial-metadata/">Editorial Metadata 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' ),
70 );
71 $edit_flow->register_module( $this->module_name, $args );
72
73
74 }
75
76 /**
77 * Initialize the module. Conditionally loads if the module is enabled
78 */
79 function init() {
80
81 // Register the taxonomy we use for Editorial Metadata with WordPress core
82 $this->register_taxonomy();
83
84 // Register our settings
85 add_action( 'admin_init', array( $this, 'register_settings' ) );
86
87 // Actions relevant to the configuration view (adding, editing, or sorting existing Editorial Metadata)
88 add_action( 'admin_init', array( $this, 'handle_add_editorial_metadata' ) );
89 add_action( 'admin_init', array( $this, 'handle_edit_editorial_metadata' ) );
90 add_action( 'admin_init', array( $this, 'handle_change_editorial_metadata_visibility' ) );
91 add_action( 'admin_init', array( $this, 'handle_delete_editorial_metadata' ) );
92 add_action( 'wp_ajax_inline_save_term', array( $this, 'handle_ajax_inline_save_term' ) );
93 add_action( 'wp_ajax_update_term_positions', array( $this, 'handle_ajax_update_term_positions' ) );
94
95 add_action( 'add_meta_boxes', array( $this, 'handle_post_metaboxes' ) );
96 add_action( 'save_post', array( $this, 'save_meta_box' ), 10, 2 );
97
98 // Add Editorial Metadata columns to the Manage Posts view
99 $supported_post_types = $this->get_post_types_for_module( $this->module );
100 foreach( $supported_post_types as $post_type ) {
101 add_action( "manage_{$post_type}_posts_columns", array( $this, 'filter_manage_posts_columns' ) );
102 add_action( 'manage_posts_custom_column', array( $this, 'action_manage_posts_custom_column' ), 10, 2 );
103 }
104
105 // Add Editorial Metadata to the calendar if the calendar is activated
106 if ( $this->module_enabled( 'calendar' ) )
107 add_filter( 'ef_calendar_item_information_fields', array( $this, 'filter_calendar_item_fields' ), 10, 2 );
108
109 // Add Editorial Metadata columns to the Story Budget if it exists
110 if ( $this->module_enabled( 'story_budget' ) ) {
111 add_filter( 'ef_story_budget_term_columns', array( $this, 'filter_story_budget_term_columns' ) );
112 // Register an action to handle this data later
113 add_filter( 'ef_story_budget_term_column_value', array( $this, 'filter_story_budget_term_column_values' ), 10, 3 );
114 }
115
116 // Load necessary scripts and stylesheets
117 add_action( 'admin_enqueue_scripts', array( $this, 'add_admin_scripts' ) );
118
119 }
120
121 /**
122 * Load default editorial metadata the first time the module is loaded
123 *
124 * @since 0.7
125 */
126 function install() {
127 // Our default metadata fields
128 $default_metadata = array(
129 array(
130 'name' => __( 'First Draft Date', 'edit-flow' ),
131 'slug' => 'first-draft-date',
132 'type' => 'date',
133 'description' => __( 'When the first draft needs to be ready.', 'edit-flow' ),
134 ),
135 array(
136 'name' => __( 'Assignment', 'edit-flow' ),
137 'slug' => 'assignment',
138 'type' => 'paragraph',
139 'description' => __( 'What the post needs to cover.', 'edit-flow' ),
140 ),
141 array(
142 'name' => __( 'Needs Photo', 'edit-flow' ),
143 'slug' => 'needs-photo',
144 'type' => 'checkbox',
145 'description' => __( 'Checked if this post needs a photo.', 'edit-flow' ),
146 ),
147 array(
148 'name' => __( 'Word Count', 'edit-flow' ),
149 'slug' => 'word-count',
150 'type' => 'number',
151 'description' => __( 'Required post length in words.', 'edit-flow' ),
152 ),
153 );
154 // Load the metadata fields if the slugs don't conflict
155 foreach ( $default_metadata as $args ) {
156 if ( !term_exists( $args['slug'], self::metadata_taxonomy ) ) {
157 $this->insert_editorial_metadata_term( $args );
158 }
159 }
160 }
161
162 /**
163 * Upgrade our data in case we need to
164 *
165 * @since 0.7
166 */
167 function upgrade( $previous_version ) {
168 global $edit_flow;
169
170 // Upgrade path to v0.7
171 if ( version_compare( $previous_version, '0.7' , '<' ) ) {
172 // Technically we've run this code before so we don't want to auto-install new data
173 $edit_flow->update_module_option( $this->module->name, 'loaded_once', true );
174 }
175
176 }
177
178 /**
179 * Generate <select> HTML for all of the metadata types
180 */
181 function get_select_html( $description ) {
182 $current_metadata_type = $description->type;
183 $metadata_types = $this->get_supported_metadata_types();
184 ?>
185 <select id="<?php echo self::metadata_taxonomy; ?>'_type" name="<?php echo self::metadata_taxonomy; ?>'_type">
186 <?php foreach ( $metadata_types as $metadata_type => $metadata_type_name ) : ?>
187 <option value="<?php echo $metadata_type; ?>" <?php selected( $metadata_type, $current_metadata_type ); ?>><?php echo $metadata_type_name; ?></option>
188 <?php endforeach; ?>
189 </select>
190 <?php
191 }
192
193 /**
194 * Prepare an array of supported editorial metadata types
195 *
196 * @return array $supported_metadata_types All of the supported metadata
197 */
198 function get_supported_metadata_types() {
199 $supported_metadata_types = array(
200 'checkbox' => __('Checkbox', 'edit-flow'),
201 'date' => __('Date', 'edit-flow'),
202 'location' => __('Location', 'edit-flow'),
203 'number' => __('Number', 'edit-flow'),
204 'paragraph' => __('Paragraph', 'edit-flow'),
205 'text' => __('Text', 'edit-flow'),
206 'user' => __('User', 'edit-flow'),
207 );
208 return $supported_metadata_types;
209 }
210
211 /**
212 * Enqueue relevant admin Javascript
213 */
214 function add_admin_scripts() {
215 global $current_screen, $pagenow;
216
217 // Add the metabox date picker JS and CSS
218 $current_post_type = $this->get_current_post_type();
219 $supported_post_types = $this->get_post_types_for_module( $this->module );
220 if ( in_array( $current_post_type, $supported_post_types ) ) {
221 $this->enqueue_datepicker_resources();
222
223 // Now add the rest of the metabox CSS
224 wp_enqueue_style( 'edit_flow-editorial_metadata-styles', $this->module_url . 'lib/editorial-metadata.css', false, EDIT_FLOW_VERSION, 'all' );
225 }
226 // A bit of custom CSS for the Manage Posts view if we have viewable metadata
227 if ( $current_screen->base == 'edit' && in_array( $current_post_type, $supported_post_types ) ) {
228 $terms = $this->get_editorial_metadata_terms();
229 $viewable_terms = array();
230 foreach( $terms as $term ) {
231 if ( $term->viewable )
232 $viewable_terms[] = $term;
233 }
234 if ( !empty( $viewable_terms ) ) {
235 $css_rules = array(
236 '.wp-list-table.fixed .column-author' => array(
237 'min-width: 7em;',
238 'width: auto;',
239 ),
240 '.wp-list-table.fixed .column-tags' => array(
241 'min-width: 7em;',
242 'width: auto;',
243 ),
244 '.wp-list-table.fixed .column-categories' => array(
245 'min-width: 7em;',
246 'width: auto;',
247 ),
248 );
249 foreach( $viewable_terms as $viewable_term ) {
250 switch( $viewable_term->type ) {
251 case 'checkbox':
252 case 'number':
253 case 'date':
254 $css_rules['.wp-list-table.fixed .column-' . $this->module->slug . '-' . $viewable_term->slug] = array(
255 'min-width: 6em;',
256 );
257 break;
258 case 'location':
259 case 'text':
260 case 'user':
261 $css_rules['.wp-list-table.fixed .column-' . $this->module->slug . '-' . $viewable_term->slug] = array(
262 'min-width: 7em;',
263 );
264 break;
265 case 'paragraph':
266 $css_rules['.wp-list-table.fixed .column-' . $this->module->slug . '-' . $viewable_term->slug] = array(
267 'min-width: 8em;',
268 );
269 break;
270 }
271 }
272 // Allow users to filter out rules if there's something wonky
273 $css_rules = apply_filters( 'ef_editorial_metadata_manage_posts_css_rules', $css_rules );
274 echo "<style type=\"text/css\">\n";
275 foreach( (array)$css_rules as $css_property => $rules ) {
276 echo $css_property . " {" . implode( ' ', $rules ) . "}\n";
277 }
278 echo '</style>';
279 }
280
281 }
282
283 // Load Javascript specific to the editorial metadata configuration view
284 if ( $this->is_whitelisted_settings_view( $this->module->name ) ) {
285 wp_enqueue_script( 'jquery-ui-sortable' );
286 wp_enqueue_script( 'edit-flow-editorial-metadata-configure', EDIT_FLOW_URL . 'modules/editorial-metadata/lib/editorial-metadata-configure.js', array( 'jquery', 'jquery-ui-sortable', 'edit-flow-settings-js' ), EDIT_FLOW_VERSION, true );
287 }
288 }
289
290 /**
291 * Register the post metadata taxonomy
292 */
293 function register_taxonomy() {
294
295 // We need to make sure taxonomy is registered for all of the post types that support it
296 $supported_post_types = $this->get_post_types_for_module( $this->module );
297
298 register_taxonomy( self::metadata_taxonomy, $supported_post_types,
299 array(
300 'public' => false,
301 'labels' => array(
302 'name' => _x( 'Editorial Metadata', 'taxonomy general name', 'edit-flow' ),
303 'singular_name' => _x( 'Editorial Metadata', 'taxonomy singular name', 'edit-flow' ),
304 'search_items' => __( 'Search Editorial Metadata', 'edit-flow' ),
305 'popular_items' => __( 'Popular Editorial Metadata', 'edit-flow' ),
306 'all_items' => __( 'All Editorial Metadata', 'edit-flow' ),
307 'edit_item' => __( 'Edit Editorial Metadata', 'edit-flow' ),
308 'update_item' => __( 'Update Editorial Metadata', 'edit-flow' ),
309 'add_new_item' => __( 'Add New Editorial Metadata', 'edit-flow' ),
310 'new_item_name' => __( 'New Editorial Metadata', 'edit-flow' ),
311 ),
312 'rewrite' => false,
313 )
314 );
315 }
316
317 /*****************************************************
318 * Post meta box generation and processing
319 ****************************************************/
320
321 /**
322 * Load the post metaboxes for all of the post types that are supported
323 */
324 function handle_post_metaboxes() {
325 $title = __( 'Editorial Metadata', 'edit-flow' );
326 if ( current_user_can( 'manage_options' ) ) {
327 // Make the metabox title include a link to edit the Editorial Metadata terms. Logic similar to how Core dashboard widgets work.
328 $url = add_query_arg( 'page', 'ef-editorial-metadata-settings', get_admin_url( null, 'admin.php' ) );
329 $title .= ' <span class="postbox-title-action"><a href="' . esc_url( $url ) . '" class="edit-box open-box">' . __( 'Configure' ) . '</a></span>';
330 }
331
332 $supported_post_types = $this->get_post_types_for_module( $this->module );
333 foreach ( $supported_post_types as $post_type ) {
334 add_meta_box( self::metadata_taxonomy, $title, array( $this, 'display_meta_box' ), $post_type, 'side' );
335 }
336 }
337
338 /**
339 * Displays HTML output for Editorial Metadata post meta box
340 *
341 * @param object $post Current post
342 */
343 function display_meta_box( $post ) {
344 echo "<div id='" . self::metadata_taxonomy . "_meta_box'>";
345 // Add nonce for verification upon save
346 echo "<input type='hidden' name='" . self::metadata_taxonomy . "_nonce' value='" . wp_create_nonce(__FILE__) . "' />";
347
348 $terms = $this->get_editorial_metadata_terms();
349 if ( !count( $terms ) ) {
350 $message = __( 'No editorial metadata available.' );
351 if ( current_user_can( 'manage_options' ) )
352 $message .= sprintf( __( ' <a href="%s">Add fields to get started</a>.' ), $this->get_link() );
353 else
354 $message .= __( ' Encourage your site administrator to configure your editorial workflow by adding editorial metadata.' );
355 echo '<p>' . $message . '</p>';
356 } else {
357 foreach ( $terms as $term ) {
358 $postmeta_key = $this->get_postmeta_key( $term );
359 $current_metadata = esc_attr( $this->get_postmeta_value( $term, $post->ID ) );
360 $type = $term->type;
361 $description = $term->description;
362 if ( $description )
363 $description_span = "<span class='description'>$description</span>";
364 else
365 $description_span = '';
366 echo "<div class='" . self::metadata_taxonomy . " " . self::metadata_taxonomy . "_$type'>";
367 switch( $type ) {
368 case "date":
369 // TODO: Move this to a function
370 if ( !empty( $current_metadata ) ) {
371 // Turn timestamp into a human-readable date
372 $current_metadata = date( 'M d Y' , intval( $current_metadata ) );
373 }
374 echo "<label for='$postmeta_key'>{$term->name}</label>";
375 if ( $description_span )
376 echo "<label for='$postmeta_key'>$description_span</label>";
377 echo "<input id='$postmeta_key' name='$postmeta_key' type='text' class='date-pick' value='$current_metadata' />";
378 break;
379 case "location":
380 echo "<label for='$postmeta_key'>{$term->name}</label>";
381 if ( $description_span )
382 echo "<label for='$postmeta_key'>$description_span</label>";
383 echo "<input id='$postmeta_key' name='$postmeta_key' type='text' value='$current_metadata' />";
384 if ( !empty( $current_metadata ) )
385 echo "<div><a href='http://maps.google.com/?q={$current_metadata}&t=m' target='_blank'>" . sprintf( __( 'View &#8220;%s&#8221; on Google Maps', 'edit-flow' ), $current_metadata ) . "</a></div>";
386 break;
387 case "text":
388 echo "<label for='$postmeta_key'>{$term->name}$description_span</label>";
389 echo "<input id='$postmeta_key' name='$postmeta_key' type='text' value='$current_metadata' />";
390 break;
391 case "paragraph":
392 echo "<label for='$postmeta_key'>{$term->name}$description_span</label>";
393 echo "<textarea id='$postmeta_key' name='$postmeta_key'>$current_metadata</textarea>";
394 break;
395 case "checkbox":
396 echo "<label for='$postmeta_key'>{$term->name}$description_span</label>";
397 echo "<input id='$postmeta_key' name='$postmeta_key' type='checkbox' value='1' " . checked($current_metadata, 1, false) . " />";
398 break;
399 case "user":
400 echo "<label for='$postmeta_key'>{$term->name}$description_span</label>";
401 $user_dropdown_args = array(
402 'show_option_all' => __( '-- Select a user --', 'edit-flow' ),
403 'name' => $postmeta_key,
404 'selected' => $current_metadata
405 );
406 wp_dropdown_users( $user_dropdown_args );
407 break;
408 case "number":
409 echo "<label for='$postmeta_key'>{$term->name}$description_span</label>";
410 echo "<input id='$postmeta_key' name='$postmeta_key' type='text' value='$current_metadata' />";
411 break;
412 default:
413 echo "<p>" . __( 'This editorial metadata type is not yet supported.', 'edit-flow' ) . "</p>";
414 }
415 echo "</div>";
416 echo "<div class='clear'></div>";
417 } // Done iterating through metadata terms
418 }
419 echo "</div>";
420 }
421
422 /**
423 * Save any values in the editorial metadata post meta box
424 *
425 * @param int $id Unique ID for the post being saved
426 * @param object $post Post object
427 */
428 function save_meta_box( $id, $post ) {
429
430 // Authentication checks: make sure data came from our meta box and that the current user is allowed to edit the post
431 // TODO: switch to using check_admin_referrer? See core (e.g. edit.php) for usage
432 if ( ! isset( $_POST[self::metadata_taxonomy . "_nonce"] )
433 || ! wp_verify_nonce( $_POST[self::metadata_taxonomy . "_nonce"], __FILE__ ) ) {
434 return $id;
435 }
436
437 if( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
438 || ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) )
439 || $post->post_type == 'post' && !current_user_can( 'edit_post', $id )
440 || $post->post_type == 'page' && !current_user_can( 'edit_page', $id ) ) {
441 return $id;
442 }
443
444 // Authentication passed, let's save the data
445 $terms = $this->get_editorial_metadata_terms();
446 $term_slugs = array();
447
448 foreach ( $terms as $term ) {
449 // Setup the key for this editorial metadata term (same as what's in $_POST)
450 $key = $this->get_postmeta_key( $term );
451
452 // Get the current editorial metadata
453 // TODO: do we care about the current_metadata at all?
454 //$current_metadata = get_post_meta( $id, $key, true );
455
456 $new_metadata = isset( $_POST[$key] ) ? $_POST[$key] : '';
457
458 if ( empty ( $new_metadata ) ) {
459 delete_post_meta( $id, $key );
460 } else {
461
462 $type = $term->type;
463 // TODO: Move this to a function
464 if ( $type == 'date' ) {
465 $new_metadata = strtotime( $new_metadata );
466 }
467 if ( $type == 'number' ) {
468 $new_metadata = (int)$new_metadata;
469 }
470
471 $new_metadata = strip_tags( $new_metadata );
472 update_post_meta( $id, $key, $new_metadata );
473
474 // Add the slugs of the terms with non-empty new metadata to an array
475 $term_slugs[] = $term->slug;
476 }
477 }
478
479 // Relate the post to the terms used and taxonomy type (wp_term_relationships table).
480 // This will allow us to update and display the count of metadata in posts in use per term.
481 // TODO: Core only correlates posts with terms if the post_status is publish. Do we care what it is?
482 if ( $post->post_status === 'publish' ) {
483 wp_set_object_terms( $id, $term_slugs, self::metadata_taxonomy );
484 }
485 }
486
487 /**
488 * Generate a unique key based on the term
489 *
490 * @param object $term Term object
491 * @return string $postmeta_key Unique key
492 */
493 function get_postmeta_key( $term ) {
494 $key = self::metadata_postmeta_key;
495 $type = $term->type;
496 $prefix = "{$key}_{$type}";
497 $postmeta_key = "{$prefix}_" . ( is_object( $term ) ? $term->slug : $term );
498 return $postmeta_key;
499 }
500
501 /**
502 * Returns the value for the given metadata
503 *
504 * @param object|string|int term The term object, slug or ID for the metadata field term
505 * @param int post_id The ID of the post
506 */
507 function get_postmeta_value( $term, $post_id ) {
508 if( ! is_object( $term ) ) {
509 if ( is_int( $term ) )
510 $term = $this->get_editorial_metadata_term_by( 'id', $term );
511 else
512 $term = $this->get_editorial_metadata_term_by( 'slug', $term );
513 }
514 $postmeta_key = $this->get_postmeta_key( $term );
515 return get_metadata( 'post', $post_id, $postmeta_key, true );
516 }
517
518 /**
519 * Get all of the editorial metadata terms as objects and sort by position
520 * @todo Figure out what we should do with the filter...
521 *
522 * @return array $ordered_terms The terms as they should be ordered
523 */
524 function get_editorial_metadata_terms() {
525
526
527 $args = array(
528 'orderby' => apply_filters( 'ef_editorial_metadata_term_order', 'name' ),
529 'hide_empty' => false
530 );
531 $terms = get_terms( self::metadata_taxonomy, $args );
532 $ordered_terms = array();
533 $hold_to_end = array();
534 // Order the terms
535 foreach ( $terms as $key => $term ) {
536
537 // Unencode and set all of our psuedo term meta because we need the position and viewable if they exists
538 // First do an array_merge() on the term object to make sure the keys exist, then array_merge()
539 // any values that may already exist
540 $unencoded_description = $this->get_unencoded_description( $term->description );
541 $defaults = array(
542 'description' => '',
543 'viewable' => false,
544 'position' => false,
545 );
546 $term = array_merge( $defaults, (array)$term );
547 if ( is_array( $unencoded_description ) ) {
548 $term = array_merge( $term, $unencoded_description );
549 }
550 $term = (object)$term;
551 // We used to store the description field in a funny way
552 if ( isset( $term->desc ) ) {
553 $term->description = $term->desc;
554 unset( $term->desc );
555 }
556 // Only add the term to the ordered array if it has a set position and doesn't conflict with another key
557 // Otherwise, hold it for later
558 if ( $term->position && !array_key_exists( $term->position, $ordered_terms ) )
559 $ordered_terms[(int)$term->position] = $term;
560 else
561 $hold_to_end[] = $term;
562 }
563 // Sort the items numerically by key
564 ksort( $ordered_terms, SORT_NUMERIC );
565 // Append all of the terms that didn't have an existing position
566 foreach( $hold_to_end as $unpositioned_term )
567 $ordered_terms[] = $unpositioned_term;
568 return $ordered_terms;
569 }
570
571 /**
572 * Returns a term for single metadata field
573 *
574 * @param int|string $field The slug or ID for the metadata field term to return
575 * @return object $term Term's object representation
576 */
577 function get_editorial_metadata_term_by( $field, $value ) {
578
579
580 $term = get_term_by( $field, $value, self::metadata_taxonomy );
581 if ( ! $term || is_wp_error( $term ) )
582 return $term;
583
584 // Unencode and set all of our psuedo term meta because we need the position and viewable if they exists
585 $term->position = false;
586 $term->viewable = false;
587 $unencoded_description = $this->get_unencoded_description( $term->description );
588 if ( is_array( $unencoded_description ) ) {
589 foreach( $unencoded_description as $key => $value ) {
590 $term->$key = $value;
591 }
592 // We used to store the description field in a funny way
593 if ( isset( $term->desc ) ) {
594 $term->description = $term->desc;
595 unset( $term->desc );
596 }
597 }
598 return $term;
599 }
600
601 /**
602 * Register editorial metadata fields as columns in the manage posts view
603 * Only adds columns for the currently active post types - logic controlled in $this->init()
604 *
605 * @since 0.7
606 * @uses apply_filters( 'manage_posts_columns' ) in wp-admin/includes/class-wp-posts-list-table.php
607 *
608 * @param array $posts_columns Existing post columns prepared by WP_List_Table
609 * @param array $posts_columns Previous post columns with the new values
610 */
611 function filter_manage_posts_columns( $posts_columns ) {
612
613 $terms = $this->get_editorial_metadata_terms();
614 foreach( $terms as $term ) {
615 // Don't show if the term isn't viewable
616 if ( !$term->viewable )
617 continue;
618 // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions
619 $key = $this->module->slug . '-' . $term->slug;
620 $posts_columns[$key] = $term->name;
621 }
622 return $posts_columns;
623 }
624
625 /**
626 * Handle the output of an editorial metadata custom column
627 * Logic for the post types this is called on is controlled in $this->init()
628 *
629 * @since 0.7
630 * @uses do_action( 'manage_posts_custom_column' ) in wp-admin/includes/class-wp-posts-list-table.php
631 *
632 * @param string $column_name Unique string for the column
633 * @param int $post_id ID for the post of the row
634 */
635 function action_manage_posts_custom_column( $column_name, $post_id ) {
636
637 $terms = $this->get_editorial_metadata_terms();
638 // We're looking for the proper term to display its saved value
639 foreach( $terms as $term ) {
640 $key = $this->module->slug . '-' . $term->slug;
641 if ( $column_name != $key )
642 continue;
643
644 $postmeta_key = $this->get_postmeta_key( $term );
645 $current_metadata = $this->get_postmeta_value( $term, $post_id );
646 $type = $term->type;
647 switch( $type ) {
648 case "date":
649 if ( !empty( $current_metadata ) )
650 $current_metadata = date( get_option( 'date_format' ), intval( $current_metadata ) );
651 case "location":
652 case "text":
653 case "number":
654 case "paragraph":
655 echo esc_html( $current_metadata );
656 break;
657 case "checkbox":
658 if ( $current_metadata )
659 echo __( 'Yes', 'edit-flow' );
660 else
661 echo __( 'No', 'edit-flow' );
662 break;
663 case "user":
664 $userdata = get_userdata( $current_metadata );
665 if ( is_object( $userdata ) )
666 echo esc_html( $userdata->display_name );
667 break;
668 default:
669 break;
670 }
671
672 }
673
674 }
675
676 /**
677 * If the Edit Flow Calendar is enabled, add viewable Editorial Metadata terms
678 *
679 * @since 0.7
680 * @uses apply_filters( 'ef_calendar_item_information_fields' )
681 *
682 * @param array $calendar_fields Additional data fields to include on the calendar
683 * @param int $post_id Unique ID for the post data we're building
684 * @return array $calendar_fields Calendar fields with our viewable Editorial Metadata added
685 */
686 function filter_calendar_item_fields( $calendar_fields, $post_id ) {
687
688
689 // Make sure we respect which post type we're on
690 if ( !in_array( get_post_type( $post_id ), $this->get_post_types_for_module( $this->module ) ) )
691 return $calendar_fields;
692
693 $terms = $this->get_editorial_metadata_terms();
694
695 foreach( $terms as $term ) {
696 $key = $this->module->slug . '-' . $term->slug;
697 if ( !$term->viewable )
698 continue;
699
700 // Default values
701 $term_data = array(
702 'label' => $term->name,
703 'value' => '',
704 );
705 $postmeta_key = $this->get_postmeta_key( $term );
706 $current_metadata = $this->get_postmeta_value( $term, $post_id );
707 $type = $term->type;
708 switch( $type ) {
709 case "date":
710 if ( !empty( $current_metadata ) )
711 $current_metadata = date( get_option( 'date_format' ), intval( $current_metadata ) );
712 $term_data['value'] = esc_html( $current_metadata );
713 break;
714 case "location":
715 case "text":
716 case "number":
717 case "paragraph":
718 if ( $current_metadata )
719 $term_data['value'] = esc_html( $current_metadata );
720 break;
721 case "checkbox":
722 if ( $current_metadata )
723 $term_data['value'] = __( 'Yes', 'edit-flow' );
724 else
725 $term_data['value'] = __( 'No', 'edit-flow' );
726 break;
727 case "user":
728 $userdata = get_userdata( $current_metadata );
729 if ( is_object( $userdata ) )
730 $term_data['value'] = esc_html( $userdata->display_name );
731 break;
732 default:
733 break;
734 }
735
736 $calendar_fields[$key] = $term_data;
737 }
738 return $calendar_fields;
739
740 }
741
742 /**
743 * If the Edit Flow Story Budget is enabled, register our viewable terms as columns
744 *
745 * @since 0.7
746 * @uses apply_filters( 'ef_story_budget_term_columns' )
747 *
748 * @param array $term_columns The existing columns on the story budget
749 * @return array $term_columns Term columns with viewable Editorial Metadata terms
750 */
751 function filter_story_budget_term_columns( $term_columns ) {
752
753 $terms = $this->get_editorial_metadata_terms();
754 foreach( $terms as $term ) {
755 // Don't show if the term isn't viewable
756 if ( !$term->viewable )
757 continue;
758 // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions
759 $key = $this->module->slug . '-' . $term->slug;
760 // Switch to underscores
761 $key = str_replace( '-', '_', $key );
762 $term_columns[$key] = $term->name;
763 }
764 return $term_columns;
765
766 }
767
768 /**
769 * If the Edit Flow Story Budget is enabled,
770 *
771 * @since 0.7
772 * @uses apply_filters( 'ef_story_budget_term_column_value' )
773 *
774 * @param object $post The post we're displaying
775 * @param string $column_name Name of the column, as registered with EF_Story_Budget::register_term_columns
776 * @param object $parent_term The parent term for the term column
777 */
778 function filter_story_budget_term_column_values( $column_name, $post, $parent_term ) {
779
780 $local_column_name = str_replace( '_', '-', $column_name );
781 // Don't accidentally handle values not our own
782 if ( false === strpos( $local_column_name, $this->module->slug ) )
783 return $column_name;
784
785 $term_slug = str_replace( $this->module->slug . '-', '', $local_column_name );
786 $term = $this->get_editorial_metadata_term_by( 'slug', $term_slug );
787
788 // Don't allow non-viewable term data to be displayed
789 if ( !$term->viewable )
790 return $column_name;
791
792 $output = '';
793 $postmeta_key = $this->get_postmeta_key( $term );
794 $current_metadata = $this->get_postmeta_value( $term, $post->ID );
795 switch( $term->type ) {
796 case "date":
797 if ( !empty( $current_metadata ) )
798 $current_metadata = date( get_option( 'date_format' ), intval( $current_metadata ) );
799 $output = esc_html( $current_metadata );
800 break;
801 case "location":
802 case "text":
803 case "number":
804 case "paragraph":
805 if ( $current_metadata )
806 $output = esc_html( $current_metadata );
807 break;
808 case "checkbox":
809 if ( $current_metadata )
810 $output = __( 'Yes', 'edit-flow' );
811 else
812 $output = __( 'No', 'edit-flow' );
813 break;
814 case "user":
815 $userdata = get_userdata( $current_metadata );
816 if ( is_object( $userdata ) )
817 $output = esc_html( $userdata->display_name );
818 break;
819 default:
820 break;
821 }
822 return $output;
823
824 }
825
826 /**
827 * Update an existing editorial metadata term if the term_id exists
828 *
829 * @since 0.7
830 *
831 * @param int $term_id The term's unique ID
832 * @param array $args Any values that need to be updated for the term
833 * @return object|WP_Error $updated_term The updated term or a WP_Error object if something disastrous happened
834 */
835 function update_editorial_metadata_term( $term_id, $args ) {
836
837 $new_args = array();
838 $old_term = $this->get_editorial_metadata_term_by( 'id', $term_id );
839 if ( $old_term )
840 $old_args = array(
841 'position' => $old_term->position,
842 'name' => $old_term->name,
843 'slug' => $old_term->slug,
844 'description' => $old_term->description,
845 'type' => $old_term->type,
846 'viewable' => $old_term->viewable,
847 );
848 $new_args = array_merge( $old_args, $args );
849
850 // We're encoding metadata that isn't supported by default in the term's description field
851 $args_to_encode = array(
852 'description' => $new_args['description'],
853 'position' => $new_args['position'],
854 'type' => $new_args['type'],
855 'viewable' => $new_args['viewable'],
856 );
857 $encoded_description = $this->get_encoded_description( $args_to_encode );
858 $new_args['description'] = $encoded_description;
859
860 $updated_term = wp_update_term( $term_id, self::metadata_taxonomy, $new_args );
861 $updated_term = $this->get_editorial_metadata_term_by( 'id', $term_id );
862 return $updated_term;
863 }
864
865 /**
866 * Insert a new editorial metadata term
867 * @todo Handle conflicts with existing terms at that position (if relevant)
868 *
869 * @since 0.7
870 */
871 function insert_editorial_metadata_term( $args ) {
872
873
874 // Term is always added to the end of the list
875 $default_position = count( $this->get_editorial_metadata_terms() ) + 2;
876 $defaults = array(
877 'position' => $default_position,
878 'name' => '',
879 'slug' => '',
880 'description' => '',
881 'type' => '',
882 'viewable' => false,
883 );
884 $args = array_merge( $defaults, $args );
885 $term_name = $args['name'];
886 unset( $args['name'] );
887
888 // We're encoding metadata that isn't supported by default in the term's description field
889 $args_to_encode = array(
890 'description' => $args['description'],
891 'position' => $args['position'],
892 'type' => $args['type'],
893 'viewable' => $args['viewable'],
894 );
895 $encoded_description = $this->get_encoded_description( $args_to_encode );
896 $args['description'] = $encoded_description;
897
898 $inserted_term = wp_insert_term( $term_name, self::metadata_taxonomy, $args );
899 return $inserted_term;
900 }
901
902 /**
903 * Settings and other management code
904 */
905
906 /**
907 * Delete an existing editorial metadata term
908 *
909 * @since 0.7
910 *
911 * @param int $term_id The term we want deleted
912 * @return bool $result Whether or not the term was deleted
913 */
914 function delete_editorial_metadata_term( $term_id ) {
915 $result = wp_delete_term( $term_id, self::metadata_taxonomy );
916 return $result;
917 }
918
919 /**
920 * Generate a link to one of the editorial metadata actions
921 *
922 * @since 0.7
923 *
924 * @param array $args (optional) Action and any query args to add to the URL
925 * @return string $link Direct link to complete the action
926 */
927 function get_link( $args = array() ) {
928 if ( !isset( $args['action'] ) )
929 $args['action'] = '';
930 if ( !isset( $args['page'] ) )
931 $args['page'] = $this->module->settings_slug;
932 // Add other things we may need depending on the action
933 switch( $args['action'] ) {
934 case 'make-viewable':
935 case 'make-hidden':
936 case 'delete-term':
937 $args['nonce'] = wp_create_nonce( $args['action'] );
938 break;
939 default:
940 break;
941 }
942 return add_query_arg( $args, get_admin_url( null, 'admin.php' ) );
943 }
944
945 /**
946 * Handles a request to add a new piece of editorial metadata
947 */
948 function handle_add_editorial_metadata() {
949
950 if ( !isset( $_POST['submit'], $_POST['form-action'], $_GET['page'] )
951 || $_GET['page'] != $this->module->settings_slug || $_POST['form-action'] != 'add-term' )
952 return;
953
954 if ( !wp_verify_nonce( $_POST['_wpnonce'], 'editorial-metadata-add-nonce' ) )
955 wp_die( $this->module->messages['nonce-failed'] );
956
957 if ( !current_user_can( 'manage_options' ) )
958 wp_die( $this->module->messages['invalid-permissions'] );
959
960 // Sanitize all of the user-entered values
961 $term_name = strip_tags( trim( $_POST['metadata_name'] ) );
962 $term_slug = ( !empty( $_POST['metadata_slug'] ) ) ? sanitize_title( $_POST['metadata_slug'] ) : sanitize_title( $term_name );
963 $term_description = strip_tags( trim( $_POST['metadata_description'] ) );
964 $term_type = sanitize_key( $_POST['metadata_type'] );
965
966 $_REQUEST['form-errors'] = array();
967
968 /**
969 * Form validation for adding new editorial metadata term
970 *
971 * Details
972 * - "name", "slug", and "type" are required fields
973 * - "description" can accept a limited amount of HTML, and is optional
974 */
975 // Field is required
976 if ( empty( $term_name ) )
977 $_REQUEST['form-errors']['name'] = __( 'Please enter a name for the editorial metadata.', 'edit-flow' );
978 // Field is required
979 if ( empty( $term_slug ) )
980 $_REQUEST['form-errors']['slug'] = __( 'Please enter a slug for the editorial metadata.', 'edit-flow' );
981 if ( term_exists( $term_slug ) )
982 $_REQUEST['form-errors']['name'] = __( 'Name conflicts with existing term. Please choose another.', 'edit-flow' );
983 // Check to ensure a term with the same name doesn't exist
984 if ( $this->get_editorial_metadata_term_by( 'name', $term_name, self::metadata_taxonomy ) )
985 $_REQUEST['form-errors']['name'] = __( 'Name already in use. Please choose another.', 'edit-flow' );
986 // Check to ensure a term with the same slug doesn't exist
987 if ( $this->get_editorial_metadata_term_by( 'slug', $term_slug ) )
988 $_REQUEST['form-errors']['slug'] = __( 'Slug already in use. Please choose another.', 'edit-flow' );
989 // Check to make sure the status doesn't already exist as another term because otherwise we'd get a weird slug
990 // Check that the term name doesn't exceed 50 chars
991 if ( strlen( $term_name ) > 50 )
992 $_REQUEST['form-errors']['name'] = __( 'Name cannot exceed 50 characters. Please try a shorter name.', 'edit-flow' );
993 // Metadata type needs to pass our whitelist check
994 $metadata_types = $this->get_supported_metadata_types();
995 if ( empty( $_POST['metadata_type'] ) || !isset( $metadata_types[$_POST['metadata_type'] ] ) )
996 $_REQUEST['form-errors']['type'] = __( 'Please select a valid metadata type.', 'edit-flow' );
997 // Metadata viewable needs to be a valid Yes or No
998 $term_viewable = false;
999 if ( $_POST['metadata_viewable'] == 'yes' )
1000 $term_viewable = true;
1001
1002 // Kick out if there are any errors
1003 if ( count( $_REQUEST['form-errors'] ) ) {
1004 $_REQUEST['error'] = 'form-error';
1005 return;
1006 }
1007
1008 // Try to add the status
1009 $args = array(
1010 'name' => $term_name,
1011 'description' => $term_description,
1012 'slug' => $term_slug,
1013 'type' => $term_type,
1014 'viewable' => $term_viewable,
1015 );
1016 $return = $this->insert_editorial_metadata_term( $args );
1017 if ( is_wp_error( $return ) )
1018 wp_die( __( 'Error adding term.', 'edit-flow' ) );
1019
1020 $redirect_url = add_query_arg( array( 'page' => $this->module->settings_slug, 'message' => 'term-added' ), get_admin_url( null, 'admin.php' ) );
1021 wp_redirect( $redirect_url );
1022 exit;
1023 }
1024
1025 /**
1026 * Handles a request to edit an editorial metadata
1027 */
1028 function handle_edit_editorial_metadata() {
1029 if ( !isset( $_POST['submit'], $_GET['page'], $_GET['action'], $_GET['term-id'] )
1030 || $_GET['page'] != $this->module->settings_slug || $_GET['action'] != 'edit-term' )
1031 return;
1032
1033 if ( !wp_verify_nonce( $_POST['_wpnonce'], 'editorial-metadata-edit-nonce' ) )
1034 wp_die( $this->module->messages['nonce-failed'] );
1035
1036 if ( !current_user_can( 'manage_options' ) )
1037 wp_die( $this->module->messages['invalid-permissions'] );
1038
1039 if ( !$existing_term = $this->get_editorial_metadata_term_by( 'id', (int)$_GET['term-id'] ) )
1040 wp_die( $this->module->messsage['term-error'] );
1041
1042 $new_name = strip_tags( trim( $_POST['name'] ) );
1043 $new_description = strip_tags( trim( $_POST['description'] ) );
1044
1045 /**
1046 * Form validation for editing editorial metadata term
1047 *
1048 * Details
1049 * - "name", "slug", and "type" are required fields
1050 * - "description" can accept a limited amount of HTML, and is optional
1051 */
1052 $_REQUEST['form-errors'] = array();
1053 // Check if name field was filled in
1054 if( empty( $new_name ) )
1055 $_REQUEST['form-errors']['name'] = __( 'Please enter a name for the editorial metadata', 'edit-flow' );
1056
1057 // Check that the name isn't numeric
1058 if ( is_numeric( $new_name ) )
1059 $_REQUEST['form-errors']['name'] = __( 'Please enter a valid, non-numeric name for the editorial metadata.', 'edit-flow' );
1060
1061 $term_exists = term_exists( sanitize_title( $new_name ) );
1062 if ( $term_exists && $term_exists != $existing_term->term_id )
1063 $_REQUEST['form-errors']['name'] = __( 'Metadata name conflicts with existing term. Please choose another.', 'edit-flow' );
1064
1065 // Check to ensure a term with the same name doesn't exist,
1066 $search_term = $this->get_editorial_metadata_term_by( 'name', $new_name );
1067 if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id )
1068 $_REQUEST['form-errors']['name'] = __( 'Name already in use. Please choose another.', 'edit-flow' );
1069 // or that the term name doesn't map to an existing term's slug
1070 $search_term = $this->get_editorial_metadata_term_by( 'slug', sanitize_title( $new_name ) );
1071 if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id )
1072 $_REQUEST['form-errors']['name'] = __( 'Name conflicts with slug for another term. Please choose something else.', 'edit-flow' );
1073
1074 // Check that the term name doesn't exceed 50 chars
1075 if ( strlen( $new_name ) > 50 )
1076 $_REQUEST['form-errors']['name'] = __( 'Name cannot exceed 50 characters. Please try a shorter name.', 'edit-flow' );
1077 // Make sure the viewable state is valid
1078 $new_viewable = false;
1079 if ( $_POST['viewable'] == 'yes' )
1080 $new_viewable = true;
1081
1082 // Kick out if there are any errors
1083 if ( count( $_REQUEST['form-errors'] ) ) {
1084 $_REQUEST['error'] = 'form-error';
1085 return;
1086 }
1087
1088 // Try to add the metadata term
1089 $args = array(
1090 'name' => $new_name,
1091 'description' => $new_description,
1092 'viewable' => $new_viewable,
1093 );
1094 $return = $this->update_editorial_metadata_term( $existing_term->term_id, $args );
1095 if ( is_wp_error( $return ) )
1096 wp_die( __( 'Error updating term.', 'edit-flow' ) );
1097
1098 $redirect_url = add_query_arg( array( 'page' => $this->module->settings_slug, 'message' => 'term-updated' ), get_admin_url( null, 'admin.php' ) );
1099 wp_redirect( $redirect_url );
1100 exit;
1101 }
1102
1103 /**
1104 * Handle a $_GET request to change the visibility of an Editorial Metadata term
1105 *
1106 * @since 0.7
1107 */
1108 function handle_change_editorial_metadata_visibility() {
1109
1110 // Check that the current GET request is our GET request
1111 if ( !isset( $_GET['page'], $_GET['action'], $_GET['term-id'], $_GET['nonce'] )
1112 || $_GET['page'] != $this->module->settings_slug || !in_array( $_GET['action'], array( 'make-viewable', 'make-hidden' ) ) )
1113 return;
1114
1115 // Check for proper nonce
1116 if ( !wp_verify_nonce( $_GET['nonce'], 'make-viewable' ) && !wp_verify_nonce( $_GET['nonce'], 'make-hidden' ) )
1117 wp_die( $this->module->messages['nonce-failed'] );
1118
1119 // Only allow users with the proper caps
1120 if ( !current_user_can( 'manage_options' ) )
1121 wp_die( $this->module->messages['invalid-permissions'] );
1122
1123 $term_id = (int)$_GET['term-id'];
1124 $args = array();
1125 if ( $_GET['action'] == 'make-viewable' )
1126 $args['viewable'] = true;
1127 elseif ( $_GET['action'] == 'make-hidden' )
1128 $args['viewable'] = false;
1129
1130 $return = $this->update_editorial_metadata_term( $term_id, $args );
1131 if ( is_wp_error( $return ) )
1132 wp_die( __( 'Error updating term.', 'edit-flow' ) );
1133
1134 $redirect_url = $this->get_link( array( 'message' => 'term-visibility-changed' ) );
1135 wp_redirect( $redirect_url );
1136 exit;
1137
1138 }
1139
1140 /**
1141 * Handle the request to update a given Editorial Metadata term via inline edit
1142 *
1143 * @since 0.7
1144 */
1145 function handle_ajax_inline_save_term() {
1146
1147 if ( !wp_verify_nonce( $_POST['inline_edit'], 'editorial-metadata-inline-edit-nonce' ) )
1148 die( $this->module->messages['nonce-failed'] );
1149
1150 if ( !current_user_can( 'manage_options') )
1151 die( $this->module->messages['invalid-permissions'] );
1152
1153 $term_id = (int) $_POST['term_id'];
1154 if ( !$existing_term = $this->get_editorial_metadata_term_by( 'id', $term_id ) )
1155 die( $this->module->messsage['term-error'] );
1156
1157 $metadata_name = strip_tags( trim( $_POST['name'] ) );
1158 $metadata_description = strip_tags( trim( $_POST['description'] ) );
1159
1160 /**
1161 * Form validation for editing editorial metadata term
1162 */
1163 // Check if name field was filled in
1164 if ( empty( $metadata_name ) ) {
1165 $change_error = new WP_Error( 'invalid', __( 'Please enter a name for the editorial metadata', 'edit-flow' ) );
1166 die( $change_error->get_error_message() );
1167 }
1168
1169 // Check that the name isn't numeric
1170 if( is_numeric( $metadata_name) ) {
1171 $change_error = new WP_Error( 'invalid', __( 'Please enter a valid, non-numeric name for the editorial metadata.', 'edit-flow' ) );
1172 die( $change_error->get_error_message() );
1173 }
1174
1175 // Check that the term name doesn't exceed 50 chars
1176 if ( strlen( $metadata_name ) > 50 ) {
1177 $change_error = new WP_Error( 'invalid', __( 'Name cannot exceed 50 characters. Please try a shorter name.' ) );
1178 die( $change_error->get_error_message() );
1179 }
1180
1181 // Check to make sure the status doesn't already exist as another term because otherwise we'd get a fatal error
1182 $term_exists = term_exists( sanitize_title( $metadata_name ) );
1183 if ( $term_exists && $term_exists != $term_id ) {
1184 $change_error = new WP_Error( 'invalid', __( 'Metadata name conflicts with existing term. Please choose another.', 'edit-flow' ) );
1185 die( $change_error->get_error_message() );
1186 }
1187
1188 // Check to ensure a term with the same name doesn't exist,
1189 $search_term = get_term_by( 'name', $metadata_name, self::metadata_taxonomy );
1190 if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id ) {
1191 $change_error = new WP_Error( 'invalid', __( 'Name already in use. Please choose another.', 'edit-flow' ) );
1192 die( $change_error->get_error_message() );
1193 }
1194 // or that the term name doesn't map to an existing term's slug
1195 $search_term = get_term_by( 'slug', sanitize_title( $metadata_name ), self::metadata_taxonomy );
1196 if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id ) {
1197 $change_error = new WP_Error( 'invalid', __( 'Name conflicts with slug for another term. Please choose again.', 'edit-flow' ) );
1198 die( $change_error->get_error_message() );
1199 }
1200
1201 // Prepare the term name and description for saving
1202 $args = array(
1203 'name' => $metadata_name,
1204 'description' => $metadata_description,
1205 );
1206 $return = $this->update_editorial_metadata_term( $existing_term->term_id, $args );
1207 if( !is_wp_error( $return ) ) {
1208 set_current_screen( 'edit-editorial-metadata' );
1209 $wp_list_table = new EF_Editorial_Metadata_List_Table();
1210 $wp_list_table->prepare_items();
1211 echo $wp_list_table->single_row( $return );
1212 die();
1213 } else {
1214 $change_error = new WP_Error( 'invalid', sprintf( __( 'Could not update the term: <strong>%s</strong>', 'edit-flow' ), $status_name ) );
1215 die( $change_error->get_error_message() );
1216 }
1217
1218 }
1219
1220 /**
1221 * Handle the ajax request to update all of the term positions
1222 *
1223 * @since 0.7
1224 */
1225 function handle_ajax_update_term_positions() {
1226
1227 if ( !wp_verify_nonce( $_POST['editorial_metadata_sortable_nonce'], 'editorial-metadata-sortable' ) )
1228 $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] );
1229
1230 if ( !current_user_can( 'manage_options') )
1231 $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] );
1232
1233 if ( !isset( $_POST['term_positions'] ) || !is_array( $_POST['term_positions'] ) )
1234 $this->print_ajax_response( 'error', __( 'Terms not set.', 'edit-flow' ) );
1235
1236 foreach ( $_POST['term_positions'] as $position => $term_id ) {
1237
1238 // Have to add 1 to the position because the index started with zero
1239 $args = array(
1240 'position' => (int)$position + 1,
1241 );
1242 $return = $this->update_editorial_metadata_term( (int)$term_id, $args );
1243 // @todo check that this was a valid return
1244 }
1245 $this->print_ajax_response( 'success', $this->module->messages['term-position-updated'] );
1246 }
1247
1248 /**
1249 * Handles a request to delete an editorial metadata term
1250 */
1251 function handle_delete_editorial_metadata() {
1252 if ( !isset( $_GET['page'], $_GET['action'], $_GET['term-id'] )
1253 || $_GET['page'] != $this->module->settings_slug || $_GET['action'] != 'delete-term' )
1254 return;
1255
1256 if ( !wp_verify_nonce( $_GET['nonce'], 'delete-term' ) )
1257 wp_die( $this->module->messages['nonce-failed'] );
1258
1259 if ( !current_user_can( 'manage_options' ) )
1260 wp_die( $this->module->messages['invalid-permissions'] );
1261
1262 if ( !$existing_term = $this->get_editorial_metadata_term_by( 'id', (int)$_GET['term-id'] ) )
1263 wp_die( $this->module->messsage['term-error'] );
1264
1265 $result = $this->delete_editorial_metadata_term( $existing_term->term_id );
1266 if ( !$result || is_wp_error( $result ) )
1267 wp_die( __( 'Error deleting term.', 'edit-flow' ) );
1268
1269 $redirect_url = add_query_arg( array( 'page' => $this->module->settings_slug, 'message' => 'term-deleted' ), get_admin_url( null, 'admin.php' ) );
1270 wp_redirect( $redirect_url );
1271 exit;
1272 }
1273
1274 /**
1275 * Register settings for notifications so we can partially use the Settings API
1276 * (We use the Settings API for form generation, but not saving)
1277 *
1278 * @since 0.7
1279 * @uses add_settings_section(), add_settings_field()
1280 */
1281 function register_settings() {
1282 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name );
1283 add_settings_field( 'post_types', __( 'Add to these post types:', 'edit-flow' ), array( $this, 'settings_post_types_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
1284 }
1285
1286 /**
1287 * Choose the post types for editorial metadata
1288 *
1289 * @since 0.7
1290 */
1291 function settings_post_types_option() {
1292 global $edit_flow;
1293 $edit_flow->settings->helper_option_custom_post_type( $this->module );
1294 }
1295
1296 /**
1297 * Validate data entered by the user
1298 *
1299 * @since 0.7
1300 *
1301 * @param array $new_options New values that have been entered by the user
1302 * @return array $new_options Form values after they've been sanitized
1303 */
1304 function settings_validate( $new_options ) {
1305
1306 // Whitelist validation for the post type options
1307 if ( !isset( $new_options['post_types'] ) )
1308 $new_options['post_types'] = array();
1309 $new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support );
1310
1311 return $new_options;
1312 }
1313
1314 /**
1315 * Prepare and display the configuration view for editorial metadata.
1316 * There are four primary components:
1317 * - Form to add a new Editorial Metadata term
1318 * - Form generated by the settings API for managing Editorial Metadata options
1319 * - Table of existing Editorial Metadata terms with ability to take actions on each
1320 * - Full page width view for editing a single Editorial Metadata term
1321 *
1322 * @since 0.7
1323 */
1324 function print_configure_view() {
1325 global $edit_flow;
1326 $wp_list_table = new EF_Editorial_Metadata_List_Table();
1327 $wp_list_table->prepare_items();
1328 ?>
1329 <script type="text/javascript">
1330 var ef_confirm_delete_term_string = "<?php echo esc_js( __( 'Are you sure you want to delete this term? Any metadata for this term will remain but will not be visible unless this term is re-added.', 'edit-flow' ) ); ?>";
1331 </script>
1332 <?php if ( !isset( $_GET['action'] ) || ( isset( $_GET['action'] ) && $_GET['action'] != 'edit-term' ) ): ?>
1333 <div id="col-right">
1334 <div class="col-wrap">
1335 <form id="posts-filter" action="" method="post">
1336 <?php $wp_list_table->display(); ?>
1337 <?php wp_nonce_field( 'editorial-metadata-sortable', 'editorial-metadata-sortable' ); ?>
1338 </form>
1339 </div>
1340 </div><!-- /col-right -->
1341 <?php $wp_list_table->inline_edit(); ?>
1342 <?php endif; ?>
1343
1344 <?php if ( isset( $_GET['action'], $_GET['term-id'] ) && $_GET['action'] == 'edit-term' ): ?>
1345 <?php /** Full page width view for editing a given editorial metadata term **/ ?>
1346 <?php
1347 // Check whether the term exists
1348 $term_id = (int)$_GET['term-id'];
1349 $term = $this->get_editorial_metadata_term_by( 'id', $term_id );
1350 if ( !$term ) {
1351 echo '<div class="error"><p>' . $this->module->messages['term-missing'] . '</p></div>';
1352 return;
1353 }
1354 $metadata_types = $this->get_supported_metadata_types();
1355 $type = $term->type;
1356 $edit_term_link = $this->get_link( array( 'action' => 'edit-term', 'term-id' => $term->term_id ) );
1357
1358 $name = ( isset( $_POST['name'] ) ) ? stripslashes( $_POST['name'] ) : $term->name;
1359 $description = ( isset( $_POST['description'] ) ) ? stripslashes( $_POST['description'] ) : $term->description;
1360 if ( $term->viewable )
1361 $viewable = 'yes';
1362 else
1363 $viewable = 'no';
1364 $viewable = ( isset( $_POST['viewable'] ) ) ? stripslashes( $_POST['viewable'] ) : $viewable;
1365 ?>
1366
1367 <form method="post" action="<?php echo esc_url( $edit_term_link ); ?>" >
1368 <input type="hidden" name="action" value="editedtag" />
1369 <input type="hidden" name="tag_id" value="<?php echo esc_attr( $term->term_id ); ?>" />
1370 <input type="hidden" name="taxonomy" value="<?php echo esc_attr( self::metadata_taxonomy ) ?>" />
1371 <?php
1372 wp_original_referer_field();
1373 wp_nonce_field( 'editorial-metadata-edit-nonce' );
1374 ?>
1375 <table class="form-table">
1376 <tr class="form-field form-required">
1377 <th scope="row" valign="top"><label for="name"><?php _e( 'Name' ); ?></label></th>
1378 <td><input name="name" id="name" type="text" value="<?php echo esc_attr( $name ); ?>" size="40" aria-required="true" />
1379 <?php $edit_flow->settings->helper_print_error_or_description( 'name', __( 'The name is for labeling the metadata field.', 'edit-flow' ) ); ?>
1380 </tr>
1381 <tr class="form-field">
1382 <th scope="row" valign="top"><?php _e( 'Slug', 'edit-flow' ); ?></th>
1383 <td>
1384 <input type="text" disabled="disabled" value="<?php echo esc_attr( $term->slug ); ?>" />
1385 <p class="description"><?php _e( 'The slug cannot be changed once the term has been created.', 'edit-flow' ); ?></p>
1386 </td>
1387 </tr>
1388 <tr class="form-field">
1389 <th scope="row" valign="top"><label for="description"><?php _e( 'Description', 'edit-flow' ); ?></label></th>
1390 <td>
1391 <textarea name="description" id="description" rows="5" cols="50" style="width: 97%;"><?php echo esc_html( $description ); ?></textarea>
1392 <?php $edit_flow->settings->helper_print_error_or_description( 'description', __( 'The description can be used to communicate with your team about what the metadata is for.', 'edit-flow' ) ); ?>
1393 </td>
1394 </tr>
1395 <tr class="form-field">
1396 <th scope="row" valign="top"><?php _e( 'Type', 'edit-flow' ); ?></th>
1397 <td>
1398 <input type="text" disabled="disabled" value="<?php echo esc_attr( $metadata_types[$type] ); ?>" />
1399 <p class="description"><?php _e( 'The metadata type cannot be changed once created.', 'edit-flow' ); ?></p>
1400 </td>
1401 </tr>
1402 <tr class="form-field">
1403 <th scope="row" valign="top"><?php _e( 'Viewable', 'edit-flow' ); ?></th>
1404 <td>
1405 <?php
1406 $metadata_viewable_options = array(
1407 'no' => __( 'No', 'edit-flow' ),
1408 'yes' => __( 'Yes', 'edit-flow' ),
1409 );
1410 ?>
1411 <select id="viewable" name="viewable">
1412 <?php foreach ( $metadata_viewable_options as $metadata_viewable_key => $metadata_viewable_value ) : ?>
1413 <option value="<?php echo esc_attr( $metadata_viewable_key ); ?>" <?php selected( $viewable, $metadata_viewable_key ); ?>><?php echo esc_attr( $metadata_viewable_value ); ?></option>
1414 <?php endforeach; ?>
1415 </select>
1416 <?php $edit_flow->settings->helper_print_error_or_description( 'viewable', __( 'When viewable, metadata can be seen on views other than the edit post view (e.g. calendar, manage posts, story budget, etc.)', 'edit-flow' ) ); ?>
1417 </td>
1418 </tr>
1419 <input type="hidden" name="<?php echo self::metadata_taxonomy ?>'_type" value="<?php echo $type; ?>" />
1420 </table>
1421 <p class="submit">
1422 <?php submit_button( __( 'Update Metadata Term', 'edit-flow' ), 'primary', 'submit', false ); ?>
1423 <a class="cancel-settings-link" href="<?php echo esc_url( add_query_arg( 'page', $this->module->settings_slug, get_admin_url( null, 'admin.php' ) ) ); ?>"><?php _e( 'Cancel', 'edit-flow' ); ?></a>
1424 </p>
1425 </form>
1426
1427 <?php else: ?>
1428 <?php /** If not in full-screen edit term mode, we can create new terms or change options **/ ?>
1429 <div id="col-left">
1430 <div class="col-wrap">
1431 <div class="form-wrap">
1432 <h3 class="nav-tab-wrapper">
1433 <a href="<?php echo esc_url( add_query_arg( array( 'page' => $this->module->settings_slug ), get_admin_url( null, 'admin.php' ) ) ); ?>" class="nav-tab<?php if ( !isset( $_GET['action'] ) || $_GET['action'] != 'change-options' ) echo ' nav-tab-active'; ?>"><?php _e( 'Add New', 'edit-flow' ); ?></a>
1434 <a href="<?php echo esc_url( add_query_arg( array( 'page' => $this->module->settings_slug, 'action' => 'change-options' ), get_admin_url( null, 'admin.php' ) ) ); ?>" class="nav-tab<?php if ( isset( $_GET['action'] ) && $_GET['action'] == 'change-options' ) echo ' nav-tab-active'; ?>"><?php _e( 'Options', 'edit-flow' ); ?></a>
1435 </h3>
1436
1437 <?php if ( isset( $_GET['action'] ) && $_GET['action'] == 'change-options' ): ?>
1438 <?php /** Basic form built on WP Settings API for outputting Editorial Metadata options **/ ?>
1439 <form class="basic-settings" action="<?php echo esc_url( add_query_arg( array( 'page' => $this->module->settings_slug, 'action' => 'change-options' ), get_admin_url( null, 'admin.php' ) ) ); ?>" method="post">
1440 <?php settings_fields( $this->module->options_group_name ); ?>
1441 <?php do_settings_sections( $this->module->options_group_name ); ?>
1442 <?php echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />'; ?>
1443 <?php submit_button(); ?>
1444 </form>
1445 <?php else: ?>
1446 <?php /** Custom form for adding a new Editorial Metadata term **/ ?>
1447 <form class="add:the-list:" action="<?php echo esc_url( add_query_arg( array( 'page' => $this->module->settings_slug ), get_admin_url( null, 'admin.php' ) ) ); ?>" method="post" id="addmetadata" name="addmetadata">
1448 <div class="form-field form-required">
1449 <label for="metadata_name"><?php _e( 'Name', 'edit-flow' ); ?></label>
1450 <input type="text" aria-required="true" size="20" maxlength="20" id="metadata_name" name="metadata_name" value="<?php if ( !empty( $_POST['metadata_name'] ) ) echo esc_attr( stripslashes( $_POST['metadata_name'] ) ) ?>" />
1451 <?php $edit_flow->settings->helper_print_error_or_description( 'name', __( 'The name is for labeling the metadata field.', 'edit-flow' ) ); ?>
1452 </div>
1453 <div class="form-field form-required">
1454 <label for="metadata_slug"><?php _e( 'Slug', 'edit-flow' ); ?></label>
1455 <input type="text" aria-required="true" size="20" maxlength="20" id="metadata_slug" name="metadata_slug" value="<?php if ( !empty( $_POST['metadata_slug'] ) ) echo esc_attr( $_POST['metadata_slug'] ) ?>" />
1456 <?php $edit_flow->settings->helper_print_error_or_description( 'slug', __( 'The "slug" is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.', 'edit-flow' ) ); ?>
1457 </div>
1458 <div class="form-field">
1459 <label for="metadata_description"><?php _e( 'Description', 'edit-flow' ); ?></label>
1460 <textarea cols="40" rows="5" id="metadata_description" name="metadata_description"><?php if ( !empty( $_POST['metadata_description'] ) ) echo esc_html( stripslashes( $_POST['metadata_description'] ) ) ?></textarea>
1461 <?php $edit_flow->settings->helper_print_error_or_description( 'description', __( 'The description can be used to communicate with your team about what the metadata is for.', 'edit-flow' ) ); ?>
1462 </div>
1463 <div class="form-field form-required">
1464 <label for="metadata_type"><?php _e( 'Type', 'edit-flow' ); ?></label>
1465 <?php
1466 $metadata_types = $this->get_supported_metadata_types();
1467 // Select the previously selected metadata type if a valid one exists
1468 $current_metadata_type = ( isset( $_POST['metadata_type'] ) && in_array( $_POST['metadata_type'], array_keys( $metadata_types ) ) ) ? $_POST['metadata_type'] : false;
1469 ?>
1470 <select id="metadata_type" name="metadata_type">
1471 <?php foreach ( $metadata_types as $metadata_type => $metadata_type_name ) : ?>
1472 <option value="<?php echo esc_attr( $metadata_type ); ?>" <?php selected( $metadata_type, $current_metadata_type ); ?>><?php echo esc_attr( $metadata_type_name ); ?></option>
1473 <?php endforeach; ?>
1474 </select>
1475 <?php $edit_flow->settings->helper_print_error_or_description( 'type', __( 'Indicate the type of editorial metadata.', 'edit-flow' ) ); ?>
1476 </div>
1477 <div class="form-field form-required">
1478 <label for="metadata_viewable"><?php _e( 'Viewable', 'edit-flow' ); ?></label>
1479 <?php
1480 $metadata_viewable_options = array(
1481 'no' => __( 'No', 'edit-flow' ),
1482 'yes' => __( 'Yes', 'edit-flow' ),
1483 );
1484 $current_metadata_viewable = ( isset( $_POST['metadata_viewable'] ) && in_array( $_POST['metadata_viewable'], array_keys( $metadata_viewable_options ) ) ) ? $_POST['metadata_viewable'] : 'no';
1485 ?>
1486 <select id="metadata_viewable" name="metadata_viewable">
1487 <?php foreach ( $metadata_viewable_options as $metadata_viewable_key => $metadata_viewable_value ) : ?>
1488 <option value="<?php echo esc_attr( $metadata_viewable_key ); ?>" <?php selected( $current_metadata_viewable, $metadata_viewable_key ); ?>><?php echo esc_attr( $metadata_viewable_value ); ?></option>
1489 <?php endforeach; ?>
1490 </select>
1491 <?php $edit_flow->settings->helper_print_error_or_description( 'viewable', __( 'When viewable, metadata can be seen on views other than the edit post view (e.g. calendar, manage posts, story budget, etc.)', 'edit-flow' ) ); ?>
1492 </div>
1493 <?php wp_nonce_field( 'editorial-metadata-add-nonce' );?>
1494 <input type="hidden" id="form-action" name="form-action" value="add-term" />
1495 <p class="submit"><?php submit_button( __( 'Add New Metadata Term', '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>
1496 </form>
1497 <?php endif; ?>
1498 </div>
1499 </div>
1500 </div>
1501
1502 <?php
1503 endif;
1504 }
1505
1506 }
1507
1508 }
1509
1510 /**
1511 * Management interface for Editorial Metadata. Extends WP_List_Table class
1512 */
1513 class EF_Editorial_Metadata_List_Table extends WP_List_Table {
1514
1515 var $callback_args;
1516 var $taxonomy;
1517 var $tax;
1518
1519 /**
1520 * Construct the class
1521 */
1522 function __construct() {
1523 global $edit_flow;
1524
1525 $this->taxonomy = EF_Editorial_Metadata::metadata_taxonomy;
1526
1527 $this->tax = get_taxonomy( $this->taxonomy );
1528
1529 $columns = $this->get_columns();
1530 $hidden = array(
1531 'position',
1532 );
1533 $sortable = array();
1534
1535 $this->_column_headers = array( $columns, $hidden, $sortable );
1536
1537 parent::__construct( array(
1538 'plural' => 'editorial metadata',
1539 'singular' => 'editorial metadata',
1540 ) );
1541 }
1542
1543 /**
1544 * Prepare the items to be displayed on the list table
1545 *
1546 * @since 0.7
1547 */
1548 function prepare_items() {
1549 global $edit_flow;
1550 $this->items = $edit_flow->editorial_metadata->get_editorial_metadata_terms();
1551
1552 $this->set_pagination_args( array(
1553 'total_items' => count( $this->items ),
1554 'per_page' => count( $this->items ),
1555 ) );
1556 }
1557
1558 /**
1559 * Message to be displayed when there is no editorial metadata
1560 *
1561 * @since 0.7
1562 */
1563 function no_items() {
1564 _e( 'No editorial metadata found.', 'edit-flow' );
1565 }
1566
1567 /**
1568 * Register the columns to appear in the table
1569 *
1570 * @since 0.7
1571 */
1572 function get_columns() {
1573
1574 $columns = array(
1575 'position' => __( 'Position', 'edit-flow' ),
1576 'name' => __( 'Name', 'edit-flow' ),
1577 'type' => __( 'Metadata Type', 'edit-flow' ),
1578 'description' => __( 'Description', 'edit-flow' ),
1579 'viewable' => __( 'Viewable', 'edit-flow' ),
1580 );
1581 return $columns;
1582 }
1583
1584 /**
1585 * Prepare a single row of Editorial Metadata
1586 *
1587 * @since 0.7
1588 *
1589 * @param object $term The current term we're displaying
1590 * @param int $level Level is always zero because it isn't a parent-child tax
1591 */
1592 function single_row( $term, $level = 0 ) {
1593 static $alternate_class = '';
1594 $alternate_class = ( $alternate_class == '' ? ' alternate' : '' );
1595 $row_class = ' class="term-static' . $alternate_class . '"';
1596
1597 echo '<tr id="term-' . $term->term_id . '"' . $row_class . '>';
1598 echo $this->single_row_columns( $term );
1599 echo '</tr>';
1600 }
1601
1602 /**
1603 * Handle the column output when there's no method for it
1604 *
1605 * @since 0.7
1606 *
1607 * @param object $item Editorial Metadata term as an object
1608 * @param string $column_name How the column was registered at birth
1609 */
1610 function column_default( $item, $column_name ) {
1611
1612 switch( $column_name ) {
1613 case 'position':
1614 case 'type':
1615 case 'description':
1616 return esc_html( $item->$column_name );
1617 break;
1618 case 'viewable':
1619 if ( $item->viewable )
1620 return __( 'Yes', 'edit-flow' );
1621 else
1622 return __( 'No', 'edit-flow' );
1623 break;
1624 default:
1625 break;
1626 }
1627
1628 }
1629
1630 /**
1631 * Column for displaying the term's name and associated actions
1632 *
1633 * @since 0.7
1634 *
1635 * @param object $item Editorial Metadata term as an object
1636 */
1637 function column_name( $item ) {
1638 global $edit_flow;
1639 $item_edit_link = esc_url( $edit_flow->editorial_metadata->get_link( array( 'action' => 'edit-term', 'term-id' => $item->term_id ) ) );
1640 $item_delete_link = esc_url( $edit_flow->editorial_metadata->get_link( array( 'action' => 'delete-term', 'term-id' => $item->term_id ) ) );
1641
1642 $out = '<strong><a class="row-title" href="' . $item_edit_link . '">' . esc_html( $item->name ) . '</a></strong>';
1643
1644 $actions = array();
1645 $actions['edit'] = "<a href='$item_edit_link'>" . __( 'Edit', 'edit-flow' ) . "</a>";
1646 $actions['inline hide-if-no-js'] = '<a href="#" class="editinline">' . __( 'Quick&nbsp;Edit' ) . '</a>';
1647 if ( $item->viewable )
1648 $actions['change-visibility make-hidden'] = '<a title="' . esc_attr( __( 'Hidden metadata can only be viewed on the edit post view.', 'edit-flow' ) ) . '" href="' . esc_url( $edit_flow->editorial_metadata->get_link( array( 'action' => 'make-hidden', 'term-id' => $item->term_id ) ) ) . '">' . __( 'Make Hidden', 'edit-flow' ) . '</a>';
1649 else
1650 $actions['change-visibility make-viewable'] = '<a title="' . esc_attr( __( 'When viewable, metadata can be seen on views other than the edit post view (e.g. calendar, manage posts, story budget, etc.)', 'edit-flow' ) ) . '" href="' . esc_url( $edit_flow->editorial_metadata->get_link( array( 'action' => 'make-viewable', 'term-id' => $item->term_id ) ) ) . '">' . __( 'Make Viewable', 'edit-flow' ) . '</a>';
1651 $actions['delete delete-status'] = "<a href='$item_delete_link'>" . __( 'Delete', 'edit-flow' ) . "</a>";
1652
1653 $out .= $this->row_actions( $actions, false );
1654 $out .= '<div class="hidden" id="inline_' . $item->term_id . '">';
1655 $out .= '<div class="name">' . $item->name . '</div>';
1656 $out .= '<div class="description">' . $item->description . '</div>';
1657 $out .= '</div>';
1658
1659 return $out;
1660 }
1661
1662 /**
1663 * Admins can use the inline edit capability to quickly make changes to the title or description
1664 *
1665 * @since 0.7
1666 */
1667 function inline_edit() {
1668
1669 ?>
1670 <form method="get" action=""><table style="display: none"><tbody id="inlineedit">
1671 <tr id="inline-edit" class="inline-edit-row" style="display: none"><td colspan="<?php echo $this->get_column_count(); ?>" class="colspanchange">
1672 <fieldset><div class="inline-edit-col">
1673 <h4><?php _e( 'Quick Edit' ); ?></h4>
1674 <label>
1675 <span class="title"><?php _e( 'Name', 'edit-flow' ); ?></span>
1676 <span class="input-text-wrap"><input type="text" name="name" class="ptitle" value="" maxlength="20" /></span>
1677 </label>
1678 <label>
1679 <span class="title"><?php _e( 'Description', 'edit-flow' ); ?></span>
1680 <span class="input-text-wrap"><input type="text" name="description" class="pdescription" value="" /></span>
1681 </label>
1682 </div></fieldset>
1683 <p class="inline-edit-save submit">
1684 <a accesskey="c" href="#inline-edit" title="<?php _e( 'Cancel' ); ?>" class="cancel button-secondary alignleft"><?php _e( 'Cancel' ); ?></a>
1685 <?php $update_text = __( 'Update Metadata Term', 'edit-flow' ); ?>
1686 <a accesskey="s" href="#inline-edit" title="<?php echo esc_attr( $update_text ); ?>" class="save button-primary alignright"><?php echo $update_text; ?></a>
1687 <img class="waiting" style="display:none;" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
1688 <span class="error" style="display:none;"></span>
1689 <?php wp_nonce_field( 'editorial-metadata-inline-edit-nonce', 'inline_edit', false ); ?>
1690 <br class="clear" />
1691 </p>
1692 </td></tr>
1693 </tbody></table></form>
1694 <?php
1695 }
1696 }
1697