PluginProbe
Edit Flow / 0.9.6
Edit Flow v0.9.6
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.9.6, at modules/editorial-metadata/editorial-metadata.php

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