PluginProbe
Edit Flow / 0.9.9
Edit Flow v0.9.9
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 / story-budget / story-budget.php

story-budget.php in Edit Flow 0.9.9, at modules/story-budget/story-budget.php

821 lines 28.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class EF_Story_Budget
4 * This class displays a budgeting system for an editorial desk's publishing workflow.
5 *
6 * @author sbressler
7 */
8 class EF_Story_Budget extends EF_Module {
9
10 var $taxonomy_used = 'category';
11
12 var $module;
13
14 var $num_columns = 0;
15
16 var $max_num_columns;
17
18 var $no_matching_posts = true;
19
20 var $terms = array();
21
22 var $user_filters;
23
24 const screen_id = 'dashboard_page_story-budget';
25
26 const usermeta_key_prefix = 'ef_story_budget_';
27
28 const default_num_columns = 1;
29
30 private $term_columns;
31
32 /**
33 * Register the module with Edit Flow but don't do anything else
34 */
35 function __construct() {
36
37 $this->module_url = $this->get_module_url( __FILE__ );
38 // Register the module with Edit Flow
39 $args = array(
40 'title' => __( 'Story Budget', 'edit-flow' ),
41 'short_description' => sprintf( __( 'View the status of all your content <a href="%s">at a glance</a>.', 'edit-flow' ), admin_url( 'index.php?page=story-budget' ) ),
42 'extended_description' => __( 'Use the story budget to see how content on your site is progressing. Filter by specific categories or date ranges to see details about each post in progress.', 'edit-flow' ),
43 'module_url' => $this->module_url,
44 'img_url' => $this->module_url . 'lib/story_budget_s128.png',
45 'slug' => 'story-budget',
46 'default_options' => array(
47 'enabled' => 'on',
48 ),
49 'configure_page_cb' => false,
50 'autoload' => false,
51 );
52 $this->module = EditFlow()->register_module( 'story_budget', $args );
53
54 }
55
56 /**
57 * Initialize the rest of the stuff in the class if the module is active
58 */
59 function init() {
60
61 $view_story_budget_cap = apply_filters( 'ef_view_story_budget_cap', 'ef_view_story_budget' );
62 if ( !current_user_can( $view_story_budget_cap ) )
63 return;
64
65 $this->num_columns = $this->get_num_columns();
66 $this->max_num_columns = apply_filters( 'ef_story_budget_max_num_columns', 3 );
67
68 // Filter to allow users to pick a taxonomy other than 'category' for sorting their posts
69 $this->taxonomy_used = apply_filters( 'ef_story_budget_taxonomy_used', $this->taxonomy_used );
70
71 add_action( 'admin_init', array( $this, 'handle_form_date_range_change' ) );
72 add_action( 'admin_init', array( $this, 'add_screen_options_panel' ) );
73 // Register the columns of data appearing on every term. This is hooked into admin_init
74 // so other Edit Flow modules can register their filters if needed
75 add_action( 'admin_init', array( $this, 'register_term_columns' ) );
76
77 add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
78 // Load necessary scripts and stylesheets
79 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
80 add_action( 'admin_enqueue_scripts', array( $this, 'action_enqueue_admin_styles' ) );
81
82 }
83
84 /**
85 * Give users the appropriate permissions to view the story budget the first time the module is loaded
86 *
87 * @since 0.7
88 */
89 function install() {
90
91 $story_budget_roles = array(
92 'administrator' => array( 'ef_view_story_budget' ),
93 'editor' => array( 'ef_view_story_budget' ),
94 'author' => array( 'ef_view_story_budget' ),
95 'contributor' => array( 'ef_view_story_budget' )
96 );
97 foreach( $story_budget_roles as $role => $caps ) {
98 $this->add_caps_to_role( $role, $caps );
99 }
100 }
101
102 /**
103 * Upgrade our data in case we need to
104 *
105 * @since 0.7
106 */
107 function upgrade( $previous_version ) {
108 global $edit_flow;
109
110 // Upgrade path to v0.7
111 if ( version_compare( $previous_version, '0.7' , '<' ) ) {
112 // Migrate whether the story budget was enabled or not and clean up old option
113 if ( $enabled = get_option( 'edit_flow_story_budget_enabled' ) )
114 $enabled = 'on';
115 else
116 $enabled = 'off';
117 $edit_flow->update_module_option( $this->module->name, 'enabled', $enabled );
118 delete_option( 'edit_flow_story_budget_enabled' );
119
120 // Technically we've run this code before so we don't want to auto-install new data
121 $edit_flow->update_module_option( $this->module->name, 'loaded_once', true );
122 }
123
124 }
125
126 /**
127 * Include the story budget link in the admin menu.
128 *
129 * @uses add_submenu_page()
130 */
131 function action_admin_menu() {
132 add_submenu_page( 'index.php', __('Story Budget', 'edit-flow'), __('Story Budget', 'edit-flow'), apply_filters( 'ef_view_story_budget_cap', 'ef_view_story_budget' ), $this->module->slug, array( $this, 'story_budget') );
133 }
134
135 /**
136 * Enqueue necessary admin scripts only on the story budget page.
137 *
138 * @uses enqueue_admin_script()
139 */
140 function enqueue_admin_scripts() {
141 global $current_screen;
142
143 if ( $current_screen->id != self::screen_id )
144 return;
145
146 $num_columns = $this->get_num_columns();
147 echo '<script type="text/javascript"> var ef_story_budget_number_of_columns="' . esc_js( $this->num_columns ) . '";</script>';
148
149 $this->enqueue_datepicker_resources();
150 wp_enqueue_script( 'edit_flow-story_budget', $this->module_url . 'lib/story-budget.js', array( 'edit_flow-date_picker' ), EDIT_FLOW_VERSION, true );
151 }
152
153 /**
154 * Enqueue a screen and print stylesheet for the story budget.
155 */
156 function action_enqueue_admin_styles() {
157 global $current_screen;
158
159 if ( $current_screen->id != self::screen_id )
160 return;
161
162 wp_enqueue_style( 'edit_flow-story_budget-styles', $this->module_url . 'lib/story-budget.css', false, EDIT_FLOW_VERSION, 'screen' );
163 wp_enqueue_style( 'edit_flow-story_budget-print-styles', $this->module_url . 'lib/story-budget-print.css', false, EDIT_FLOW_VERSION, 'print' );
164 }
165
166 /**
167 * Register the columns of information that appear for each term module.
168 * Modeled after how WP_List_Table works, but focused on hooks instead of OOP extending
169 *
170 * @since 0.7
171 */
172 function register_term_columns() {
173
174 $term_columns = array(
175 'title' => __( 'Title', 'edit-flow' ),
176 'status' => __( 'Status', 'edit-flow' ),
177 'author' => __( 'Author', 'edit-flow' ),
178 'post_date' => __( 'Post Date', 'edit-flow' ),
179 'post_modified' => __( 'Last Modified', 'edit-flow' ),
180 );
181
182 $term_columns = apply_filters( 'ef_story_budget_term_columns', $term_columns );
183 $this->term_columns = $term_columns;
184 }
185
186 /**
187 * Handle a form submission to change the user's date range on the budget
188 *
189 * @since 0.7
190 */
191 function handle_form_date_range_change() {
192
193 if ( ! isset( $_POST['ef-story-budget-range-submit'], $_POST['ef-story-budget-number-days'], $_POST['ef-story-budget-start-date_hidden'] ) ) {
194 return;
195 }
196
197 if ( !wp_verify_nonce( $_POST['nonce'], 'change-date' ) )
198 wp_die( $this->module->messages['nonce-failed'] );
199
200 $current_user = wp_get_current_user();
201 $new_filters = array (
202 'start_date' => $_POST['ef-story-budget-start-date_hidden'],
203 'number_days' => (int) $_POST['ef-story-budget-number-days'],
204 );
205 $user_filters = $this->update_user_filters_from_form_date_range_change( $current_user, $new_filters );
206
207 $this->update_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', $user_filters );
208 wp_redirect( menu_page_url( $this->module->slug, false ) );
209 exit;
210 }
211
212 /**
213 * Handles updating the users
214 */
215 public function update_user_filters_from_form_date_range_change( $current_user, $new_filters ) {
216 $existing_filters = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', true );
217
218 // Default start date value
219 if ( isset( $new_filters['start_date'] ) ) {
220 // Validate that it's a legitimate date
221 $valid_date = DateTime::createFromFormat( 'Y-m-d', $new_filters['start_date'] );
222
223 if ( false === $valid_date ) {
224 $start_date = date_i18n( 'Y-m-d' );
225 } else {
226 $start_date = $valid_date->format( 'Y-m-d' );
227 }
228
229 // Set the start_date filter (to new value or default)
230 $existing_filters['start_date'] = $start_date;
231 }
232
233 if ( isset( $new_filters['number_days'] ) ) {
234 $new_filters['number_days'] = intval( $new_filters['number_days'] );
235 if ( $new_filters['number_days'] <= 1 ) {
236 $existing_filters['number_days'] = 1;
237 } else {
238 $existing_filters['number_days'] = $new_filters['number_days'];
239 }
240 }
241
242 return $existing_filters;
243 }
244
245 /**
246 * Get the number of columns to show on the story budget
247 */
248 function get_num_columns() {
249
250 if ( empty( $this->num_columns ) ) {
251 $current_user = wp_get_current_user();
252 $this->num_columns = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'screen_columns', true );
253 // If usermeta didn't have a value already, use a default value and insert into DB
254 if ( empty( $this->num_columns ) ) {
255 $this->num_columns = self::default_num_columns;
256 $this->save_column_prefs( array( self::usermeta_key_prefix . 'screen_columns' => $this->num_columns ) );
257 }
258 }
259 return $this->num_columns;
260 }
261
262 /**
263 * Add module options to the screen panel
264 *
265 * @since 0.8.3
266 */
267 function add_screen_options_panel() {
268 require_once( EDIT_FLOW_ROOT . '/common/php/' . 'screen-options.php' );
269 add_screen_options_panel( self::usermeta_key_prefix . 'screen_columns', __( 'Screen Layout', 'edit-flow' ), array( $this, 'print_column_prefs' ), self::screen_id, array( $this, 'save_column_prefs' ), true );
270 }
271
272 /**
273 * Print column number preferences for screen options
274 */
275 function print_column_prefs() {
276 $return_val = __( 'Number of Columns: ', 'edit-flow' );
277 for ( $i = 1; $i <= $this->max_num_columns; ++$i ) {
278 $return_val .= "<label><input type='radio' name='" . esc_attr( self::usermeta_key_prefix ) . "screen_columns' value='" . esc_attr( $i ) . "' " . checked($this->get_num_columns(), $i, false) . " />&nbsp;" . esc_attr( $i ) . "</label>\n";
279 }
280 return $return_val;
281 }
282
283 /**
284 * Save the current user's preference for number of columns.
285 */
286 function save_column_prefs( $posted_fields ) {
287
288 $key = self::usermeta_key_prefix . 'screen_columns';
289 $this->num_columns = (int) $posted_fields[ $key ];
290
291 $current_user = wp_get_current_user();
292 $this->update_user_meta( $current_user->ID, $key, $this->num_columns );
293 }
294
295 /**
296 * Create the story budget view. This calls lots of other methods to do its work. This will
297 * ouput any messages, create the table navigation, then print the columns based on
298 * get_num_columns(), which will in turn print the stories themselves.
299 */
300 function story_budget() {
301
302 // Update the current user's filters with the variables set in $_GET
303 $this->user_filters = $this->update_user_filters();
304
305 if ( !empty( $this->user_filters[$this->taxonomy_used] ) ) {
306 $terms = array();
307 $terms[] = get_term( $this->user_filters[$this->taxonomy_used], $this->taxonomy_used );
308 } else {
309 // Get all of the terms from the taxonomy, regardless whether there are published posts
310 $args = array(
311 'orderby' => 'name',
312 'order' => 'asc',
313 'hide_empty' => 0,
314 'parent' => 0,
315 );
316 $terms = get_terms( $this->taxonomy_used, $args );
317 }
318 $this->terms = apply_filters( 'ef_story_budget_filter_terms', $terms ); // allow for reordering or any other filtering of terms
319
320 ?>
321 <div class="wrap" id="ef-story-budget-wrap">
322 <div id="ef-story-budget-title">
323 <?php echo '<img src="' . esc_url( $this->module->img_url ) . '" class="module-icon icon32" />'; ?>
324 <h2><?php _e( 'Story Budget', 'edit-flow' ); ?>&nbsp;<span class="time-range"><?php $this->story_budget_time_range(); ?></span></h2>
325 </div><!-- /Story Budget Title -->
326 <?php $this->print_messages(); ?>
327 <?php $this->table_navigation(); ?>
328 <div class="metabox-holder">
329 <?php
330 echo '<div class="postbox-container columns-number-' . absint( $this->num_columns ) . '">';
331 foreach( (array) $this->terms as $term ) {
332 $this->print_term( $term );
333 }
334
335 echo '</div>';
336 ?>
337 <style>
338 <?php
339 for ( $i = 1; $i <= $this->max_num_columns; ++$i ) {
340 ?>
341 .columns-number-<?php echo (int) $i; ?> .postbox {
342 flex-basis: <?php echo 99 / $i ?>%;
343 }
344 <?php
345 }
346 ?>
347 </style>
348 </div>
349 </div>
350 <?php
351 }
352
353 /**
354 * Allow the user to define the date range in a new and exciting way
355 *
356 * @since 0.7
357 */
358 function story_budget_time_range() {
359 ?>
360 <form method="POST" action="<?php echo esc_attr( menu_page_url( $this->module->slug, false ) ); ?>">
361 <?php _e( 'starting', 'edit-flow' ); ?>
362 <span class="form-value"><?php echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $this->user_filters['start_date'] ) ) ); ?></span>
363 <input type="text" id="ef-story-budget-start-date" name="ef-story-budget-start-date" size="20" autocomplete="off" class="date-pick" value="<?php echo esc_attr( date_i18n( get_option( 'date_format' ), strtotime( $this->user_filters['start_date'] ) ) ); ?>" />
364 <input type="hidden" id="ef-story-budget-start-date_hidden" name="ef-story-budget-start-date_hidden" />
365 <?php _e( 'showing', 'edit-flow' ); ?>
366 <input type="text" id="ef-story-budget-number-days" name="ef-story-budget-number-days" size="3" maxlength="3" value="<?php echo esc_attr( $this->user_filters['number_days'] ); ?>" />
367 <span class="form-value"><?php echo esc_html( $this->user_filters['number_days'] ); ?></span>
368 <?php echo esc_html( _n( 'day', 'days', $this->user_filters['number_days'], 'edit-flow' ) ); ?>
369 <span class="change-date-buttons">
370 <input id="ef-story-budget-range-submit" name="ef-story-budget-range-submit" type="submit" class="button-primary" value="<?php echo esc_attr( __( 'Change', 'edit-flow' ) ); ?>" />
371 <a class="change-date-cancel hidden" href="#"><?php echo esc_html( __( 'Cancel', 'edit-flow' ) ); ?></a>
372 <a class="change-date" href="#"><?php echo esc_html( __( 'Change', 'edit-flow' ) ); ?></a>
373 </span>
374 <?php wp_nonce_field( 'change-date', 'nonce', 'change-date-nonce', true ); ?>
375 </form>
376 <?php
377 }
378
379 /**
380 * Get all of the posts for a given term based on filters
381 *
382 * @param object $term The term we're getting posts for
383 * @return array $term_posts An array of post objects for the term
384 */
385 function get_posts_for_term( $term, $args = null ) {
386
387 $defaults = array(
388 'post_status' => null,
389 'author' => null,
390 'posts_per_page' => apply_filters( 'ef_story_budget_max_query', 200 ),
391 );
392 $args = array_merge( $defaults, $args );
393
394 // Filter to the term and any children if it's hierarchical
395 $arg_terms = array(
396 $term->term_id,
397 );
398 $arg_terms = array_merge( $arg_terms, get_term_children( $term->term_id, $this->taxonomy_used ) ) ;
399 $args['tax_query'] = array(
400 array(
401 'taxonomy' => $this->taxonomy_used,
402 'field' => 'id',
403 'terms' => $arg_terms,
404 'operator' => 'IN',
405 ),
406 );
407
408 // Unpublished as a status is just an array of everything but 'publish'.
409 if ( 'unpublish' == $args['post_status'] ) {
410 $args['post_status'] = '';
411 $post_stati = wp_filter_object_list( $this->get_budget_post_stati(), array( 'name' => 'publish' ), 'not' );
412
413 if ( ! apply_filters( 'ef_show_scheduled_as_unpublished', false ) ) {
414 $post_stati = wp_filter_object_list( $post_stati, array( 'name' => 'future' ), 'not' );
415 }
416
417 $args['post_status'] .= implode( ',', wp_list_pluck( $post_stati, 'name' ) );
418 }
419
420 // Filter by post_author if it's set
421 if ( $args['author'] === '0' ) unset( $args['author'] );
422
423 $beginning_date = strtotime( $this->user_filters['start_date'] );
424 $days_to_show = $this->user_filters['number_days'];
425 $ending_date = $beginning_date + ( $days_to_show * DAY_IN_SECONDS );
426
427 $args['date_query'] = array(
428 'after' => date( "Y-m-d", $beginning_date ),
429 'before' => date( "Y-m-d", $ending_date ),
430 'inclusive' => true,
431 );
432
433 // Filter for an end user to implement any of their own query args
434 $args = apply_filters( 'ef_story_budget_posts_query_args', $args );
435
436 $term_posts_query_results = new WP_Query( $args );
437
438 $term_posts = array();
439 while ( $term_posts_query_results->have_posts() ) {
440 $term_posts_query_results->the_post();
441 global $post;
442 $term_posts[] = $post;
443 }
444
445 return $term_posts;
446 }
447
448
449 /**
450 * Prints the stories in a single term in the story budget.
451 *
452 * @param object $term The term to print.
453 */
454 function print_term( $term ) {
455 global $wpdb;
456 $posts = $this->get_posts_for_term( $term, $this->user_filters );
457 if ( !empty( $posts ) )
458 // Don't display the message for $no_matching_posts
459 $this->no_matching_posts = false;
460
461 ?>
462 <div class="postbox<?php if ( !empty( $posts )) echo ' postbox-has-posts'; ?>">
463 <div class="handlediv" title="<?php _e( 'Click to toggle', 'edit-flow' ); ?>"><br /></div>
464 <h3 class='hndle'><span><?php echo esc_html( $term->name ); ?></span></h3>
465 <div class="inside">
466 <?php if ( !empty( $posts )) : ?>
467 <table class="widefat post fixed story-budget" cellspacing="0">
468 <thead>
469 <tr>
470 <?php foreach( (array)$this->term_columns as $key => $name ): ?>
471 <th scope="col" id="<?php echo esc_attr( sanitize_key( $key ) ); ?>" class="manage-column column-<?php echo esc_attr( sanitize_key( $key ) ); ?>" ><?php echo esc_html( $name ); ?></th>
472 <?php endforeach; ?>
473 </tr>
474 </thead>
475 <tfoot></tfoot>
476 <tbody>
477 <?php
478 foreach ($posts as $post)
479 $this->print_post( $post, $term );
480 ?>
481 </tbody>
482 </table>
483 <?php else: ?>
484 <div class="message info"><p><?php _e( 'There are no posts for this term in the range or filter specified.', 'edit-flow' ); ?></p></div>
485 <?php endif; ?>
486 </div>
487 </div>
488 <?php
489 }
490
491 /**
492 * Prints a single post within a term in the story budget.
493 *
494 * @param object $post The post to print.
495 * @param object $parent_term The top-level term to which this post belongs.
496 */
497 function print_post( $post, $parent_term ) {
498 ?>
499 <tr id='post-<?php echo esc_attr( $post->ID ); ?>' class='alternate' valign="top">
500 <?php foreach( (array)$this->term_columns as $key => $name ) {
501 echo '<td>';
502 if ( method_exists( $this, 'term_column_' . $key ) ) {
503 $method = 'term_column_' . $key;
504 echo $this->$method( $post, $parent_term );
505 } else {
506 echo $this->term_column_default( $post, $key, $parent_term );
507 }
508 echo '</td>';
509 } ?>
510 </tr>
511 <?php
512 }
513
514 /**
515 * Default callback for producing the HTML for a term column's single post value
516 * Includes a filter other modules can hook into
517 *
518 * @since 0.7
519 *
520 * @param object $post The post we're displaying
521 * @param string $column_name Name of the column, as registered with register_term_columns
522 * @param object $parent_term The parent term for the term column
523 * @return string $output Output value for the term column
524 */
525 function term_column_default( $post, $column_name, $parent_term ) {
526
527 // Hook for other modules to get data into columns
528 $column_value = null;
529 $column_value = apply_filters( 'ef_story_budget_term_column_value', $column_name, $post, $parent_term );
530 if ( !is_null( $column_value ) && $column_value != $column_name )
531 return $column_value;
532
533 switch( $column_name ) {
534 case 'status':
535 $status_name = get_post_status_object( $post->post_status );
536 return $status_name->label;
537 break;
538 case 'author':
539 $post_author = get_userdata( $post->post_author );
540 return $post_author->display_name;
541 break;
542 case 'post_date':
543 $output = get_the_time( get_option( 'date_format' ), $post->ID ) . '<br />';
544 $output .= get_the_time( get_option( 'time_format' ), $post->ID );
545 return $output;
546 break;
547 case 'post_modified':
548 return sprintf( esc_html__( '%s ago', 'edit-flow' ), human_time_diff( get_the_time( 'U', $post->ID ), current_time( 'timestamp' ) ) );
549 break;
550 default:
551 break;
552 }
553
554 }
555
556 /**
557 * Prepare the data for the title term column
558 *
559 * @since 0.7
560 */
561 function term_column_title( $post, $parent_term ) {
562 $post_title = _draft_or_post_title( $post->ID );
563
564 $post_type_object = get_post_type_object( $post->post_type );
565 $can_edit_post = current_user_can( $post_type_object->cap->edit_post, $post->ID );
566 if ( $can_edit_post )
567 $output = '<strong><a href="' . get_edit_post_link( $post->ID ) . '">' . esc_html( $post_title ) . '</a></strong>';
568 else
569 $output = '<strong>' . esc_html( $post_title ) . '</strong>';
570
571 // Edit or Trash or View
572 $output .= '<div class="row-actions">';
573 $item_actions = array();
574 if ( $can_edit_post )
575 $item_actions['edit'] = '<a title="' . __( 'Edit this post', 'edit-flow' ) . '" href="' . get_edit_post_link( $post->ID ) . '">' . __( 'Edit', 'edit-flow' ) . '</a>';
576 if ( EMPTY_TRASH_DAYS > 0 && current_user_can( $post_type_object->cap->delete_post, $post->ID ) )
577 $item_actions['trash'] = '<a class="submitdelete" title="' . __( 'Move this item to the Trash', 'edit-flow' ) . '" href="' . get_delete_post_link( $post->ID ) . '">' . __( 'Trash', 'edit-flow' ) . '</a>';
578
579 // Display a View or a Preview link depending on whether the post has been published or not
580 if ( in_array( $post->post_status, array( 'publish' ) ) )
581 $item_actions['view'] = '<a href="' . get_permalink( $post->ID ) . '" title="' . esc_attr( sprintf( __( 'View &#8220;%s&#8221;', 'edit-flow' ), $post_title ) ) . '" rel="permalink">' . __( 'View', 'edit-flow' ) . '</a>';
582 else if ( $can_edit_post )
583 $item_actions['previewpost'] = '<a href="' . esc_url( apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ), $post ) ) . '" title="' . esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;', 'edit-flow' ), $post_title ) ) . '" rel="permalink">' . __( 'Preview', 'edit-flow' ) . '</a>';
584
585 $item_actions = apply_filters( 'ef_story_budget_item_actions', $item_actions, $post->ID );
586 if ( count( $item_actions ) ) {
587 $output .= '<div class="row-actions">';
588 $html = '';
589 foreach ( $item_actions as $class => $item_action ) {
590 $html .= '<span class="' . esc_attr( $class ) . '">' . $item_action . '</span> | ';
591 }
592 $output .= rtrim( $html, '| ' );
593 $output .= '</div>';
594 }
595
596 return $output;
597 }
598
599 /**
600 * Print any messages that should appear based on the action performed
601 */
602 function print_messages() {
603 ?>
604
605 <?php
606 if ( isset($_GET['trashed']) || isset($_GET['untrashed']) ) {
607
608 echo '<div id="trashed-message" class="updated"><p>';
609
610 // Following mostly stolen from edit.php
611
612 if ( isset( $_GET['trashed'] ) && (int) $_GET['trashed'] ) {
613 printf( _n( 'Item moved to the trash.', '%d items moved to the trash.', $_GET['trashed'] ), number_format_i18n( $_GET['trashed'] ) );
614 $ids = isset($_GET['ids']) ? $_GET['ids'] : 0;
615 echo ' <a href="' . esc_url( wp_nonce_url( "edit.php?post_type=post&doaction=undo&action=untrash&ids=$ids", "bulk-posts" ) ) . '">' . __( 'Undo', 'edit-flow' ) . '</a><br />';
616 unset($_GET['trashed']);
617 }
618
619 if ( isset($_GET['untrashed'] ) && (int) $_GET['untrashed'] ) {
620 printf( _n( 'Item restored from the Trash.', '%d items restored from the Trash.', $_GET['untrashed'] ), number_format_i18n( $_GET['untrashed'] ) );
621 unset($_GET['undeleted']);
622 }
623
624 echo '</p></div>';
625 }
626 }
627
628 /**
629 * Print the table navigation and filter controls, using the current user's filters if any are set.
630 */
631 function table_navigation() {
632 ?>
633 <div class="tablenav" id="ef-story-budget-tablenav">
634 <div class="alignleft actions">
635 <form method="GET" style="float: left;">
636 <input type="hidden" name="page" value="story-budget"/>
637 <?php
638 foreach($this->story_budget_filters() as $select_id => $select_name ) {
639 echo $this->story_budget_filter_options( $select_id, $select_name, $this->user_filters );
640 }
641 ?>
642 <input type="submit" id="post-query-submit" value="<?php _e( 'Filter', 'edit-flow' ); ?>" class="button-primary button" />
643 </form>
644 <form method="GET" style="float: left;">
645 <input type="hidden" name="page" value="story-budget"/>
646 <input type="hidden" name="post_status" value=""/>
647 <input type="hidden" name="cat" value=""/>
648 <input type="hidden" name="author" value=""/>
649 <?php
650 foreach( $this->story_budget_filters() as $select_id => $select_name ) {
651 echo '<input type="hidden" name="'.$select_name.'" value="" />';
652 }
653 ?>
654 <input type="submit" id="post-query-clear" value="<?php _e( 'Reset', 'edit-flow' ); ?>" class="button-secondary button" />
655 </form>
656 </div><!-- /alignleft actions -->
657
658 <div class="print-box" style="float:right; margin-right: 30px;"><!-- Print link -->
659 <a href="#" id="print_link"><?php _e( 'Print', 'edit-flow' ); ?></a>
660 </div>
661 <div class="clear"></div>
662
663 </div><!-- /tablenav -->
664 <?php
665 }
666
667 /**
668 * Update the current user's filters for story budget display with the filters in $_GET. The filters
669 * in $_GET take precedence over the current users filters if they exist.
670 */
671 function update_user_filters() {
672
673 $current_user = wp_get_current_user();
674
675 $user_filters = array(
676 'post_status' => $this->filter_get_param( 'post_status' ),
677 'cat' => $this->filter_get_param( 'cat' ),
678 'author' => $this->filter_get_param( 'author' ),
679 'start_date' => $this->filter_get_param( 'start_date' ),
680 'number_days' => $this->filter_get_param( 'number_days' )
681 );
682
683 $current_user_filters = array();
684 $current_user_filters = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', true );
685
686 // If any of the $_GET vars are missing, then use the current user filter
687 foreach ( $user_filters as $key => $value ) {
688 if ( is_null( $value ) && !empty( $current_user_filters[$key] ) ) {
689 $user_filters[$key] = $current_user_filters[$key];
690 }
691 }
692
693 if ( !$user_filters['start_date'] )
694 $user_filters['start_date'] = date( 'Y-m-d' );
695
696 if ( !$user_filters['number_days'] )
697 $user_filters['number_days'] = 10;
698
699 $user_filters = apply_filters('ef_story_budget_filter_values', $user_filters, $current_user_filters);
700
701 $this->update_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', $user_filters );
702 return $user_filters;
703 }
704
705 /**
706 * Get the filters for the current user for the story budget display, or insert the default
707 * filters if not already set.
708 *
709 * @return array The filters for the current user, or the default filters if the current user has none.
710 */
711 function get_user_filters() {
712
713 $current_user = wp_get_current_user();
714 $user_filters = array();
715 $user_filters = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', true );
716
717 // If usermeta didn't have filters already, insert defaults into DB
718 if ( empty( $user_filters ) )
719 $user_filters = $this->update_user_filters();
720 return $user_filters;
721 }
722
723 /**
724 *
725 * @param string $param The parameter to look for in $_GET
726 * @return null if the parameter is not set in $_GET, empty string if the parameter is empty in $_GET,
727 * or a sanitized version of the parameter from $_GET if set and not empty
728 */
729 function filter_get_param( $param ) {
730 // Sure, this could be done in one line. But we're cooler than that: let's make it more readable!
731 if ( !isset( $_GET[$param] ) ) {
732 return null;
733 } else if ( empty( $_GET[$param] ) ) {
734 return '';
735 }
736
737 return sanitize_key( $_GET[$param] );
738 }
739
740 function story_budget_filters() {
741 $select_filter_names = array();
742
743 $select_filter_names['post_status'] = 'post_status';
744 $select_filter_names['cat'] = 'cat';
745 $select_filter_names['author'] = 'author';
746
747 return apply_filters('ef_story_budget_filter_names', $select_filter_names);
748 }
749
750 function story_budget_filter_options( $select_id, $select_name, $filters ) {
751 switch( $select_id ) {
752 case 'post_status':
753 $post_stati = $this->get_budget_post_stati();
754 ?>
755 <select id="post_status" name="post_status"><!-- Status selectors -->
756 <option value=""><?php _e( 'View all statuses', 'edit-flow' ); ?></option>
757 <?php
758 foreach ( $post_stati as $status ) {
759 echo '<option value="' . esc_attr( $status->name ) . '" ' . selected( $status->name, $filters['post_status'] ) . '>' . esc_html( $status->label ) . '</option>';
760 }
761 echo '<option value="unpublish"' . selected('unpublish', $filters['post_status']) . '>' . __( 'Unpublished', 'edit-flow' ) . '</option>';
762 ?>
763 </select>
764 <?php
765 break;
766 case 'cat':
767 // Borrowed from wp-admin/edit.php
768 if ( taxonomy_exists('category') ) {
769 $category_dropdown_args = array(
770 'show_option_all' => __( 'View all categories', 'edit-flow' ),
771 'hide_empty' => 0,
772 'hierarchical' => 1,
773 'show_count' => 0,
774 'orderby' => 'name',
775 'selected' => $this->user_filters['cat']
776 );
777 wp_dropdown_categories( $category_dropdown_args );
778 }
779 break;
780 case 'author':
781 $users_dropdown_args = array(
782 'show_option_all' => __( 'View all users', 'edit-flow' ),
783 'name' => 'author',
784 'selected' => $this->user_filters['author'],
785 'who' => 'authors',
786 );
787 $users_dropdown_args = apply_filters( 'ef_story_budget_users_dropdown_args', $users_dropdown_args );
788 wp_dropdown_users( $users_dropdown_args );
789 break;
790 default:
791 do_action( 'ef_story_budget_filter_display', $select_id, $select_name, $filters);
792 break;
793 }
794 }
795
796 /**
797 * Returns a list of custom status objects used by the story budget
798 *
799 * @return array An array of StdClass objects representing statuses
800 */
801 public function get_budget_post_stati() {
802 $post_stati = get_post_stati( array(), 'object' );
803 $custom_status_slugs = wp_list_pluck( $this->get_post_statuses(), 'slug' );
804 $custom_status_slugs[] = 'future';
805 $custom_status_slugs[] = 'publish';
806
807 $custom_status_slug_keys = array_flip( $custom_status_slugs );
808
809 $final_statuses = [];
810
811 foreach( $post_stati as $status ) {
812 if ( !empty( $custom_status_slug_keys[ $status->name ] ) ) {
813 $final_statuses[] = $status;
814 }
815 }
816
817 return apply_filters( 'ef_budget_post_stati', $final_statuses );
818 }
819
820 }
821