PluginProbe
Edit Flow / 0.8
Edit Flow v0.8
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 / calendar / calendar.php

calendar.php in Edit Flow 0.8, at modules/calendar/calendar.php

1,735 lines 66.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class EF_Calendar
4 * This class displays an editorial calendar for viewing upcoming and past content at a glance
5 *
6 * @author danielbachhuber
7 */
8 if ( !class_exists('EF_Calendar') ) {
9
10 class EF_Calendar extends EF_Module {
11
12 const usermeta_key_prefix = 'ef_calendar_';
13 const screen_id = 'dashboard_page_calendar';
14
15 var $module;
16
17 var $start_date = '';
18 var $current_week = 1;
19 var $total_weeks = 6; // default number of weeks to show per screen
20 var $hidden = 0; // counter of hidden posts per date square
21 var $max_visible_posts_per_date = 4; // total number of posts to be shown per square before 'more' link
22
23 /**
24 * Construct the EF_Calendar class
25 */
26 function __construct() {
27 global $edit_flow;
28
29 $this->module_url = $this->get_module_url( __FILE__ );
30 // Register the module with Edit Flow
31 $args = array(
32 'title' => __( 'Calendar', 'edit-flow' ),
33 'short_description' => sprintf( __( 'View upcoming content in a <a href="%s">customizable calendar</a>.', 'edit-flow' ), admin_url( 'index.php?page=calendar' ) ),
34 'extended_description' => __( 'Edit Flow’s calendar lets you see your posts over a customizable date range. Filter by status or click on the post title to see its details. Drag and drop posts between days to change their publication date date.', 'edit-flow' ),
35 'module_url' => $this->module_url,
36 'img_url' => $this->module_url . 'lib/calendar_s128.png',
37 'slug' => 'calendar',
38 'post_type_support' => 'ef_calendar',
39 'default_options' => array(
40 'enabled' => 'on',
41 'post_types' => array(
42 'post' => 'on',
43 'page' => 'off',
44 ),
45 'quick_create_post_type' => 'post',
46 'ics_subscription' => 'off',
47 'ics_secret_key' => '',
48 ),
49 'messages' => array(
50 'post-date-updated' => __( "Post date updated.", 'edit-flow' ),
51 'update-error' => __( 'There was an error updating the post. Please try again.', 'edit-flow' ),
52 'published-post-ajax' => __( "Updating the post date dynamically doesn't work for published content. Please <a href='%s'>edit the post</a>.", 'edit-flow' ),
53 'key-regenerated' => __( 'iCal secret key regenerated. Please inform all users they will need to resubscribe.', 'edit-flow' ),
54 ),
55 'configure_page_cb' => 'print_configure_view',
56 'configure_link_text' => __( 'Calendar Options', 'edit-flow' ),
57 'settings_help_tab' => array(
58 'id' => 'ef-calendar-overview',
59 'title' => __('Overview', 'edit-flow'),
60 'content' => __('<p>The calendar is a convenient week-by-week or month-by-month view into your content. Quickly see which stories are on track to being published on time, and which will need extra effort.</p>', 'edit-flow'),
61 ),
62 'settings_help_sidebar' => __( '<p><strong>For more information:</strong></p><p><a href="http://editflow.org/features/calendar/">Calendar 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' ),
63 );
64 $this->module = EditFlow()->register_module( 'calendar', $args );
65
66 }
67
68 /**
69 * Initialize all of our methods and such. Only runs if the module is active
70 *
71 * @uses add_action()
72 */
73 function init() {
74
75 // .ics calendar subscriptions
76 add_action( 'wp_ajax_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) );
77 add_action( 'wp_ajax_nopriv_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) );
78
79 // Check whether the user should have the ability to view the calendar
80 $view_calendar_cap = 'ef_view_calendar';
81 $view_calendar_cap = apply_filters( 'ef_view_calendar_cap', $view_calendar_cap );
82 if ( !current_user_can( $view_calendar_cap ) ) return false;
83
84 // Define the create-post capability
85 $this->create_post_cap = apply_filters( 'ef_calendar_create_post_cap', 'edit_posts' );
86
87 require_once( EDIT_FLOW_ROOT . '/common/php/' . 'screen-options.php' );
88 add_screen_options_panel( self::usermeta_key_prefix . 'screen_options', __( 'Calendar Options', 'edit-flow' ), array( $this, 'generate_screen_options' ), self::screen_id, false, true );
89 add_action( 'admin_init', array( $this, 'handle_save_screen_options' ) );
90
91 add_action( 'admin_init', array( $this, 'register_settings' ) );
92 add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
93 add_action( 'admin_print_styles', array( $this, 'add_admin_styles' ) );
94 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
95
96 // Ajax manipulation for the calendar
97 add_action( 'wp_ajax_ef_calendar_drag_and_drop', array( $this, 'handle_ajax_drag_and_drop' ) );
98
99 // Ajax insert post placeholder for a specific date
100 add_action( 'wp_ajax_ef_insert_post', array( $this, 'handle_ajax_insert_post' ) );
101
102 //Update metadata
103 add_action( 'wp_ajax_ef_calendar_update_metadata', array( $this, 'handle_ajax_update_metadata' ) );
104
105 // Action to regenerate the calendar feed sekret
106 add_action( 'admin_init', array( $this, 'handle_regenerate_calendar_feed_secret' ) );
107 }
108
109 /**
110 * Load the capabilities onto users the first time the module is run
111 *
112 * @since 0.7
113 */
114 function install() {
115
116 // Add necessary capabilities to allow management of calendar
117 // view_calendar - administrator --> contributor
118 $calendar_roles = array(
119 'administrator' => array('ef_view_calendar'),
120 'editor' => array('ef_view_calendar'),
121 'author' => array('ef_view_calendar'),
122 'contributor' => array('ef_view_calendar')
123 );
124
125 foreach ( $calendar_roles as $role => $caps ) {
126 $this->add_caps_to_role( $role, $caps );
127 }
128 }
129
130 /**
131 * Upgrade our data in case we need to
132 *
133 * @since 0.7
134 */
135 function upgrade( $previous_version ) {
136 global $edit_flow;
137
138 // Upgrade path to v0.7
139 if ( version_compare( $previous_version, '0.7' , '<' ) ) {
140 // Migrate whether the calendar was enabled or not and clean up old option
141 if ( $enabled = get_option( 'edit_flow_calendar_enabled' ) )
142 $enabled = 'on';
143 else
144 $enabled = 'off';
145 $edit_flow->update_module_option( $this->module->name, 'enabled', $enabled );
146 delete_option( 'edit_flow_calendar_enabled' );
147
148 // Technically we've run this code before so we don't want to auto-install new data
149 $edit_flow->update_module_option( $this->module->name, 'loaded_once', true );
150 }
151
152 }
153
154 /**
155 * Add the calendar link underneath the "Dashboard"
156 *
157 * @uses add_submenu_page
158 */
159 function action_admin_menu() {
160 add_submenu_page('index.php', __('Calendar', 'edit-flow'), __('Calendar', 'edit-flow'), apply_filters( 'ef_view_calendar_cap', 'ef_view_calendar' ), $this->module->slug, array( $this, 'view_calendar' ) );
161 }
162
163 /**
164 * Add any necessary CSS to the WordPress admin
165 *
166 * @uses wp_enqueue_style()
167 */
168 function add_admin_styles() {
169 global $pagenow;
170 // Only load calendar styles on the calendar page
171 if ( $pagenow == 'index.php' && isset( $_GET['page'] ) && $_GET['page'] == 'calendar' )
172 wp_enqueue_style( 'edit-flow-calendar-css', $this->module_url . 'lib/calendar.css', false, EDIT_FLOW_VERSION );
173 }
174
175 /**
176 * Add any necessary JS to the WordPress admin
177 *
178 * @since 0.7
179 * @uses wp_enqueue_script()
180 */
181 function enqueue_admin_scripts() {
182
183 $this->enqueue_datepicker_resources();
184
185 if ( $this->is_whitelisted_functional_view() ) {
186 $js_libraries = array(
187 'jquery',
188 'jquery-ui-core',
189 'jquery-ui-sortable',
190 'jquery-ui-draggable',
191 'jquery-ui-droppable',
192 );
193 foreach( $js_libraries as $js_library ) {
194 wp_enqueue_script( $js_library );
195 }
196 wp_enqueue_script( 'edit-flow-calendar-js', $this->module_url . 'lib/calendar.js', $js_libraries, EDIT_FLOW_VERSION, true );
197
198 $ef_cal_js_params = array( 'can_add_posts' => current_user_can( $this->create_post_cap ) ? 'true' : 'false' );
199 wp_localize_script( 'edit-flow-calendar-js', 'ef_calendar_params', $ef_cal_js_params );
200 }
201
202 }
203
204 /**
205 * Prepare the options that need to appear in Screen Options
206 *
207 * @since 0.7
208 */
209 function generate_screen_options() {
210
211 $output = '';
212 $screen_options = $this->get_screen_options();
213
214 $output .= __( 'Number of Weeks: ', 'edit-flow' );
215 $output .= '<select id="' . self::usermeta_key_prefix . 'num_weeks" name="' . self::usermeta_key_prefix . 'num_weeks">';
216 for( $i = 1; $i <= 12; $i++ ) {
217 $output .= '<option value="' . esc_attr( $i ) . '" ' . selected( $i, $screen_options['num_weeks'], false ) . '>' . esc_attr( $i ) . '</option>';
218 }
219 $output .= '</select>';
220
221 $output .= '&nbsp;&nbsp;&nbsp;<input id="screen-options-apply" name="screen-options-apply" type="submit" value="' . __( 'Apply' ) . '" class="button-secondary" />';
222
223 if ( 'on' == $this->module->options->ics_subscription && $this->module->options->ics_secret_key ) {
224 $args = array(
225 'action' => 'ef_calendar_ics_subscription',
226 'user' => wp_get_current_user()->user_login,
227 'user_key' => md5( wp_get_current_user()->user_login . $this->module->options->ics_secret_key ),
228 );
229 $subscription_link = add_query_arg( $args, admin_url( 'admin-ajax.php' ) );
230 $output .= '<br />';
231 $output .= __( 'Subscribe in iCal or Google Calendar', 'edit-flow' );
232 $output .= ':<br /><input type="text" size="100" value="' . esc_attr( $subscription_link ) . '" />';
233 }
234
235 return $output;
236 }
237
238 /**
239 * Handle the request to save the screen options
240 *
241 * @since 0.7
242 */
243 function handle_save_screen_options() {
244
245 // Only handle screen options submissions from the current screen
246 if ( !isset( $_POST['screen-options-apply'], $_POST['ef_calendar_num_weeks'] ) )
247 return;
248
249 // Nonce check
250 if ( !wp_verify_nonce( $_POST['_wpnonce-' . self::usermeta_key_prefix . 'screen_options'], 'save_settings-' . self::usermeta_key_prefix . 'screen_options' ) )
251 wp_die( $this->module->messages['nonce-failed'] );
252
253 // Get the current screen options
254 $screen_options = $this->get_screen_options();
255
256 // Save the number of weeks to show
257 $screen_options['num_weeks'] = (int)$_POST['ef_calendar_num_weeks'];
258
259 // Save the screen options
260 $current_user = wp_get_current_user();
261 $this->update_user_meta( $current_user->ID, self::usermeta_key_prefix . 'screen_options', $screen_options );
262
263 // Redirect after we're complete
264 $redirect_to = menu_page_url( $this->module->slug, false );
265 wp_redirect( $redirect_to );
266 exit;
267 }
268
269 /**
270 * Handle an AJAX request from the calendar to update a post's timestamp.
271 * Notes:
272 * - For Post Time, if the post is unpublished, the change sets the publication timestamp
273 * - If the post was published or scheduled for the future, the change will change the timestamp. 'publish' posts
274 * will become scheduled if moved past today and 'future' posts will be published if moved before today
275 * - Need to respect user permissions. Editors can move all, authors can move their own, and contributors can't move at all
276 *
277 * @since 0.7
278 */
279 function handle_ajax_drag_and_drop() {
280 global $wpdb;
281
282 // Nonce check!
283 if ( !wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) )
284 $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] );
285
286 // Check that we got a proper post
287 $post_id = (int)$_POST['post_id'];
288 $post = get_post( $post_id );
289 if ( !$post )
290 $this->print_ajax_response( 'error', $this->module->messages['missing-post'] );
291
292 // Check that the user can modify the post
293 if ( !$this->current_user_can_modify_post( $post ) )
294 $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] );
295
296 // Check that it's not yet published
297 if ( in_array( $post->post_status, $this->published_statuses ) )
298 $this->print_ajax_response( 'error', sprintf( $this->module->messages['published-post-ajax'], get_edit_post_link( $post_id ) ) );
299
300 // Check that the new date passed is a valid one
301 $next_date_full = strtotime( $_POST['next_date'] );
302 if ( !$next_date_full )
303 $this->print_ajax_response( 'error', __( 'Something is wrong with the format for the new date.', 'edit-flow' ) );
304
305 // Persist the old hourstamp because we can't manipulate the exact time on the calendar
306 // Bump the last modified timestamps too
307 $existing_time = date( 'H:i:s', strtotime( $post->post_date ) );
308 $existing_time_gmt = date( 'H:i:s', strtotime( $post->post_date_gmt ) );
309 $new_values = array(
310 'post_date' => date( 'Y-m-d', $next_date_full ) . ' ' . $existing_time,
311 'post_modified' => current_time( 'mysql' ),
312 'post_modified_gmt' => current_time( 'mysql', 1 ),
313 );
314
315 // By default, changing a post on the calendar won't set the timestamp.
316 // If the user desires that to be the behaviour, they can set the result of this filter to 'true'
317 // With how WordPress works internally, setting 'post_date_gmt' will set the timestamp
318 if ( apply_filters( 'ef_calendar_allow_ajax_to_set_timestamp', false ) )
319 $new_values['post_date_gmt'] = date( 'Y-m-d', $next_date_full ) . ' ' . $existing_time_gmt;
320
321 // We have to do SQL unfortunately because of core bugginess
322 // Note to those reading this: bug Nacin to allow us to finish the custom status API
323 // See http://core.trac.wordpress.org/ticket/18362
324 $response = $wpdb->update( $wpdb->posts, $new_values, array( 'ID' => $post->ID ) );
325 if ( !$response )
326 $this->print_ajax_response( 'error', $this->module->messages['update-error'] );
327
328 $this->print_ajax_response( 'success', $this->module->messages['post-date-updated'] );
329 exit;
330 }
331
332 /**
333 * After checking that the request is valid, do an .ics file
334 *
335 * @since 0.8
336 */
337 function handle_ics_subscription() {
338
339 // Only do .ics subscriptions when the option is active
340 if ( 'on' != $this->module->options->ics_subscription )
341 die(); // @todo return accepted response value.
342
343 // Confirm all of the arguments are present
344 if ( ! isset( $_GET['user'], $_GET['user_key'] ) )
345 die(); // @todo return an error response
346
347 // Confirm this is a valid request
348 $user = sanitize_user( $_GET['user'] );
349 $user_key = sanitize_user( $_GET['user_key'] );
350 $ics_secret_key = $this->module->options->ics_secret_key;
351 if ( ! $ics_secret_key || md5( $user . $ics_secret_key ) !== $user_key )
352 die( $this->module->messages['nonce-failed'] );
353
354 // Set up the post data to be printed
355 $post_query_args = array();
356 $calendar_filters = $this->calendar_filters();
357 foreach( $calendar_filters as $filter ) {
358 if ( isset( $_GET[$filter] ) && false !== ( $value = $this->sanitize_filter( $filter, $_GET[$filter] ) ) )
359 $post_query_args[$filter] = $value;
360 }
361
362 // Set the start date for the posts_where filter
363 $this->start_date = apply_filters( 'ef_calendar_ics_subscription_start_date', $this->get_beginning_of_week( date( 'Y-m-d', current_time( 'timestamp' ) ) ) );
364
365 $this->total_weeks = apply_filters( 'ef_calendar_total_weeks', $this->total_weeks, 'ics_subscription' );
366
367 $formatted_posts = array();
368 for( $current_week = 1; $current_week <= $this->total_weeks; $current_week++ ) {
369 // We need to set the object variable for our posts_where filter
370 $this->current_week = $current_week;
371 $week_posts = $this->get_calendar_posts_for_week( $post_query_args, 'ics_subscription' );
372 foreach( $week_posts as $date => $day_posts ) {
373 foreach( $day_posts as $num => $post ) {
374
375 $start_date = date( 'Ymd', strtotime( $post->post_date ) ) . 'T' . date( 'His', strtotime( $post->post_date ) ) . 'Z';
376 $end_date = date( 'Ymd', strtotime( $post->post_date ) + (5 * 60) ) . 'T' . date( 'His', strtotime( $post->post_date ) + (5 * 60) ) . 'Z';
377 $last_modified = date( 'Ymd', strtotime( $post->post_modified_gmt ) ) . 'T' . date( 'His', strtotime( $post->post_modified_gmt ) ) . 'Z';
378
379 // Remove the convert chars and wptexturize filters from the title
380 remove_filter( 'the_title', 'convert_chars' );
381 remove_filter( 'the_title', 'wptexturize' );
382
383 $formatted_post = array(
384 'BEGIN' => 'VEVENT',
385 'UID' => $post->guid,
386 'SUMMARY' => $this->do_ics_escaping( apply_filters( 'the_title', $post->post_title ) ) . ' - ' . $this->get_post_status_friendly_name( get_post_status( $post->ID ) ),
387 'DTSTART' => $start_date,
388 'DTEND' => $end_date,
389 'LAST-MODIFIED' => $last_modified,
390 'URL' => get_post_permalink( $post->ID ),
391 );
392
393 // Description should include everything visible in the calendar popup
394 $information_fields = $this->get_post_information_fields( $post );
395 $formatted_post['DESCRIPTION'] = '';
396 if ( ! empty( $information_fields ) ) {
397 foreach( $information_fields as $key => $values ) {
398 $formatted_post['DESCRIPTION'] .= $values['label'] . ': ' . $values['value'] . '\n';
399 }
400 $formatted_post['DESCRIPTION'] = rtrim( $formatted_post['DESCRIPTION'] );
401 }
402
403 $formatted_post['END'] = 'VEVENT';
404
405 // @todo auto format any field longer than 75 bytes
406
407 $formatted_posts[] = $formatted_post;
408 }
409 }
410 }
411
412 // Other template data
413 $header = array(
414 'BEGIN' => 'VCALENDAR',
415 'VERSION' => '2.0',
416 'PRODID' => '-//Edit Flow//Edit Flow ' . EDIT_FLOW_VERSION . '//EN',
417 );
418
419 $footer = array(
420 'END' => 'VCALENDAR',
421 );
422
423 // Render the .ics template and set the content type
424 header( 'Content-type: text/calendar' );
425 foreach( array( $header, $formatted_posts, $footer ) as $section ) {
426 foreach( $section as $key => $value ) {
427 if ( is_string( $value ) )
428 echo $this->do_ics_line_folding( $key . ':' . $value );
429 else
430 foreach( $value as $k => $v ) {
431 echo $this->do_ics_line_folding( $k . ':' . $v );
432 }
433 }
434 }
435 die();
436
437 }
438
439 /**
440 * Perform line folding according to RFC 5545.
441 *
442 * @param string $line The line without trailing CRLF
443 * @return string The line after line-folding with all necessary CRLF.
444 */
445 function do_ics_line_folding( $line ) {
446 $len = mb_strlen( $line );
447 if ( $len <= 75) {
448 return $line . "\r\n";
449 }
450
451 $chunks = array();
452 $start = 0;
453 while( true ) {
454 $chunk = mb_substr( $line, $start, 75 );
455 $chunkLen = mb_strlen( $chunk );
456 $start += $chunkLen;
457 if ( $start < $len ) {
458 $chunks[] = $chunk . "\r\n ";
459 }
460 else {
461 $chunks[] = $chunk ."\r\n";
462 return implode( "", $chunks );
463 }
464 }
465 }
466
467 /**
468 * Perform the encoding necessary for ICS feed text.
469 *
470 * @param string $text The string that needs to be escaped
471 * @return string The string after escaping for ICS.
472 * @since 0.8
473 * */
474
475 function do_ics_escaping( $text ) {
476 $text = str_replace( ",", "\,", $text );
477 $text = str_replace( ";", "\:", $text );
478 $text = str_replace( "\\", "\\\\", $text );
479 return $text;
480 }
481
482 /**
483 * Handle a request to regenerate the calendar feed secret
484 *
485 * @since 0.8
486 */
487 public function handle_regenerate_calendar_feed_secret() {
488
489 if ( ! isset( $_GET['action'] ) || 'ef_calendar_regenerate_calendar_feed_secret' != $_GET['action'] )
490 return;
491
492 if ( ! current_user_can( 'manage_options' ) )
493 wp_die( $this->module->messages['invalid-permissions'] );
494
495 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'ef-regenerate-ics-key' ) )
496 wp_die( $this->module->messages['nonce-failed'] );
497
498 EditFlow()->update_module_option( $this->module->name, 'ics_secret_key', wp_generate_password() );
499
500 wp_safe_redirect( add_query_arg( 'message', 'key-regenerated', menu_page_url( $this->module->settings_slug, false ) ) );
501 exit;
502 }
503
504 /**
505 * Get a user's screen options
506 *
507 * @since 0.7
508 * @uses get_user_meta()
509 *
510 * @return array $screen_options The screen options values
511 */
512 function get_screen_options() {
513
514 $defaults = array(
515 'num_weeks' => (int)$this->total_weeks,
516 );
517 $current_user = wp_get_current_user();
518 $screen_options = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'screen_options', true );
519 $screen_options = array_merge( (array)$defaults, (array)$screen_options );
520
521 return $screen_options;
522 }
523
524 /**
525 * Get the user's filters for calendar, either with $_GET or from saved
526 *
527 * @uses get_user_meta()
528 * @return array $filters All of the set or saved calendar filters
529 */
530 function get_filters() {
531
532 $current_user = wp_get_current_user();
533 $filters = array();
534 $old_filters = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', true );
535
536 $default_filters = array(
537 'post_status' => '',
538 'cpt' => '',
539 'cat' => '',
540 'author' => '',
541 'start_date' => date( 'Y-m-d', current_time( 'timestamp' ) ),
542 );
543 $old_filters = array_merge( $default_filters, (array)$old_filters );
544
545 // Sanitize and validate any newly added filters
546 foreach( $old_filters as $key => $old_value ) {
547 if ( isset( $_GET[$key] ) && false !== ( $new_value = $this->sanitize_filter( $key, $_GET[$key] ) ) )
548 $filters[$key] = $new_value;
549 else
550 $filters[$key] = $old_value;
551 }
552
553 // Set the start date as the beginning of the week, according to blog settings
554 $filters['start_date'] = $this->get_beginning_of_week( $filters['start_date'] );
555
556 $filters = apply_filters( 'ef_calendar_filter_values', $filters, $old_filters );
557
558 $this->update_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', $filters );
559
560 return $filters;
561 }
562
563 /**
564 * Build all of the HTML for the calendar view
565 */
566 function view_calendar() {
567
568 $this->dropdown_taxonomies = array();
569
570 $supported_post_types = $this->get_post_types_for_module( $this->module );
571
572 // Get the user's screen options for displaying the data
573 $screen_options = $this->get_screen_options();
574 // Total number of weeks to display on the calendar. Run it through a filter in case we want to override the
575 // user's standard
576 $this->total_weeks = apply_filters( 'ef_calendar_total_weeks', $screen_options['num_weeks'], 'dashboard' );
577
578 $dotw = array(
579 'Sat',
580 'Sun',
581 );
582 $dotw = apply_filters( 'ef_calendar_weekend_days', $dotw );
583
584 // Get filters either from $_GET or from user settings
585 $filters = $this->get_filters();
586 // For generating the WP Query objects later on
587 $post_query_args = array(
588 'post_status' => $filters['post_status'],
589 'post_type' => $filters['cpt'],
590 'cat' => $filters['cat'],
591 'author' => $filters['author']
592 );
593 $this->start_date = $filters['start_date'];
594
595 // We use this later to label posts if they need labeling
596 if ( count( $supported_post_types ) > 1 ) {
597 $all_post_types = get_post_types( null, 'objects' );
598 }
599 $dates = array();
600 $heading_date = $filters['start_date'];
601 for ( $i=0; $i<7; $i++ ) {
602 $dates[$i] = $heading_date;
603 $heading_date = date( 'Y-m-d', strtotime( "+1 day", strtotime( $heading_date ) ) );
604 }
605
606 // we sort by post statuses....... eventually
607 $post_statuses = $this->get_post_statuses();
608 ?>
609 <div class="wrap">
610 <div id="ef-calendar-title"><!-- Calendar Title -->
611 <?php echo '<img src="' . esc_url( $this->module->img_url ) . '" class="module-icon icon32" />'; ?>
612 <h2><?php _e( 'Calendar', 'edit-flow' ); ?>&nbsp;<span class="time-range"><?php $this->calendar_time_range(); ?></span></h2>
613 </div><!-- /Calendar Title -->
614
615 <?php
616 // Handle posts that have been trashed or untrashed
617 if ( isset( $_GET['trashed'] ) || isset( $_GET['untrashed'] ) ) {
618
619 echo '<div id="trashed-message" class="updated"><p>';
620 if ( isset( $_GET['trashed'] ) && (int) $_GET['trashed'] ) {
621 printf( _n( 'Post moved to the trash.', '%d posts moved to the trash.', $_GET['trashed'] ), number_format_i18n( $_GET['trashed'] ) );
622 $ids = isset($_GET['ids']) ? $_GET['ids'] : 0;
623 $pid = explode( ',', $ids );
624 $post_type = get_post_type( $pid[0] );
625 echo ' <a href="' . esc_url( wp_nonce_url( "edit.php?post_type=$post_type&doaction=undo&action=untrash&ids=$ids", "bulk-posts" ) ) . '">' . __( 'Undo', 'edit-flow' ) . '</a><br />';
626 unset( $_GET['trashed'] );
627 }
628 if ( isset($_GET['untrashed'] ) && (int) $_GET['untrashed'] ) {
629 printf( _n( 'Post restored from the Trash.', '%d posts restored from the Trash.', $_GET['untrashed'] ), number_format_i18n( $_GET['untrashed'] ) );
630 unset( $_GET['undeleted'] );
631 }
632 echo '</p></div>';
633 }
634 ?>
635
636 <div id="ef-calendar-wrap"><!-- Calendar Wrapper -->
637
638 <?php $this->print_top_navigation( $filters, $dates ); ?>
639
640 <?php
641 $table_classes = array();
642 // CSS don't like our classes to start with numbers
643 if ( $this->total_weeks == 1 )
644 $table_classes[] = 'one-week-showing';
645 elseif ( $this->total_weeks == 2 )
646 $table_classes[] = 'two-weeks-showing';
647 elseif ( $this->total_weeks == 3 )
648 $table_classes[] = 'three-weeks-showing';
649
650 $table_classes = apply_filters( 'ef_calendar_table_classes', $table_classes );
651 ?>
652 <table id="ef-calendar-view" class="<?php echo esc_attr( implode( ' ', $table_classes ) ); ?>">
653 <thead>
654 <tr class="calendar-heading">
655 <?php echo $this->get_time_period_header( $dates ); ?>
656 </tr>
657 </thead>
658 <tbody>
659
660 <?php
661 $current_month = date( 'F', strtotime( $filters['start_date'] ) );
662 for( $current_week = 1; $current_week <= $this->total_weeks; $current_week++ ):
663 // We need to set the object variable for our posts_where filter
664 $this->current_week = $current_week;
665 $week_posts = $this->get_calendar_posts_for_week( $post_query_args );
666 $date_format = 'Y-m-d';
667 $week_single_date = $this->get_beginning_of_week( $filters['start_date'], $date_format, $current_week );
668 $week_dates = array();
669 $split_month = false;
670 for ( $i = 0 ; $i < 7; $i++ ) {
671 $week_dates[$i] = $week_single_date;
672 $single_date_month = date( 'F', strtotime( $week_single_date ) );
673 if ( $single_date_month != $current_month ) {
674 $split_month = $single_date_month;
675 $current_month = $single_date_month;
676 }
677 $week_single_date = date( 'Y-m-d', strtotime( "+1 day", strtotime( $week_single_date ) ) );
678 }
679 ?>
680 <?php if ( $split_month ): ?>
681 <tr class="month-marker">
682 <?php foreach( $week_dates as $key => $week_single_date ) {
683 if ( date( 'F', strtotime( $week_single_date ) ) != $split_month && date( 'F', strtotime( "+1 day", strtotime( $week_single_date ) ) ) == $split_month ) {
684 $previous_month = date( 'F', strtotime( $week_single_date ) );
685 echo '<td class="month-marker-previous">' . esc_html( $previous_month ) . '</td>';
686 } else if ( date( 'F', strtotime( $week_single_date ) ) == $split_month && date( 'F', strtotime( "-1 day", strtotime( $week_single_date ) ) ) != $split_month ) {
687 echo '<td class="month-marker-current">' . esc_html( $split_month ) . '</td>';
688 } else {
689 echo '<td class="month-marker-empty"></td>';
690 }
691 } ?>
692 </tr>
693 <?php endif; ?>
694
695 <tr class="week-unit">
696 <?php foreach( $week_dates as $day_num => $week_single_date ): ?>
697 <?php
698 // Somewhat ghetto way of sorting all of the day's posts by post status order
699 if ( !empty( $week_posts[$week_single_date] ) ) {
700 $week_posts_by_status = array();
701 foreach( $post_statuses as $post_status ) {
702 $week_posts_by_status[$post_status->slug] = array();
703 }
704 // These statuses aren't handled by custom statuses or post statuses
705 $week_posts_by_status['private'] = array();
706 $week_posts_by_status['publish'] = array();
707 $week_posts_by_status['future'] = array();
708 foreach( $week_posts[$week_single_date] as $num => $post ) {
709 $week_posts_by_status[$post->post_status][$num] = $post;
710 }
711 unset( $week_posts[$week_single_date] );
712 foreach( $week_posts_by_status as $status ) {
713 foreach( $status as $num => $post ) {
714 $week_posts[$week_single_date][] = $post;
715 }
716 }
717 }
718
719 $td_classes = array(
720 'day-unit',
721 );
722 $day_name = date( 'D', strtotime( $week_single_date ) );
723
724 if ( in_array( $day_name, $dotw ) )
725 $td_classes[] = 'weekend-day';
726
727 if ( $week_single_date == date( 'Y-m-d', current_time( 'timestamp' ) ) )
728 $td_classes[] = 'today';
729
730 // Last day of the week
731 if ( $day_num == 6 )
732 $td_classes[] = 'last-day';
733
734 $td_classes = apply_filters( 'ef_calendar_table_td_classes', $td_classes, $week_single_date );
735 ?>
736 <td class="<?php echo esc_attr( implode( ' ', $td_classes ) ); ?>" id="<?php echo esc_attr( $week_single_date ); ?>">
737 <button class='schedule-new-post-button'>+</button>
738 <?php if ( $week_single_date == date( 'Y-m-d', current_time( 'timestamp' ) ) ): ?>
739 <div class="day-unit-today"><?php _e( 'Today', 'edit-flow' ); ?></div>
740 <?php endif; ?>
741 <div class="day-unit-label"><?php echo esc_html( date( 'j', strtotime( $week_single_date ) ) ); ?></div>
742 <ul class="post-list">
743 <?php
744 $this->hidden = 0;
745 if ( !empty( $week_posts[$week_single_date] ) ) {
746
747 $week_posts[$week_single_date] = apply_filters( 'ef_calendar_posts_for_week', $week_posts[$week_single_date] );
748
749 foreach ( $week_posts[$week_single_date] as $num => $post ){
750 $this->generate_post_li_html( $post, $week_single_date, $num );
751 }
752
753 }
754 ?>
755 </ul>
756 <?php if ( $this->hidden ): ?>
757 <a class="show-more" href="#"><?php printf( __( 'Show %d more', 'edit-flow' ), $this->hidden ); ?></a>
758 <?php endif; ?>
759
760 <?php if( current_user_can('publish_posts') ) :
761 $date_formatted = date( 'D, M jS, Y', strtotime( $week_single_date ) );
762 ?>
763
764 <form method="POST" class="post-insert-dialog">
765 <?php /* translators: %1$s = post type name, %2$s = date */ ?>
766 <h1><?php echo sprintf( __( 'Schedule a %1$s for %2$s', 'edit-flow' ), $this->get_quick_create_post_type_name(), $date_formatted ); ?></h1>
767 <?php /* translators: %s = post type name */ ?>
768 <input type="text" class="post-insert-dialog-post-title" name="post-insert-dialog-post-title" placeholder="<?php echo esc_attr( sprintf( _x( '%s Title', 'post type name', 'edit-flow' ), $this->get_quick_create_post_type_name() ) ); ?>">
769 <input type="hidden" class="post-insert-dialog-post-date" name="post-insert-dialog-post-title" value="<?php echo esc_attr( $week_single_date ); ?>">
770 <div class="post-insert-dialog-controls">
771 <input type="submit" class="button left" value="<?php echo esc_html( sprintf( _x( 'Create %s', 'post type name', 'edit-flow' ), $this->get_quick_create_post_type_name() ) ); ?>">
772 <a class="post-insert-dialog-edit-post-link" href="#"><?php echo esc_html( sprintf( _x( 'Edit %s', 'post type name', 'edit-flow' ), $this->get_quick_create_post_type_name() ) ); ?>&nbsp;&raquo;</a>
773 </div>
774 <div class="spinner">&nbsp;</div>
775 </form>
776 <?php endif; ?>
777
778 </td>
779 <?php endforeach; ?>
780 </tr>
781
782 <?php endfor; ?>
783
784 </tbody>
785 </table><!-- /Week Wrapper -->
786 <?php
787 // Nonce field for AJAX actions
788 wp_nonce_field( 'ef-calendar-modify', 'ef-calendar-modify' ); ?>
789
790 <div class="clear"></div>
791 </div><!-- /Calendar Wrapper -->
792
793 </div>
794
795 <?php
796
797 }
798
799 /**
800 * Generates the HTML for a single post item in the calendar
801 * @param obj $post The WordPress post in question
802 * @param str $post_date The date of the post
803 * @param int $num The index of the post
804 *
805 * @return str HTML for a single post item
806 */
807 function generate_post_li_html( $post, $post_date, $num = 0 ){
808
809 $post_id = $post->ID;
810 $edit_post_link = get_edit_post_link( $post_id );
811
812 $post_classes = array(
813 'day-item',
814 'custom-status-' . $post->post_status,
815 );
816 // Only allow the user to drag the post if they have permissions to
817 // or if it's in an approved post status
818 // This is checked on the ajax request too.
819 if ( $this->current_user_can_modify_post( $post ) && !in_array( $post->post_status, $this->published_statuses ) )
820 $post_classes[] = 'sortable';
821
822 if ( in_array( $post->post_status, $this->published_statuses ) )
823 $post_classes[] = 'is-published';
824
825 // Hide posts over a certain number to prevent clutter, unless user is only viewing 1 or 2 weeks
826 $max_visible_posts = apply_filters( 'ef_calendar_max_visible_posts_per_date', $this->max_visible_posts_per_date);
827
828 if ( $num >= $max_visible_posts && $this->total_weeks > 2 ) {
829 $post_classes[] = 'hidden';
830 $this->hidden++;
831 }
832 $post_classes = apply_filters( 'ef_calendar_table_td_li_classes', $post_classes, $post_date, $post->ID );
833
834 ?>
835 <li class="<?php echo esc_attr( implode( ' ', $post_classes ) ); ?>" id="post-<?php echo esc_attr( $post->ID ); ?>">
836 <div style="clear:right;"></div>
837 <div class="item-static">
838 <div class="item-default-visible">
839 <div class="item-status"><span class="status-text"><?php echo esc_html( $this->get_post_status_friendly_name( get_post_status( $post_id ) ) ); ?></span></div>
840 <div class="inner">
841 <span class="item-headline post-title"><strong><?php echo esc_html( $post->post_title ); ?></strong></span>
842 </div>
843 </div>
844 <div class="item-inner">
845 <?php $this->get_inner_information( $this->get_post_information_fields( $post ), $post ); ?>
846 </div>
847 </div>
848 </li>
849 <?php
850
851 } // generate_post_li_html()
852
853 /**
854 * get_inner_information description
855 * Functionality for generating the inner html elements on the calendar
856 * has been separated out so various ajax functions can reload certain
857 * parts of an inner html element.
858 * @param array $ef_calendar_item_information_fields
859 * @param WP_Post $post
860 * @param array $published_statuses
861 *
862 * @since 0.8
863 */
864 function get_inner_information( $ef_calendar_item_information_fields, $post ) {
865 ?>
866 <table class="item-information">
867 <?php foreach( $this->get_post_information_fields( $post ) as $field => $values ): ?>
868 <tr class="item-field item-information-<?php echo esc_attr( $field ); ?>">
869 <th class="label"><?php echo esc_html( $values['label'] ); ?>:</th>
870 <?php if ( $values['value'] && isset($values['type']) ): ?>
871 <?php if( isset( $values['editable'] ) && $this->current_user_can_modify_post( $post ) ) : ?>
872 <td class="value<?php if( $values['editable'] ) { ?> editable-value<?php } ?>"><?php echo esc_html( $values['value'] ); ?></td>
873 <?php if( $values['editable'] ): ?>
874 <td class="editable-html hidden" data-type="<?php echo $values['type']; ?>" data-metadataterm="<?php echo str_replace( 'editorial-metadata-', '', str_replace( 'tax_', '', $field ) ); ?>"><?php echo $this->get_editable_html( $values['type'], $values['value'] ); ?></td>
875 <?php endif; ?>
876 <?php else: ?>
877 <td class="value"><?php echo esc_html( $values['value'] ); ?></td>
878 <?php endif; ?>
879 <?php elseif( $values['value'] ): ?>
880 <td class="value"><?php echo esc_html( $values['value'] ); ?></td>
881 <?php else: ?>
882 <td class="value"><em class="none"><?php echo _e( 'None', 'edit-flow' ); ?></em></td>
883 <?php endif; ?>
884 </tr>
885 <?php endforeach; ?>
886 <?php do_action( 'ef_calendar_item_additional_html', $post->ID ); ?>
887 </table>
888 <?php
889 $post_type_object = get_post_type_object( $post->post_type );
890 $item_actions = array();
891 if ( $this->current_user_can_modify_post( $post ) ) {
892 // Edit this post
893 $item_actions['edit'] = '<a href="' . get_edit_post_link( $post->ID, true ) . '" title="' . esc_attr( __( 'Edit this item', 'edit-flow' ) ) . '">' . __( 'Edit', 'edit-flow' ) . '</a>';
894 // Trash this post
895 $item_actions['trash'] = '<a href="'. get_delete_post_link( $post->ID) . '" title="' . esc_attr( __( 'Trash this item' ), 'edit-flow' ) . '">' . __( 'Trash', 'edit-flow' ) . '</a>';
896 // Preview/view this post
897 if ( !in_array( $post->post_status, $this->published_statuses ) ) {
898 $item_actions['view'] = '<a href="' . esc_url( apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ) . '" title="' . esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;', 'edit-flow' ), $post->post_title ) ) . '" rel="permalink">' . __( 'Preview', 'edit-flow' ) . '</a>';
899 } elseif ( 'trash' != $post->post_status ) {
900 $item_actions['view'] = '<a href="' . get_permalink( $post->ID ) . '" title="' . esc_attr( sprintf( __( 'View &#8220;%s&#8221;', 'edit-flow' ), $post->post_title ) ) . '" rel="permalink">' . __( 'View', 'edit-flow' ) . '</a>';
901 }
902 //Save metadata
903 $item_actions['save hidden'] = '<a href="#savemetadata" id="save-editorial-metadata" class="post-'. $post->ID .'" title="'. esc_attr( sprintf( __( 'Save &#8220;%s&#8221;', 'edit-flow' ), $post->post_title ) ) . '" >' . __( 'Save', 'edit-flow') . '</a>';
904 }
905 // Allow other plugins to add actions
906 $item_actions = apply_filters( 'ef_calendar_item_actions', $item_actions, $post->ID );
907 if ( count( $item_actions ) ) {
908 echo '<div class="item-actions">';
909 $html = '';
910 foreach ( $item_actions as $class => $item_action ) {
911 $html .= '<span class="' . esc_attr( $class ) . '">' . $item_action . ' | </span> ';
912 }
913 echo rtrim( $html, '| ' );
914 echo '</div>';
915 }
916 ?>
917 <div style="clear:right;"></div>
918 <?php
919
920 } // generate_post_li_html()
921
922 function get_editable_html( $type, $value ) {
923
924 switch( $type ) {
925 case 'text':
926 case 'location':
927 case 'number':
928 return '<input type="text" class="metadata-edit-' . $type . '" value="' . $value . '"/>';
929 break;
930 case 'paragraph':
931 return '<textarea type="text" class="metadata-edit-' . $type . '">' . $value . '</textarea>';
932 break;
933 case 'date':
934 return '<input type="text" value="' . $value . '" class="date-time-pick metadata-edit-' . $type . '"/>';
935 break;
936 case 'checkbox':
937 $output = '<select class="metadata-edit">';
938
939 if( $value == 'No' )
940 $output .= '<option value="0">No</option><option value="1">Yes</option>';
941 else
942 $output .= '<option value="1">Yes</option><option value="0">No</option>';
943
944 $output .= '</select>';
945
946 return $output;
947 break;
948 case 'user':
949 return wp_dropdown_users( array( 'echo' => false ) );
950 break;
951 case 'taxonomy':
952 return '<input type="text" class="metadata-edit-' . $type . '" value="' . $value . '" />';
953 break;
954 case 'taxonomy hierarchical':
955 return wp_dropdown_categories( array( 'echo' => 0, 'hide_empty' => 0 ) );
956 break;
957 }
958 }
959
960 /**
961 * Get the information fields to be presented with each post popup
962 *
963 * @since 0.8
964 *
965 * @param obj $post Post to gather information fields for
966 * @return array $information_fields All of the information fields to be presented
967 */
968 function get_post_information_fields( $post ) {
969
970 $information_fields = array();
971 // Post author
972 $information_fields['author'] = array(
973 'label' => __( 'Author', 'edit-flow' ),
974 'value' => get_the_author_meta( 'display_name', $post->post_author ),
975 'type' => 'author',
976 );
977
978 // If the calendar supports more than one post type, show the post type label
979 if ( count( $this->get_post_types_for_module( $this->module ) ) > 1 ) {
980 $information_fields['post_type'] = array(
981 'label' => __( 'Post Type', 'edit-flow' ),
982 'value' => get_post_type_object( $post->post_type )->labels->singular_name,
983 );
984 }
985 // Publication time for published statuses
986 $published_statuses = array(
987 'publish',
988 'future',
989 'private',
990 );
991 if ( in_array( $post->post_status, $published_statuses ) ) {
992 if ( $post->post_status == 'future' ) {
993 $information_fields['post_date'] = array(
994 'label' => __( 'Scheduled', 'edit-flow' ),
995 'value' => get_the_time( null, $post->ID ),
996 );
997 } else {
998 $information_fields['post_date'] = array(
999 'label' => __( 'Published', 'edit-flow' ),
1000 'value' => get_the_time( null, $post->ID ),
1001 );
1002 }
1003 }
1004 // Taxonomies and their values
1005 $args = array(
1006 'post_type' => $post->post_type,
1007 );
1008 $taxonomies = get_object_taxonomies( $args, 'object' );
1009 foreach( (array)$taxonomies as $taxonomy ) {
1010 // Sometimes taxonomies skip by, so let's make sure it has a label too
1011 if ( !$taxonomy->public || !$taxonomy->label )
1012 continue;
1013 $terms = wp_get_object_terms( $post->ID, $taxonomy->name );
1014 $key = 'tax_' . $taxonomy->name;
1015 if ( count( $terms ) ) {
1016 $value = '';
1017 foreach( (array)$terms as $term ) {
1018 $value .= $term->name . ', ';
1019 }
1020 $value = rtrim( $value, ', ' );
1021 } else {
1022 $value = '';
1023 }
1024 //Used when editing editorial metadata and post meta
1025 if ( is_taxonomy_hierarchical( $taxonomy->name ) )
1026 $type = 'taxonomy hierarchical';
1027 else
1028 $type = 'taxonomy';
1029
1030 $information_fields[$key] = array(
1031 'label' => $taxonomy->label,
1032 'value' => $value,
1033 'type' => $type,
1034 );
1035
1036 if( $post->post_type == 'page' )
1037 $ed_cap = 'edit_page';
1038 else
1039 $ed_cap = 'edit_post';
1040
1041 if( current_user_can( $ed_cap, $post->ID ) )
1042 $information_fields[$key]['editable'] = true;
1043 }
1044
1045 $information_fields = apply_filters( 'ef_calendar_item_information_fields', $information_fields, $post->ID );
1046 foreach( $information_fields as $field => $values ) {
1047 // Allow filters to hide empty fields or to hide any given individual field. Hide empty fields by default.
1048 if ( ( apply_filters( 'ef_calendar_hide_empty_item_information_fields', true, $post->ID ) && empty( $values['value'] ) )
1049 || apply_filters( "ef_calendar_hide_{$field}_item_information_field", false, $post->ID ) )
1050 unset( $information_fields[$field] );
1051 }
1052 return $information_fields;
1053 }
1054
1055 /**
1056 * Generates the filtering and navigation options for the top of the calendar
1057 *
1058 * @param array $filters Any set filters
1059 * @param array $dates All of the days of the week. Used for generating navigation links
1060 */
1061 function print_top_navigation( $filters, $dates ) {
1062 ?>
1063 <ul class="ef-calendar-navigation">
1064 <li id="calendar-filter">
1065 <form method="GET">
1066 <input type="hidden" name="page" value="calendar" />
1067 <input type="hidden" name="start_date" value="<?php echo esc_attr( $filters['start_date'] ); ?>"/>
1068 <!-- Filter by status -->
1069 <?php
1070 foreach( $this->calendar_filters() as $select_id => $select_name ) {
1071 echo $this->calendar_filter_options( $select_id, $select_name, $filters );
1072 }
1073 ?>
1074 <input type="submit" id="post-query-submit" class="button-primary button" value="<?php _e( 'Filter', 'edit-flow' ); ?>"/>
1075 </form>
1076 </li>
1077 <!-- Clear filters functionality (all of the fields, but empty) -->
1078 <li>
1079 <form method="GET">
1080 <input type="hidden" name="page" value="calendar" />
1081 <input type="hidden" name="start_date" value="<?php echo esc_attr( $filters['start_date'] ); ?>"/>
1082 <?php
1083 foreach( $this->calendar_filters() as $select_id => $select_name )
1084 echo '<input type="hidden" name="'.$select_name.'" value="" />';
1085 ?>
1086 <input type="submit" id="post-query-clear" class="button-secondary button" value="<?php _e( 'Reset', 'edit-flow' ); ?>"/>
1087 </form>
1088 </li>
1089
1090 <?php /** Previous and next navigation items (translatable so they can be increased if needed )**/ ?>
1091 <li class="date-change next-week">
1092 <a title="<?php printf( __( 'Forward 1 week', 'edit-flow' ) ); ?>" href="<?php echo esc_url( $this->get_pagination_link( 'next', $filters, 1 ) ); ?>"><?php _e( '&rsaquo;', 'edit-flow' ); ?></a>
1093 <?php if ( $this->total_weeks > 1): ?>
1094 <a title="<?php printf( __( 'Forward %d weeks', 'edit-flow' ), $this->total_weeks ); ?>" href="<?php echo esc_url( $this->get_pagination_link( 'next', $filters ) ); ?>"><?php _e( '&raquo;', 'edit-flow' ); ?></a>
1095 <?php endif; ?>
1096 </li>
1097 <li class="date-change today">
1098 <a title="<?php printf( __( 'Today is %s', 'edit-flow' ), date( get_option( 'date_format' ), current_time( 'timestamp' ) ) ); ?>" href="<?php echo esc_url( $this->get_pagination_link( 'next', $filters, 0 ) ); ?>"><?php _e( 'Today', 'edit-flow' ); ?></a>
1099 </li>
1100 <li class="date-change previous-week">
1101 <?php if ( $this->total_weeks > 1): ?>
1102 <a title="<?php printf( __( 'Back %d weeks', 'edit-flow' ), $this->total_weeks ); ?>" href="<?php echo esc_url( $this->get_pagination_link( 'previous', $filters ) ); ?>"><?php _e( '&laquo;', 'edit-flow' ); ?></a>
1103 <?php endif; ?>
1104 <a title="<?php printf( __( 'Back 1 week', 'edit-flow' ) ); ?>" href="<?php echo esc_url( $this->get_pagination_link( 'previous', $filters, 1 ) ); ?>"><?php _e( '&lsaquo;', 'edit-flow' ); ?></a>
1105 </li>
1106 <li class="ajax-actions">
1107 <img class="waiting" style="display:none;" src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" alt="" />
1108 </li>
1109 </ul>
1110 <?php
1111 }
1112
1113 /**
1114 * Generate the calendar header for a given range of dates
1115 *
1116 * @param array $dates Date range for the header
1117 * @return string $html Generated HTML for the header
1118 */
1119 function get_time_period_header( $dates ) {
1120
1121 $html = '';
1122 foreach( $dates as $date ) {
1123 $html .= '<th class="column-heading" >';
1124 $html .= esc_html( date('l', strtotime( $date ) ) );
1125 $html .= '</th>';
1126 }
1127
1128 return $html;
1129
1130 }
1131
1132 /**
1133 * Query to get all of the calendar posts for a given day
1134 *
1135 * @param array $args Any filter arguments we want to pass
1136 * @param string $request_context Where the query is coming from, to distinguish dashboard and subscriptions
1137 * @return array $posts All of the posts as an array sorted by date
1138 */
1139 function get_calendar_posts_for_week( $args = array(), $context = 'dashboard' ) {
1140 global $wpdb;
1141
1142 $supported_post_types = $this->get_post_types_for_module( $this->module );
1143 $defaults = array(
1144 'post_status' => null,
1145 'cat' => null,
1146 'author' => null,
1147 'post_type' => $supported_post_types,
1148 'posts_per_page' => -1,
1149 );
1150
1151 $args = array_merge( $defaults, $args );
1152
1153 // Unpublished as a status is just an array of everything but 'publish'
1154 if ( $args['post_status'] == 'unpublish' ) {
1155 $args['post_status'] = '';
1156 $post_statuses = $this->get_post_statuses();
1157 foreach ( $post_statuses as $post_status ) {
1158 $args['post_status'] .= $post_status->slug . ', ';
1159 }
1160 $args['post_status'] = rtrim( $args['post_status'], ', ' );
1161 // Optional filter to include scheduled content as unpublished
1162 if ( apply_filters( 'ef_show_scheduled_as_unpublished', false ) )
1163 $args['post_status'] .= ', future';
1164 }
1165 // The WP functions for printing the category and author assign a value of 0 to the default
1166 // options, but passing this to the query is bad (trashed and auto-draft posts appear!), so
1167 // unset those arguments.
1168 if ( $args['cat'] === '0' ) unset( $args['cat'] );
1169 if ( $args['author'] === '0' ) unset( $args['author'] );
1170
1171 if ( empty( $args['post_type'] ) || ! in_array( $args['post_type'], $supported_post_types ) )
1172 $args['post_type'] = $supported_post_types;
1173
1174 // Filter for an end user to implement any of their own query args
1175 $args = apply_filters( 'ef_calendar_posts_query_args', $args, $context );
1176 add_filter( 'posts_where', array( $this, 'posts_where_week_range' ) );
1177 $post_results = new WP_Query( $args );
1178 remove_filter( 'posts_where', array( $this, 'posts_where_week_range' ) );
1179
1180 $posts = array();
1181 while ( $post_results->have_posts() ) {
1182 $post_results->the_post();
1183 global $post;
1184 $key_date = date( 'Y-m-d', strtotime( $post->post_date ) );
1185 $posts[$key_date][] = $post;
1186 }
1187
1188 return $posts;
1189
1190 }
1191
1192 /**
1193 * Filter the WP_Query so we can get a week range of posts
1194 *
1195 * @param string $where The original WHERE SQL query string
1196 * @return string $where Our modified WHERE query string
1197 */
1198 function posts_where_week_range( $where = '' ) {
1199 global $wpdb;
1200
1201 $beginning_date = $this->get_beginning_of_week( $this->start_date, 'Y-m-d', $this->current_week );
1202 $ending_date = $this->get_ending_of_week( $this->start_date, 'Y-m-d', $this->current_week );
1203 // Adjust the ending date to account for the entire day of the last day of the week
1204 $ending_date = date( "Y-m-d", strtotime( "+1 day", strtotime( $ending_date ) ) );
1205 $where = $where . $wpdb->prepare( " AND ($wpdb->posts.post_date >= %s AND $wpdb->posts.post_date < %s)", $beginning_date, $ending_date );
1206
1207 return $where;
1208 }
1209
1210 /**
1211 * Gets the link for the next time period
1212 *
1213 * @param string $direction 'previous' or 'next', direction to go in time
1214 * @param array $filters Any filters that need to be applied
1215 * @param int $weeks_offset Number of weeks we're offsetting the range
1216 * @return string $url The URL for the next page
1217 */
1218 function get_pagination_link( $direction = 'next', $filters = array(), $weeks_offset = null ) {
1219
1220 $supported_post_types = $this->get_post_types_for_module( $this->module );
1221
1222 if ( !isset( $weeks_offset ) )
1223 $weeks_offset = $this->total_weeks;
1224 else if ( $weeks_offset == 0 )
1225 $filters['start_date'] = $this->get_beginning_of_week( date( 'Y-m-d', current_time( 'timestamp' ) ) );
1226
1227 if ( $direction == 'previous' )
1228 $weeks_offset = '-' . $weeks_offset;
1229
1230 $filters['start_date'] = date( 'Y-m-d', strtotime( $weeks_offset . " weeks", strtotime( $filters['start_date'] ) ) );
1231 $url = add_query_arg( $filters, menu_page_url( $this->module->slug, false ) );
1232
1233 if ( count( $supported_post_types ) > 1 )
1234 $url = add_query_arg( 'cpt', $filters['cpt'] , $url );
1235
1236 return $url;
1237
1238 }
1239
1240 /**
1241 * Given a day in string format, returns the day at the beginning of that week, which can be the given date.
1242 * The end of the week is determined by the blog option, 'start_of_week'.
1243 *
1244 * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats
1245 *
1246 * @param string $date String representing a date
1247 * @param string $format Date format in which the end of the week should be returned
1248 * @param int $week Number of weeks we're offsetting the range
1249 * @return string $formatted_start_of_week End of the week
1250 */
1251 function get_beginning_of_week( $date, $format = 'Y-m-d', $week = 1 ) {
1252
1253 $date = strtotime( $date );
1254 $start_of_week = get_option( 'start_of_week' );
1255 $day_of_week = date( 'w', $date );
1256 $date += (( $start_of_week - $day_of_week - 7 ) % 7) * 60 * 60 * 24 * $week;
1257 $additional = 3600 * 24 * 7 * ( $week - 1 );
1258 $formatted_start_of_week = date( $format, $date + $additional );
1259 return $formatted_start_of_week;
1260
1261 }
1262
1263 /**
1264 * Given a day in string format, returns the day at the end of that week, which can be the given date.
1265 * The end of the week is determined by the blog option, 'start_of_week'.
1266 *
1267 * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats
1268 *
1269 * @param string $date String representing a date
1270 * @param string $format Date format in which the end of the week should be returned
1271 * @param int $week Number of weeks we're offsetting the range
1272 * @return string $formatted_end_of_week End of the week
1273 */
1274 function get_ending_of_week( $date, $format = 'Y-m-d', $week = 1 ) {
1275
1276 $date = strtotime( $date );
1277 $end_of_week = get_option( 'start_of_week' ) - 1;
1278 $day_of_week = date( 'w', $date );
1279 $date += (( $end_of_week - $day_of_week + 7 ) % 7) * 60 * 60 * 24;
1280 $additional = 3600 * 24 * 7 * ( $week - 1 );
1281 $formatted_end_of_week = date( $format, $date + $additional );
1282 return $formatted_end_of_week;
1283
1284 }
1285
1286 /**
1287 * Human-readable time range for the calendar
1288 * Shows something like "for October 30th through November 26th" for a four-week period
1289 *
1290 * @since 0.7
1291 */
1292 function calendar_time_range() {
1293
1294 $first_datetime = strtotime( $this->start_date );
1295 if ( date( 'Y', current_time( 'timestamp' ) ) != date( 'Y', $first_datetime ) )
1296 $first_date = date( 'F jS, Y', $first_datetime );
1297 else
1298 $first_date = date( 'F jS', $first_datetime );
1299 $total_days = ( $this->total_weeks * 7 ) - 1;
1300 $last_datetime = strtotime( "+" . $total_days . " days", date( 'U', strtotime( $this->start_date ) ) );
1301 if ( date( 'Y', current_time( 'timestamp' ) ) != date( 'Y', $last_datetime ) )
1302 $last_date = date( 'F jS, Y', $last_datetime );
1303 else
1304 $last_date = date( 'F jS', $last_datetime );
1305 echo sprintf( __( 'for %1$s through %2$s'), $first_date, $last_date );
1306 }
1307
1308 /**
1309 * Check whether the current user should have the ability to modify the post
1310 *
1311 * @since 0.7
1312 *
1313 * @param object $post The post object we're checking
1314 * @return bool $can Whether or not the current user can modify the post
1315 */
1316 function current_user_can_modify_post( $post ) {
1317
1318 if ( !$post )
1319 return false;
1320
1321 $post_type_object = get_post_type_object( $post->post_type );
1322
1323 // Editors and admins are fine
1324 if ( current_user_can( $post_type_object->cap->edit_others_posts, $post->ID ) )
1325 return true;
1326 // Authors and contributors can move their own stuff if it's not published
1327 if ( current_user_can( $post_type_object->cap->edit_post, $post->ID ) && wp_get_current_user()->ID == $post->post_author && !in_array( $post->post_status, $this->published_statuses ) )
1328 return true;
1329 // Those who can publish posts can move any of their own stuff
1330 if ( current_user_can( $post_type_object->cap->publish_posts, $post->ID ) && wp_get_current_user()->ID == $post->post_author )
1331 return true;
1332
1333 return false;
1334 }
1335
1336 /**
1337 * Register settings for notifications so we can partially use the Settings API
1338 * We use the Settings API for form generation, but not saving because we have our
1339 * own way of handling the data.
1340 *
1341 * @since 0.7
1342 */
1343 function register_settings() {
1344
1345 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name );
1346 add_settings_field( 'number_of_weeks', __( 'Number of weeks to show', 'edit-flow' ), array( $this, 'settings_number_weeks_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
1347 add_settings_field( 'post_types', __( 'Post types to show', 'edit-flow' ), array( $this, 'settings_post_types_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
1348 add_settings_field( 'quick_create_post_type', __( 'Post type to create directly from calendar', 'edit-flow' ), array( $this, 'settings_quick_create_post_type_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
1349 add_settings_field( 'ics_subscription', __( 'Subscription in iCal or Google Calendar', 'edit-flow' ), array( $this, 'settings_ics_subscription_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
1350
1351 }
1352
1353 /**
1354 * Choose the post types that should be displayed on the calendar
1355 *
1356 * @since 0.7
1357 */
1358 function settings_post_types_option() {
1359 global $edit_flow;
1360 $edit_flow->settings->helper_option_custom_post_type( $this->module );
1361 }
1362
1363 /**
1364 * Choose the post type that should be created on the calendar
1365 *
1366 * @since 0.8
1367 */
1368 function settings_quick_create_post_type_option() {
1369
1370 $allowed_post_types = $this->get_all_post_types();
1371
1372 echo "<select name='" . $this->module->options_group_name . "[quick_create_post_type]'>";
1373 foreach( $allowed_post_types as $post_type => $title )
1374 echo "<option value='" . esc_attr( $post_type ) . "' " . selected( $post_type, $this->module->options->quick_create_post_type, false ) . ">".esc_html( $title )."</option>";
1375 echo "</select>";
1376
1377 }
1378
1379 /**
1380 * Give a bit of helper text to indicate the user can change
1381 * number of weeks in the screen options
1382 *
1383 * @since 0.7
1384 */
1385 function settings_number_weeks_option() {
1386 echo '<span class="description">' . __( 'The number of weeks shown on the calendar can be changed on a user-by-user basis using the calendar\'s screen options.', 'edit-flow' ) . '</span>';
1387 }
1388
1389 /**
1390 * Enable calendar subscriptions via .ics in iCal or Google Calendar
1391 *
1392 * @since 0.8
1393 */
1394 function settings_ics_subscription_option() {
1395 $options = array(
1396 'off' => __( 'Disabled', 'edit-flow' ),
1397 'on' => __( 'Enabled', 'edit-flow' ),
1398 );
1399 echo '<select id="ics_subscription" name="' . $this->module->options_group_name . '[ics_subscription]">';
1400 foreach ( $options as $value => $label ) {
1401 echo '<option value="' . esc_attr( $value ) . '"';
1402 echo selected( $this->module->options->ics_subscription, $value );
1403 echo '>' . esc_html( $label ) . '</option>';
1404 }
1405 echo '</select>';
1406
1407
1408 $regenerate_url = add_query_arg( 'action', 'ef_calendar_regenerate_calendar_feed_secret', admin_url( 'index.php' ) );
1409 $regenerate_url = wp_nonce_url( $regenerate_url, 'ef-regenerate-ics-key' );
1410 echo '&nbsp;&nbsp;&nbsp;<a href="' . esc_url( $regenerate_url ) . '">' . __( 'Regenerate calendar feed secret', 'edit-flow' ) . '</a>';
1411
1412 // If our secret key doesn't exist, create a new one
1413 if ( empty( $this->module->options->ics_secret_key ) )
1414 EditFlow()->update_module_option( $this->module->name, 'ics_secret_key', wp_generate_password() );
1415 }
1416
1417 /**
1418 * Validate the data submitted by the user in calendar settings
1419 *
1420 * @since 0.7
1421 */
1422 function settings_validate( $new_options ) {
1423
1424 $options = (array)$this->module->options;
1425
1426 $options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support );
1427
1428 if ( in_array( $new_options['quick_create_post_type'], array_keys( $this->get_all_post_types() ) ) )
1429 $options['quick_create_post_type'] = $new_options['quick_create_post_type'];
1430
1431 if ( 'on' != $new_options['ics_subscription'] )
1432 $options['ics_subscription'] = 'off';
1433 else
1434 $options['ics_subscription'] = 'on';
1435
1436 return $options;
1437 }
1438
1439 /**
1440 * Settings page for calendar
1441 */
1442 function print_configure_view() {
1443 global $edit_flow;
1444 ?>
1445 <form class="basic-settings" action="<?php echo esc_url( menu_page_url( $this->module->settings_slug, false ) ); ?>" method="post">
1446 <?php settings_fields( $this->module->options_group_name ); ?>
1447 <?php do_settings_sections( $this->module->options_group_name ); ?>
1448 <?php
1449 echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />';
1450 ?>
1451 <p class="submit"><?php submit_button( null, 'primary', 'submit', false ); ?><a class="cancel-settings-link" href="<?php echo esc_url( EDIT_FLOW_SETTINGS_PAGE ); ?>"><?php _e( 'Back to Edit Flow', 'edit-flow' ); ?></a></p>
1452 </form>
1453 <?php
1454 }
1455
1456 /**
1457 * Ajax callback to insert a post placeholder for a particular date
1458 *
1459 * @since 0.8
1460 */
1461 function handle_ajax_insert_post() {
1462
1463 // Nonce check!
1464 if ( !wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) )
1465 $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] );
1466
1467 // Check that the user has the right capabilities to add posts to the calendar (defaults to 'edit_posts')
1468 if ( !current_user_can( $this->create_post_cap ) )
1469 $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] );
1470
1471 if ( empty( $_POST['ef_insert_date'] ) )
1472 $this->print_ajax_response( 'error', __( 'No date supplied.', 'edit-flow' ) );
1473
1474 // Post type has to be visible on the calendar to create a placeholder
1475 if ( ! in_array( $this->module->options->quick_create_post_type, $this->get_post_types_for_module( $this->module ) ) )
1476 $this->print_ajax_response( 'error', __( 'Please change Quick Create to use a post type viewable on the calendar.', 'edit-flow' ) );
1477
1478 // Sanitize post values
1479 $post_title = sanitize_text_field( $_POST['ef_insert_title'] );
1480
1481 if( ! $post_title )
1482 $post_title = __( 'Untitled', 'edit-flow' );
1483
1484 $post_date = sanitize_text_field( $_POST['ef_insert_date'] );
1485
1486 $post_status = $this->get_default_post_status();
1487
1488 // Set new post parameters
1489 $post_placeholder = array(
1490 'post_title' => $post_title,
1491 'post_status' => $post_status,
1492 'post_date' => date( 'Y-m-d H:i:s', strtotime( $post_date ) ),
1493 'post_type' => $this->module->options->quick_create_post_type,
1494 );
1495
1496 // By default, adding a post to the calendar won't set the timestamp.
1497 // If the user desires that to be the behavior, they can set the result of this filter to 'true'
1498 // With how WordPress works internally, setting 'post_date_gmt' will set the timestamp
1499 if ( apply_filters( 'ef_calendar_allow_ajax_to_set_timestamp', false ) )
1500 $post_placeholder['post_date_gmt'] = date( 'Y-m-d H:i:s', strtotime( $post_date ) );
1501
1502 // Create the post
1503 $post_id = wp_insert_post( $post_placeholder );
1504
1505 if( $post_id ) { // success!
1506
1507 $post = get_post( $post_id );
1508
1509 // Generate the HTML for the post item so it can be injected
1510 ob_start();
1511 $this->generate_post_li_html( $post, $post_date );
1512 $post_li_html = ob_get_contents();
1513 ob_end_clean();
1514
1515 // announce success and send back the html to inject
1516 $this->print_ajax_response( 'success', $post_li_html );
1517
1518 } else {
1519 $this->print_ajax_response( 'error', __( 'Post could not be created', 'edit-flow' ) );
1520 }
1521 }
1522
1523 /**
1524 * Returns the singular label for the posts that are
1525 * quick-created on the calendar
1526 *
1527 * @return str Singular label for a post-type
1528 */
1529 function get_quick_create_post_type_name(){
1530
1531 $post_type_slug = $this->module->options->quick_create_post_type;
1532 $post_type_obj = get_post_type_object( $post_type_slug );
1533
1534 return $post_type_obj->labels->singular_name ? $post_type_obj->labels->singular_name : $post_type_slug;
1535 }
1536
1537 /**
1538 * ajax_ef_calendar_update_metadata
1539 * Update the metadata from the calendar.
1540 * @return string representing the overlay
1541 *
1542 * @since 0.8
1543 */
1544 function handle_ajax_update_metadata() {
1545 global $wpdb;
1546
1547 if ( ! wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) )
1548 $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] );
1549
1550 // Check that we got a proper post
1551 $post_id = ( int )$_POST['post_id'];
1552 $post = get_post( $post_id );
1553
1554 if ( ! $post )
1555 $this->print_ajax_response( 'error', $this->module->messages['missing-post'] );
1556
1557
1558 if( $post->post_type == 'page' )
1559 $edit_check = 'edit_page';
1560 else
1561 $edit_check = 'edit_post';
1562
1563 if ( !current_user_can( $edit_check, $post->ID ) )
1564 $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] );
1565
1566 // Check that the user can modify the post
1567 if ( ! $this->current_user_can_modify_post( $post ) )
1568 $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] );
1569
1570 $default_types = array(
1571 'author',
1572 'taxonomy',
1573 );
1574
1575 $metadata_types = array();
1576
1577 if ( !$this->module_enabled( 'editorial_metadata' ) )
1578 $this->print_ajax_response( 'error', $this->module->messages['update-error'] );
1579
1580 $metadata_types = array_keys( EditFlow()->editorial_metadata->get_supported_metadata_types() );
1581
1582 // Update an editorial metadata field
1583 if ( isset( $_POST['metadata_type'] ) && in_array( $_POST['metadata_type'], $metadata_types ) ) {
1584 $post_meta_key = sanitize_text_field( '_ef_editorial_meta_' . $_POST['metadata_type'] . '_' . $_POST['metadata_term'] );
1585
1586 //Javascript date parsing is terrible, so use strtotime in php
1587 if ( $_POST['metadata_type'] == 'date' )
1588 $metadata_value = strtotime( sanitize_text_field( $_POST['metadata_value'] ) );
1589 else
1590 $metadata_value = sanitize_text_field( $_POST['metadata_value'] );
1591
1592 update_post_meta( $post->ID, $post_meta_key, $metadata_value );
1593 $response = 'success';
1594 } else {
1595 switch( $_POST['metadata_type'] ) {
1596 case 'taxonomy':
1597 case 'taxonomy hierarchical':
1598 $response = wp_set_post_terms( $post->ID, $_POST['metadata_value'], $_POST['metadata_term'] );
1599 break;
1600 default:
1601 $response = new WP_Error( 'invalid-type', __( 'Invalid metadata type', 'edit-flow' ) );
1602 break;
1603 }
1604 }
1605
1606 //Assuming we've got to this point, just regurgitate the value
1607 if ( ! is_wp_error( $response ) )
1608 $this->print_ajax_response( 'success', $_POST['metadata_value'] );
1609 else
1610 $this->print_ajax_response( 'error', __( 'Metadata could not be updated.', 'edit-flow' ) );
1611 }
1612
1613 function calendar_filters() {
1614 $select_filter_names = array();
1615
1616 $select_filter_names['post_status'] = 'post_status';
1617 $select_filter_names['cat'] = 'cat';
1618 $select_filter_names['author'] = 'author';
1619 $select_filter_names['type'] = 'cpt';
1620
1621 return apply_filters( 'ef_calendar_filter_names', $select_filter_names );
1622 }
1623
1624 /**
1625 * Sanitize a $_GET or similar filter being used on the calendar
1626 *
1627 * @since 0.8
1628 *
1629 * @param string $key Filter being sanitized
1630 * @param string $dirty_value Value to be sanitized
1631 * @return string $sanitized_value Safe to use value
1632 */
1633 function sanitize_filter( $key, $dirty_value ) {
1634
1635 switch( $key ) {
1636 case 'post_status':
1637 // Whitelist-based validation for this parameter
1638 $valid_statuses = wp_list_pluck( $this->get_post_statuses(), 'slug' );
1639 $valid_statuses[] = 'future';
1640 $valid_statuses[] = 'unpublish';
1641 $valid_statuses[] = 'publish';
1642 if ( in_array( $dirty_value, $valid_statuses ) )
1643 return $dirty_value;
1644 else
1645 return '';
1646 break;
1647 case 'cpt':
1648 $cpt = sanitize_key( $dirty_value );
1649 $supported_post_types = $this->get_post_types_for_module( $this->module );
1650 if ( $cpt && in_array( $cpt, $supported_post_types ) )
1651 return $cpt;
1652 else
1653 return '';
1654 break;
1655 case 'start_date':
1656 return date( 'Y-m-d', strtotime( $dirty_value ) );
1657 break;
1658 case 'cat':
1659 case 'author':
1660 return intval( $dirty_value );
1661 break;
1662 default:
1663 return false;
1664 break;
1665 }
1666 }
1667
1668 function calendar_filter_options( $select_id, $select_name, $filters ) {
1669 switch( $select_id ){
1670 case 'post_status':
1671 $post_statuses = $this->get_post_statuses();
1672 ?>
1673 <select id="<?php echo $select_id; ?>" name="<?php echo $select_name; ?>" >
1674 <option value=""><?php _e( 'View all statuses', 'edit-flow' ); ?></option>
1675 <?php
1676 foreach ( $post_statuses as $post_status ) {
1677 echo "<option value='" . esc_attr( $post_status->slug ) . "' " . selected( $post_status->slug, $filters['post_status'] ) . ">" . esc_html( $post_status->name ) . "</option>";
1678 }
1679 ?>
1680 <option value="future" <?php selected( 'future', $filters['post_status'] ) ?> > <?php echo __( 'Scheduled', 'edit-flow' ) ?> </option>
1681 <option value="unpublish" <?php selected( 'unpublish', $filters['post_status'] ) ?> > <?php echo __( 'Unpublished', 'edit-flow' ) ?> </option>
1682 <option value="publish" <?php selected( 'publish', $filters['post_status'] ) ?> > <?php echo __( 'Published', 'edit-flow' ) ?> </option>
1683 </select>
1684 <?php
1685 break;
1686 case 'cat':
1687 // Filter by categories, borrowed from wp-admin/edit.php
1688 if ( taxonomy_exists( 'category' ) ) {
1689 $category_dropdown_args = array(
1690 'show_option_all' => __( 'View all categories', 'edit-flow' ),
1691 'hide_empty' => 0,
1692 'hierarchical' => 1,
1693 'show_count' => 0,
1694 'orderby' => 'name',
1695 'selected' => $filters['cat']
1696 );
1697 wp_dropdown_categories( $category_dropdown_args );
1698 }
1699 break;
1700 case 'author':
1701 $users_dropdown_args = array(
1702 'show_option_all' => __( 'View all users', 'edit-flow' ),
1703 'name' => 'author',
1704 'selected' => $filters['author'],
1705 'who' => 'authors',
1706 );
1707 $users_dropdown_args = apply_filters( 'ef_calendar_users_dropdown_args', $users_dropdown_args );
1708 wp_dropdown_users( $users_dropdown_args );
1709 break;
1710 case 'type':
1711 $supported_post_types = $this->get_post_types_for_module( $this->module );
1712 if ( count( $supported_post_types ) > 1 ) {
1713 ?>
1714 <select id="type" name="cpt">
1715 <option value=""><?php _e( 'View all types', 'edit-flow' ); ?></option>
1716 <?php
1717 foreach ( $supported_post_types as $key => $post_type_name ) {
1718 $all_post_types = get_post_types( null, 'objects' );
1719 echo '<option value="' . esc_attr( $post_type_name ) . '"' . selected( $post_type_name, $filters['cpt'] ) . '>' . esc_html( $all_post_types[$post_type_name]->labels->name ) . '</option>';
1720 }
1721 ?>
1722 </select>
1723 <?php
1724 }
1725 break;
1726 default:
1727 do_action( 'ef_calendar_filter_display', $select_id, $select_name, $filters );
1728 break;
1729 }
1730 }
1731
1732 } // EF_Calendar
1733
1734 } // class_exists('EF_Calendar')
1735