PluginProbe
Edit Flow / 0.9.2
Edit Flow v0.9.2
0.11.1 0.11.0 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.8 0.8.1 0.8.2 0.9 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 trunk 0.1.5 0.10.0 0.10.1 0.10.2 All 44 releases
← All changes | modules/notifications/notifications.php +468 -153 0.7.20.9.2 View file →
@@ -12,10 +12,8 @@
12 12 class EF_Notifications extends EF_Module {
13 13
14 14 // Taxonomy name used to store users following posts
15 15 var $following_users_taxonomy = 'following_users';
16 - // Taxonomy name used to store users that have opted out of notifications
17 - var $unfollowing_users_taxonomy = 'unfollowing_users';
18 16 // Taxonomy name used to store user groups following posts
19 17 var $following_usergroups_taxonomy = EF_User_Groups::taxonomy_key;
20 18
21 19 var $module;
@@ -25,9 +23,8 @@
25 23 /**
26 24 * Register the module with Edit Flow but don't do anything else
27 25 */
28 26 function __construct () {
29 - global $edit_flow;
30 27
31 28 // Register the module with Edit Flow
32 29 $this->module_url = $this->get_module_url( __FILE__ );
33 30 $args = array(
@@ -54,9 +51,9 @@
54 51 'content' => __('<p>Notifications ensure you keep up to date with progress your most important content. Users can be subscribed to notifications on a post one by one or by selecting user groups.</p><p>When enabled, email notifications can be sent when a post changes status or an editorial comment is left by a writer or an editor.</p>', 'edit-flow'),
55 52 ),
56 53 'settings_help_sidebar' => __( '<p><strong>For more information:</strong></p><p><a href="http://editflow.org/features/notifications/">Notifications 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' ),
57 54 );
58 - $this->module = $edit_flow->register_module( 'notifications', $args );
55 + $this->module = EditFlow()->register_module( 'notifications', $args );
59 56
60 57 }
61 58
62 59 /**
@@ -71,8 +68,11 @@
71 68 $this->edit_post_subscriptions_cap = apply_filters( 'ef_edit_post_subscriptions_cap', $this->edit_post_subscriptions_cap );
72 69
73 70 // Set up metabox and related actions
74 71 add_action( 'add_meta_boxes', array( $this, 'add_post_meta_box' ) );
72 +
73 + // Add "access badge" to the subscribers list.
74 + add_action( 'ef_user_subscribe_actions', array( $this, 'display_subscriber_warning_badges' ), 10, 2 );
75 75
76 76 // Saving post actions
77 77 // self::save_post_subscriptions() is hooked into transition_post_status so we can ensure usergroup data
78 78 // is properly saved before sending notifs
@@ -86,8 +86,24 @@
86 86
87 87 // Javascript and CSS if we need it
88 88 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
89 89 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
90 +
91 + // Add a "Follow" link to posts
92 + if ( apply_filters( 'ef_notifications_show_follow_link', true ) ) {
93 + // A little extra JS for the follow button
94 + add_action( 'admin_head', array( $this, 'action_admin_head_follow_js' ) );
95 + // Manage Posts
96 + add_filter( 'post_row_actions', array( $this, 'filter_post_row_actions' ), 10, 2 );
97 + add_filter( 'page_row_actions', array( $this, 'filter_post_row_actions' ), 10, 2 );
98 + // Calendar and Story Budget
99 + add_filter( 'ef_calendar_item_actions', array( $this, 'filter_post_row_actions' ), 10, 2 );
100 + add_filter( 'ef_story_budget_item_actions', array( $this, 'filter_post_row_actions' ), 10, 2 );
101 + }
102 +
103 + //Ajax for saving notifiction updates
104 + add_action( 'wp_ajax_save_notifications', array( $this, 'ajax_save_post_subscriptions' ) );
105 + add_action( 'wp_ajax_ef_notifications_user_post_subscription', array( $this, 'handle_user_post_subscription' ) );
90 106
91 107 }
92 108
93 109 /**
@@ -153,19 +169,17 @@
153 169 // Load the currently supported post types so we only register against those
154 170 $supported_post_types = $this->get_post_types_for_module( $this->module );
155 171
156 172 $args = array(
157 - 'hierarchical' => false,
158 - 'update_count_callback' =>
159 - '_update_post_term_count',
160 - 'label' => false,
161 - 'query_var' => false,
162 - 'rewrite' => false,
163 - 'show_ui' => false
173 + 'hierarchical' => false,
174 + 'update_count_callback' => '_update_post_term_count',
175 + 'label' => false,
176 + 'query_var' => false,
177 + 'rewrite' => false,
178 + 'public' => false,
179 + 'show_ui' => false
164 180 );
165 - foreach( array( $this->following_users_taxonomy, $this->unfollowing_users_taxonomy ) as $taxonomy ) {
166 - register_taxonomy( $taxonomy, $supported_post_types, $args );
167 - }
181 + register_taxonomy( $this->following_users_taxonomy, $supported_post_types, $args );
168 182 }
169 183
170 184 /**
171 185 * Enqueue necessary admin scripts
@@ -179,8 +193,16 @@
179 193 if ( $this->is_whitelisted_functional_view() ) {
180 194 wp_enqueue_script( 'jquery-listfilterizer' );
181 195 wp_enqueue_script( 'jquery-quicksearch' );
182 196 wp_enqueue_script( 'edit-flow-notifications-js', $this->module_url . 'lib/notifications.js', array( 'jquery', 'jquery-listfilterizer', 'jquery-quicksearch' ), EDIT_FLOW_VERSION, true );
197 + wp_localize_script(
198 + 'edit-flow-notifications-js',
199 + 'ef_notifications_localization',
200 + array(
201 + 'no_access' => esc_html__( 'No Access', 'edit-flow' ),
202 + 'no_email' => esc_html__( 'No Email', 'edit-flow' )
203 + )
204 + );
183 205 }
184 206 }
185 207
186 208 /**
@@ -196,8 +218,101 @@
196 218 wp_enqueue_style( 'jquery-listfilterizer' );
197 219 wp_enqueue_style( 'edit-flow-notifications-css', $this->module->module_url . 'lib/notifications.css', false, EDIT_FLOW_VERSION );
198 220 }
199 221 }
222 +
223 + /**
224 + * JS required for the Follow link to work
225 + *
226 + * @since 0.8
227 + */
228 + public function action_admin_head_follow_js() {
229 + ?>
230 +<script type='text/Javascript'>
231 +jQuery(document).ready(function($) {
232 + /**
233 + * Action to Follow / Unfollow posts on the manage posts screen
234 + */
235 + $('.wp-list-table, #ef-calendar-view, #ef-story-budget-wrap').on( 'click', '.ef_follow_link a', function(e){
236 +
237 + e.preventDefault();
238 +
239 + var link = $(this);
240 +
241 + $.ajax({
242 + type : 'GET',
243 + url : link.attr( 'href' ),
244 + success : function( data ) {
245 + if ( 'success' == data.status ) {
246 + link.attr( 'href', data.message.link );
247 + link.attr( 'title', data.message.title );
248 + link.text( data.message.text );
249 + }
250 + // @todo expose the error somehow
251 + }
252 + });
253 + return false;
254 + });
255 +});
256 +</script><?php
257 + }
258 +
259 + /**
260 + * Add a "Follow" link to supported post types Manage Posts view
261 + *
262 + * @since 0.8
263 + *
264 + * @param array $actions Any existing item actions
265 + * @param int|object $post Post id or object
266 + * @return array $actions The follow link has been appended
267 + */
268 + public function filter_post_row_actions( $actions, $post ) {
269 +
270 + $post = get_post( $post );
271 +
272 + if ( ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) )
273 + return $actions;
274 +
275 + if ( ! current_user_can( $this->edit_post_subscriptions_cap ) || ! current_user_can( 'edit_post', $post->ID ) )
276 + return $actions;
277 +
278 + $parts = $this->get_follow_action_parts( $post );
279 + $actions['ef_follow_link'] = '<a title="' . esc_attr( $parts['title'] ) . '" href="' . esc_url( $parts['link'] ) . '">' . $parts['text'] . '</a>';
280 +
281 + return $actions;
282 + }
283 +
284 + /**
285 + * Get an action parts for a user to follow or unfollow a post
286 + *
287 + * @since 0.8
288 + */
289 + private function get_follow_action_parts( $post ) {
290 +
291 + $args = array(
292 + 'action' => 'ef_notifications_user_post_subscription',
293 + 'post_id' => $post->ID,
294 + );
295 + $following_users = $this->get_following_users( $post->ID );
296 + if ( in_array( wp_get_current_user()->user_login, $following_users ) ) {
297 + $args['method'] = 'unfollow';
298 + $title_text = __( 'Click to unfollow updates to this post', 'edit-flow' );
299 + $follow_text = __( 'Following', 'edit-flow' );
300 + } else {
301 + $args['method'] = 'follow';
302 + $title_text = __( 'Follow updates to this post', 'edit-flow' );
303 + $follow_text = __( 'Follow', 'edit-flow' );
304 + }
305 +
306 + // wp_nonce_url() has encoding issues: http://core.trac.wordpress.org/ticket/20771
307 + $args['_wpnonce'] = wp_create_nonce( 'ef_notifications_user_post_subscription' );
308 +
309 + return array(
310 + 'title' => $title_text,
311 + 'text' => $follow_text,
312 + 'link' => add_query_arg( $args, admin_url( 'admin-ajax.php' ) ),
313 + );
314 + }
200 315
201 316 /**
202 317 * Add the subscriptions meta box to relevant post types
203 318 */
@@ -218,9 +333,8 @@
218 333 * @todo add_cap to set subscribers for posts; default to Admin and editors
219 334 */
220 335 function notifications_meta_box() {
221 336 global $post, $post_ID, $edit_flow;
222 -
223 337 ?>
224 338 <div id="ef-post_following_box">
225 339 <a name="subscriptions"></a>
226 340
@@ -244,14 +358,140 @@
244 358 </div>
245 359 <?php endif; ?>
246 360 <div class="clear"></div>
247 361 <input type="hidden" name="ef-save_followers" value="1" /> <?php // Extra protection against autosaves ?>
362 + <?php wp_nonce_field('save_user_usergroups', 'ef_notifications_nonce', false); ?>
248 363 </div>
249 364
250 365 <?php
251 366 }
367 +
368 + /**
369 + * Show warning badges next to a subscriber's name if they won't receive notifications
370 + *
371 + * Applies on initial loading of list via. PHP. JS will set these spans based on AJAX response when box is ticked/unticked.
372 + *
373 + * @param int $user_id
374 + * @param bool $checked True if the user is subscribed already, false otherwise.
375 + * @return void
376 + */
377 + function display_subscriber_warning_badges( $user_id, $checked ) {
378 + global $post;
379 +
380 + if (!isset( $post ) OR !$checked ) {
381 + return;
382 + }
383 +
384 + // Add No Access span if they won't be notified
385 + if (! $this->user_can_be_notified( get_user_by( 'id', $user_id ), $post->ID )) {
386 + // span.post_following_list-no_access is also added in notifications.js after AJAX that ticks/unticks a user
387 + echo '<span class="post_following_list-no_access">' . esc_html__( 'No Access', 'edit-flow' ) . '</span>';
388 + }
389 +
390 + // Add No Email span if they have no email
391 + $user_object = get_user_by( 'id', $user_id );
392 + if ( !is_a( $user_object, 'WP_User') OR empty( $user_object->user_email ) ) {
393 + // span.post_following_list-no_email is also added in notifications.js after AJAX that ticks/unticks a user
394 + echo '<span class="post_following_list-no_email">' . esc_html__( 'No Email', 'edit-flow' ) . '</span>';
395 + }
396 + }
252 397
253 398 /**
399 + * Called when a notification editorial metadata checkbox is checked. Handles saving of a user/usergroup to a post.
400 + */
401 + function ajax_save_post_subscriptions() {
402 + global $edit_flow;
403 +
404 + // Verify nonce.
405 + if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( $_POST['_nonce'], 'save_user_usergroups' ) ) {
406 + die( __( 'Nonce check failed. Please ensure you can add users or user groups to a post.', 'edit-flow' ) );
407 + }
408 +
409 + $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0;
410 + $post = get_post( $post_id );
411 +
412 + $valid_post = ! is_null( $post ) && ! wp_is_post_revision( $post_id ) && ! wp_is_post_autosave( $post_id );
413 + if ( ! isset( $_POST['ef_notifications_name'] ) || ! $valid_post || ! current_user_can( $this->edit_post_subscriptions_cap ) ) {
414 + die();
415 + }
416 +
417 + $user_group_ids = array();
418 + if ( isset( $_POST['user_group_ids'] ) && is_array( $_POST['user_group_ids'] ) ) {
419 + $user_group_ids = array_map( 'intval', $_POST['user_group_ids'] );
420 + }
421 +
422 + if ( 'ef-selected-users[]' === $_POST['ef_notifications_name'] ) {
423 + // Prevent auto-subscribing users that have opted out of notifications.
424 + add_filter( 'ef_notification_auto_subscribe_current_user', '__return_false', PHP_INT_MAX );
425 + $this->save_post_following_users( $post, $user_group_ids );
426 +
427 + if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_POST['post_id'] ) ) {
428 +
429 + // Determine if any of the selected users won't have notification access
430 + $subscribers_with_no_access = array_filter( $user_group_ids, function( $user_id ) {
431 + return ! $this->user_can_be_notified( get_user_by( 'id', $user_id ), $_POST['post_id'] );
432 + } );
433 +
434 + // Determine if any of the selected users are missing their emails
435 + $subscribers_with_no_email = array();
436 + foreach ( $user_group_ids AS $user_id ) {
437 + $user_object = get_user_by( 'id', $user_id );
438 + if ( !is_a( $user_object, 'WP_User') OR empty( $user_object->user_email ) ) {
439 + $subscribers_with_no_email[] = $user_id;
440 + }
441 + }
442 +
443 + // Assemble the json reply with various lists of problematic users
444 + $json_success = array(
445 + 'subscribers_with_no_access' => array_values( $subscribers_with_no_access ),
446 + 'subscribers_with_no_email' => array_values( $subscribers_with_no_email ),
447 + );
448 +
449 + wp_send_json_success( $json_success );
450 + }
451 + // Remove auto-subscribe prevention behavior from earlier.
452 + remove_filter( 'ef_notification_auto_subscribe_current_user', '__return_false', PHP_INT_MAX );
453 + }
454 +
455 + $groups_enabled = $this->module_enabled( 'user_groups' ) && in_array( get_post_type( $post_id ), $this->get_post_types_for_module( $edit_flow->user_groups->module ) );
456 + if ( 'following_usergroups[]' === $_POST['ef_notifications_name'] && $groups_enabled ) {
457 + $this->save_post_following_usergroups( $post, $user_group_ids );
458 + }
459 +
460 + die();
461 + }
462 +
463 + /**
464 + * Handle a request to update a user's post subscription
465 + *
466 + * @since 0.8
467 + */
468 + public function handle_user_post_subscription() {
469 +
470 + if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'ef_notifications_user_post_subscription' ) )
471 + $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] );
472 +
473 + if ( ! current_user_can( $this->edit_post_subscriptions_cap ) )
474 + $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] );
475 +
476 + $post = get_post( ( $post_id = $_GET['post_id'] ) );
477 +
478 + if ( ! $post )
479 + $this->print_ajax_response( 'error', $this->module->messages['missing-post'] );
480 +
481 + if ( 'follow' == $_GET['method'] )
482 + $retval = $this->follow_post_user( $post, get_current_user_id() );
483 + else
484 + $retval = $this->unfollow_post_user( $post, get_current_user_id() );
485 +
486 + if ( is_wp_error( $retval ) )
487 + $this->print_ajax_response( 'error', $retval->get_error_message() );
488 +
489 + $this->print_ajax_response( 'success', (object)$this->get_follow_action_parts( $post ) );
490 + }
491 +
492 +
493 + /**
254 494 * Called when post is saved. Handles saving of user/usergroup followers
255 495 *
256 496 * @param int $post ID of the post
257 497 */
@@ -336,9 +576,9 @@
336 576 $body = '';
337 577
338 578 $post_id = $post->ID;
339 579 $post_title = ef_draft_or_post_title( $post_id );
340 - $post_type = ucwords( $post->post_type );
580 + $post_type = get_post_type_object( $post->post_type )->labels->singular_name;
341 581
342 582 if( 0 != $current_user->ID ) {
343 583 $current_user_display_name = $current_user->display_name;
344 584 $current_user_email = sprintf( '(%s)', $current_user->user_email );
@@ -349,8 +589,9 @@
349 589
350 590 // Email subject and first line of body
351 591 // Set message subjects according to what action is being taken on the Post
352 592 if ( $old_status == 'new' || $old_status == 'auto-draft' ) {
593 + $old_status_friendly_name = "New";
353 594 /* translators: 1: site name, 2: post type, 3. post title */
354 595 $subject = sprintf( __( '[%1$s] New %2$s Created: "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
355 596 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
356 597 $body .= sprintf( __( 'A new %1$s (#%2$s "%3$s") was created by %4$s %5$s', 'edit-flow' ), $post_type, $post_id, $post_title, $current_user->display_name, $current_user->user_email ) . "\r\n";
@@ -366,10 +607,10 @@
366 607 $body .= sprintf( __( '%1$s #%2$s "%3$s" was restored from trash by %4$s %5$s', 'edit-flow' ), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n";
367 608 } else if ( $new_status == 'future' ) {
368 609 /* translators: 1: site name, 2: post type, 3. post title */
369 610 $subject = sprintf( __('[%1$s] %2$s Scheduled: "%3$s"'), $blogname, $post_type, $post_title );
370 - /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
371 - $body .= sprintf( __( '%1$s #%2$s "%3$s" was scheduled by %4$s %5$s' ), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n";
611 + /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email 6. scheduled date */
612 + $body .= sprintf( __( '%1$s #%2$s "%3$s" was scheduled by %4$s %5$s. It will be published on %6$s' ), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email, $this->get_scheduled_datetime( $post ) ) . "\r\n";
372 613 } else if ( $new_status == 'publish' ) {
373 614 /* translators: 1: site name, 2: post type, 3. post title */
374 615 $subject = sprintf( __( '[%1$s] %2$s Published: "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
375 616 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
@@ -383,16 +624,18 @@
383 624 /* translators: 1: site name, 2: post type, 3. post title */
384 625 $subject = sprintf( __( '[%1$s] %2$s Status Changed for "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
385 626 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
386 627 $body .= sprintf( __( 'Status was changed for %1$s #%2$s "%3$s" by %4$s %5$s', 'edit-flow'), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n";
628 + $old_status_post_obj = get_post_status_object( $old_status );
629 + $old_status_friendly_name = $old_status_post_obj->label;
387 630 }
388 631
389 632 /* translators: 1: date, 2: time, 3: timezone */
390 633 $body .= sprintf( __( 'This action was taken on %1$s at %2$s %3$s', 'edit-flow' ), date_i18n( get_option( 'date_format' ) ), date_i18n( get_option( 'time_format' ) ), get_option( 'timezone_string' ) ) . "\r\n";
391 -
392 - $old_status_friendly_name = $this->get_post_status_friendly_name( $old_status );
393 - $new_status_friendly_name = $this->get_post_status_friendly_name( $new_status );
394 -
634 +
635 + $new_status_post_obj = get_post_status_object( $new_status );
636 + $new_status_friendly_name = $new_status_post_obj->label;
637 +
395 638 // Email body
396 639 $body .= "\r\n";
397 640 /* translators: 1: old status, 2: new status */
398 641 $body .= sprintf( __( '%1$s => %2$s', 'edit-flow' ), $old_status_friendly_name, $new_status_friendly_name );
@@ -401,15 +644,16 @@
401 644 $body .= "--------------------\r\n\r\n";
402 645
403 646 $body .= sprintf( __( '== %s Details ==', 'edit-flow' ), $post_type ) . "\r\n";
404 647 $body .= sprintf( __( 'Title: %s', 'edit-flow' ), $post_title ) . "\r\n";
405 - /* translators: 1: author name, 2: author email */
406 - $body .= sprintf( __( 'Author: %1$s (%2$s)', 'edit-flow' ), $post_author->display_name, $post_author->user_email ) . "\r\n";
648 + if ( ! empty( $post_author ) ) {
649 + /* translators: 1: author name, 2: author email */
650 + $body .= sprintf( __( 'Author: %1$s (%2$s)', 'edit-flow' ), $post_author->display_name, $post_author->user_email ) . "\r\n";
651 + }
407 652
408 653 $edit_link = htmlspecialchars_decode( get_edit_post_link( $post_id ) );
409 654 if ( $new_status != 'publish' ) {
410 - $preview_nonce = wp_create_nonce( 'post_preview_' . $post_id );
411 - $view_link = add_query_arg( array( 'preview' => true, 'preview_id' => $post_id, 'preview_nonce' => $preview_nonce ), get_permalink($post_id) );
655 + $view_link = add_query_arg( array( 'preview' => 'true' ), wp_get_shortlink( $post_id ) );
412 656 } else {
413 657 $view_link = htmlspecialchars_decode( get_permalink( $post_id ) );
414 658 }
415 659 $body .= "\r\n";
@@ -426,8 +670,11 @@
426 670 }
427 671
428 672 /**
429 673 * Set up and set editorial comment notification email
674 + *
675 + * @param WP_Comment $comment
676 + * @return boolean|null|void
430 677 */
431 678 function notification_comment( $comment ) {
432 679
433 680 $post = get_post($comment->comment_post_ID);
@@ -443,11 +690,14 @@
443 690 $user = get_userdata( $post->post_author );
444 691 $current_user = wp_get_current_user();
445 692
446 693 $post_id = $post->ID;
447 - $post_type = ucwords( $post->post_type );
694 + $post_type = get_post_type_object( $post->post_type )->labels->singular_name;
448 695 $post_title = ef_draft_or_post_title( $post_id );
449 -
696 +
697 + // Fetch the text list of people who were notified from comment meta @see EF_Editorial_Comments->maybe_output_comment_meta()
698 + $notification_list = get_comment_meta( $comment->comment_ID, 'notification_list', true );
699 +
450 700 // Check if this a reply
451 701 //$parent_ID = isset( $comment->comment_parent_ID ) ? $comment->comment_parent_ID : 0;
452 702 //if($parent_ID) $parent = get_comment($parent_ID);
453 703
@@ -476,9 +726,14 @@
476 726
477 727 }
478 728 */
479 729
730 +
480 731 $body .= "\r\n--------------------\r\n";
732 + // Insert the notification list from comment meta @see EF_Editorial_Comments->maybe_output_comment_meta()
733 + if ($notification_list) {
734 + $body .= esc_html__( 'Notified', 'edit-flow' ) . ": " . esc_html( $notification_list ) . "\n";
735 + }
481 736
482 737 $edit_link = htmlspecialchars_decode( get_edit_post_link( $post_id ) );
483 738 $view_link = htmlspecialchars_decode( get_permalink( $post_id ) );
484 739
@@ -517,8 +772,12 @@
517 772 $recipients = $this->_get_notification_recipients( $post, true );
518 773
519 774 if( $recipients && ! is_array( $recipients ) )
520 775 $recipients = explode( ',', $recipients );
776 +
777 + $subject = apply_filters( 'ef_notification_send_email_subject', $subject, $action, $post );
778 + $message = apply_filters( 'ef_notification_send_email_message', $message, $action, $post );
779 + $message_headers = apply_filters( 'ef_notification_send_email_message_headers', $message_headers, $action, $post );
521 780
522 781 if( EF_NOTIFICATION_USE_CRON ) {
523 782 $this->schedule_emails( $recipients, $subject, $message, $message_headers );
524 783 } else if ( !empty( $recipients ) ) {
@@ -559,64 +818,73 @@
559 818 */
560 819 function send_single_email( $to, $subject, $message, $message_headers = '' ) {
561 820 wp_mail( $to, $subject, $message, $message_headers );
562 821 }
563 -
822 +
564 823 /**
565 - * Returns a list of recipients for a given post
824 + * Returns a list of recipients for a given post.
566 825 *
567 - * @param $post object
568 - * @param $string bool Whether to return recipients as comma-delimited string or array
569 - * @return string or array of recipients to receive notification
826 + * @param WP_Post $post
827 + * @param bool $string Whether to return recipients as comma-delimited string or array.
828 + * @return string|array Recipients to receive notification.
570 829 */
571 830 private function _get_notification_recipients( $post, $string = false ) {
572 831 global $edit_flow;
573 -
832 +
574 833 $post_id = $post->ID;
575 - if( !$post_id ) return;
576 -
577 - $authors = array();
834 + if ( ! $post_id ) {
835 + return $string ? '' : array();
836 + }
837 +
838 + // Email all admins if enabled.
578 839 $admins = array();
579 - $recipients = array();
840 + if ( 'on' === $this->module->options->always_notify_admin ) {
841 + $admins[] = get_option('admin_email');
842 + }
580 843
581 - // Email all admins, if enabled
582 - if( 'on' == $this->module->options->always_notify_admin )
583 - $admins[] = get_option('admin_email');
584 -
585 - $usergroup_users = array();
844 + $usergroup_recipients = array();
586 845 if ( $this->module_enabled( 'user_groups' ) ) {
587 - // Get following users and usergroups
588 846 $usergroups = $this->get_following_usergroups( $post_id, 'ids' );
589 - foreach( (array)$usergroups as $usergroup_id ) {
847 + foreach ( (array) $usergroups as $usergroup_id ) {
590 848 $usergroup = $edit_flow->user_groups->get_usergroup_by( 'id', $usergroup_id );
591 - foreach( (array)$usergroup->user_ids as $user_id ) {
849 + foreach ( (array) $usergroup->user_ids as $user_id ) {
592 850 $usergroup_user = get_user_by( 'id', $user_id );
593 - if ( $usergroup_user )
594 - $usergroup_users[] = $usergroup_user->user_email;
851 + if ( $this->user_can_be_notified( $usergroup_user, $post_id ) ) {
852 + $usergroup_recipients[] = $usergroup_user->user_email;
853 + }
595 854 }
596 855 }
597 856 }
598 -
599 - $users = $this->get_following_users( $post_id, 'user_email' );
600 -
601 - // Merge arrays and filter any duplicates
602 - $recipients = array_merge( $authors, $admins, $users, $usergroup_users );
603 - $recipients = array_unique( $recipients );
604 857
605 - // Process the recipients for this email to be sent
606 - foreach( $recipients as $key => $user_email ) {
607 - // Get rid of empty email entries
608 - if ( empty( $recipients[$key] ) )
609 - unset( $recipients[$key] );
610 - // Don't send the email to the current user unless we've explicitly indicated they should receive it
611 - if ( false === apply_filters( 'ef_notification_email_current_user', false ) && wp_get_current_user()->user_email == $user_email )
612 - unset( $recipients[$key] );
858 + $user_recipients = $this->get_following_users( $post_id, 'user_email' );
859 + foreach( $user_recipients as $key => $user ) {
860 + $user_object = get_user_by( 'email', $user );
861 + if ( ! $this->user_can_be_notified( $user_object, $post_id ) ) {
862 + unset( $user_recipients[ $key ] );
863 + }
613 864 }
614 -
615 - // Filter to allow further modification of recipients
865 +
866 + // Merge arrays, filter any duplicates, and remove empty entries.
867 + $recipients = array_filter( array_unique( array_merge( $admins, $user_recipients, $usergroup_recipients ) ) );
868 +
869 + // Process the recipients for this email to be sent.
870 + foreach( $recipients as $key => $user_email ) {
871 + // Don't send the email to the current user unless we've explicitly indicated they should receive it.
872 + if ( false === apply_filters( 'ef_notification_email_current_user', false ) && wp_get_current_user()->user_email == $user_email ) {
873 + unset( $recipients[ $key ] );
874 + }
875 + }
876 +
877 + /**
878 + * Filters the list of notification recipients.
879 + *
880 + * @param array $recipients List of recipient email addresses.
881 + * @param WP_Post $post
882 + * @param bool $string True if the recipients list will later be returned as a string.
883 + */
616 884 $recipients = apply_filters( 'ef_notification_recipients', $recipients, $post, $string );
617 -
618 - // If string set to true, return comma-delimited
885 +
886 + // If string set to true, return comma-delimited.
619 887 if ( $string && is_array( $recipients ) ) {
620 888 return implode( ',', $recipients );
621 889 } else {
622 890 return $recipients;
@@ -621,97 +889,124 @@
621 889 } else {
622 890 return $recipients;
623 891 }
624 892 }
625 -
893 +
626 894 /**
627 - * Set set user to follow posts
895 + * Check if a user can be notified.
896 + * This is based off of the ability to edit the post/page by default.
897 + *
898 + * @since 0.8.3
899 + * @param WP_User $user
900 + * @param int $post_id
901 + * @return bool True if the user can be notified, false otherwise.
902 + */
903 + function user_can_be_notified( $user, $post_id ) {
904 + $can_be_notified = false;
905 +
906 + if ( $user instanceof WP_User && is_user_member_of_blog( $user->ID ) && is_numeric( $post_id ) ) {
907 + // The 'edit_post' cap check also covers the undocumented 'edit_page' cap.
908 + $can_be_notified = $user->has_cap( 'edit_post', $post_id );
909 + }
910 +
911 + /**
912 + * Filters if a user can be notified. Defaults to true if they can edit the post/page.
913 + *
914 + * @param bool $can_be_notified True if the user can be notified.
915 + * @param WP_User|bool $user The user object, otherwise false.
916 + * @param int $post_id The post the user will be notified about.
917 + */
918 + return (bool) apply_filters( 'ef_notification_user_can_be_notified', $can_be_notified, $user, $post_id );
919 + }
920 +
921 + /**
922 + * Set a user or users to follow a post
628 923 *
629 - * @param $post Post object
630 - * @param $users array (optional) List of users to be added. If not included, the current user is added.
631 - * @param $append bool Whether users should be added to following_users list or replace existing list
924 + * @param int|object $post Post object or ID
925 + * @param string|array $users User or users to subscribe to post updates
926 + * @param bool $append Whether users should be added to following_users list or replace existing list
927 + *
928 + * @return true|WP_Error $response True on success, WP_Error on failure
632 929 */
633 930 function follow_post_user( $post, $users, $append = true ) {
634 931
635 - // Clean up data we're using
636 - $post_id = ( is_int($post) ) ? $post : $post->ID;
637 - if( !is_array($users) ) $users = array($users);
932 + $post = get_post( $post );
933 + if ( ! $post )
934 + return new WP_Error( 'missing-post', $this->module->messages['missing-post'] );
638 935
936 + if ( ! is_array( $users ) )
937 + $users = array( $users );
938 +
639 939 $user_terms = array();
640 -
641 940 foreach( $users as $user ) {
642 - if( is_int($user) )
643 - $user_data = get_userdata($user);
644 - else if( is_string($user) )
645 - $user_data = get_userdatabylogin($user);
646 - else
647 - $user_data = $user;
941 +
942 + if ( is_int( $user ) )
943 + $user = get_user_by( 'id', $user );
944 + elseif ( is_string( $user ) )
945 + $user = get_user_by( 'login', $user );
946 +
947 + if ( ! is_object( $user ) )
948 + continue;
949 +
950 + $name = $user->user_login;
951 +
952 + // Add user as a term if they don't exist
953 + $term = $this->add_term_if_not_exists( $name, $this->following_users_taxonomy );
648 954
649 - if( $user_data ) {
650 - // Name and slug of term are the username;
651 - $name = $user_data->user_login;
652 -
653 - /** TODO: ONLY ADD IF USER IS NOT PART OF $this->exclude_users_taxonomy for the post **/
654 - // if( !user_excluding_post($post_id, $user) )
655 -
656 - // Add user as a term if they don't exist
657 - $term = $this->add_term_if_not_exists($name, $this->following_users_taxonomy);
658 -
659 - if(!is_wp_error($term)) {
660 - $user_terms[] = $name;
661 - }
955 + if ( ! is_wp_error( $term ) ) {
956 + $user_terms[] = $name;
662 957 }
663 958 }
664 - $set = wp_set_object_terms( $post_id, $user_terms, $this->following_users_taxonomy, $append );
665 -
666 - return;
959 + $set = wp_set_object_terms( $post->ID, $user_terms, $this->following_users_taxonomy, $append );
960 +
961 + if ( is_wp_error( $set ) )
962 + return $set;
963 + else
964 + return true;
667 965 }
668 966
669 967 /**
670 - * Removes user from following_users taxonomy for the given Post, so they no longer receive future notifications
671 - * Called when delete_user action is fired
968 + * Removes user from following_users taxonomy for the given Post,
969 + * so they no longer receive future notifications.
672 970 *
673 - * @param $post Post object
971 + * @param object $post Post object or ID
972 + * @param int|string|array $users One or more users to unfollow from the post
973 + * @return true|WP_Error $response True on success, WP_Error on failure
674 974 */
675 - function unfollow_post_user( $post, $user = 0 ) {
676 - global $current_user;
677 -
678 - // TODO: Finish this
679 -
680 - $post_id = $post->ID;
681 - //if(!$user) $user = wp_get_current_user();
975 + function unfollow_post_user( $post, $users ) {
682 976
683 - if(!$post_id || !$user || $user->ID == 0)
684 - return;
685 -
686 - // Name and slug of term are the username;
687 - $name = $user->user_login;
688 -
689 - // Remove the user from the following_users taxonomy
690 - if( term_exists($name, $this->following_users_taxonomy) ) {
691 - $set = wp_set_object_terms( $post->ID, $name, $this->following_users_taxonomy, true );
692 - $old_term_ids = wp_get_object_terms($post_id, $this->following_users_taxonomy, array('fields' => 'ids', 'orderby' => 'none'));
693 -
694 -/*
695 - $delete_terms = array_diff($old_tt_ids, $tt_ids);
696 - if ( $delete_terms ) {
697 - $in_delete_terms = "'" . implode("', '", $delete_terms) . "'";
698 - do_action( 'delete_term_relationships', $object_id, $delete_terms );
699 - $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id) );
700 - do_action( 'deleted_term_relationships', $object_id, $delete_terms );
701 - wp_update_term_count($delete_terms, $taxonomy);
702 - }
703 -*/
977 + $post = get_post( $post );
978 + if ( ! $post )
979 + return new WP_Error( 'missing-post', $this->module->messages['missing-post'] );
980 +
981 + if ( ! is_array( $users ) )
982 + $users = array( $users );
983 +
984 + $terms = get_the_terms( $post->ID, $this->following_users_taxonomy );
985 + if ( is_wp_error( $terms ) )
986 + return $terms;
987 +
988 + $user_terms = wp_list_pluck( $terms, 'slug' );
989 + foreach( $users as $user ) {
990 +
991 + if ( is_int( $user ) )
992 + $user = get_user_by( 'id', $user );
993 + elseif ( is_string( $user ) )
994 + $user = get_user_by( 'login', $user );
995 +
996 + if ( ! is_object( $user ) )
997 + continue;
998 +
999 + $key = array_search( $user->user_login, $user_terms );
1000 + if ( false !== $key )
1001 + unset( $user_terms[$key] );
704 1002 }
705 -
706 - // Add user to the unfollowing_users taxonomy
707 - $insert = $this->add_term_if_not_exists($name, $this->unfollowing_users_taxonomy);
708 -
709 - if(!is_wp_error($insert)) {
710 - $exclude = wp_set_object_terms( $post_id, $name, $this->unfollowing_users_taxonomy, true );
711 - }
712 -
713 - return;
1003 + $set = wp_set_object_terms( $post->ID, $user_terms, $this->following_users_taxonomy, false );
1004 +
1005 + if ( is_wp_error( $set ) )
1006 + return $set;
1007 + else
1008 + return true;
714 1009 }
715 1010
716 1011 /**
717 1012 * follow_post_usergroups()
@@ -717,10 +1012,8 @@
717 1012 * follow_post_usergroups()
718 1013 *
719 1014 */
720 1015 function follow_post_usergroups( $post, $usergroups = 0, $append = true ) {
721 - global $edit_flow;
722 -
723 1016 if ( !$this->module_enabled( 'user_groups' ) )
724 1017 return;
725 1018
726 1019 $post_id = ( is_int($post) ) ? $post : $post->ID;
@@ -726,15 +1019,14 @@
726 1019 $post_id = ( is_int($post) ) ? $post : $post->ID;
727 1020 if( !is_array($usergroups) )
728 1021 $usergroups = array($usergroups);
729 1022
730 - $usergroup_terms = array();
731 -
732 - foreach( $usergroups as $usergroup ) {
733 - // Name and slug of term is the usergroup slug
734 - $usergroup_data = $edit_flow->user_groups->get_usergroup_by( 'id', $usergroup );
1023 + // make sure each usergroup id is an integer and not a number stored as a string
1024 + foreach( $usergroups as $key => $usergroup ) {
1025 + $usergroups[$key] = intval($usergroup);
735 1026 }
736 - $set = wp_set_object_terms( $post_id, $usergroups, $this->following_usergroups_taxonomy, $append );
1027 +
1028 + wp_set_object_terms( $post_id, $usergroups, $this->following_usergroups_taxonomy, $append );
737 1029 return;
738 1030 }
739 1031
740 1032 /**
@@ -751,11 +1043,8 @@
751 1043 if( $user ) {
752 1044 // Delete term from the following_users taxonomy
753 1045 $user_following_term = get_term_by('name', $user->user_login, $this->following_users_taxonomy);
754 1046 if( $user_following_term ) wp_delete_term($user_following_term->term_id, $this->following_users_taxonomy);
755 - // Delete term from the unfollowing_users taxonomy
756 - $user_unfollowing_term = get_term_by('name', $user->user_login, $this->unfollowing_users_taxonomy);
757 - if( $user_unfollowing_term ) wp_delete_term($user_unfollowing_term->term_id, $this->unfollowing_users_taxonomy);
758 1047 }
759 1048 return;
760 1049 }
761 1050
@@ -805,10 +1094,12 @@
805 1094 $search = 'login';
806 1095 break;
807 1096 }
808 1097 $new_user = get_user_by( $search, $user );
809 - if ( !$new_user )
1098 + if ( ! $new_user || ! is_user_member_of_blog( $new_user->ID ) ) {
1099 + unset( $users[ $key ] );
810 1100 continue;
1101 + }
811 1102 switch( $return ) {
812 1103 case 'user_login':
813 1104 $users[$key] = $new_user->user_login;
814 1105 break;
@@ -867,12 +1158,19 @@
867 1158 if ( is_int($user) )
868 1159 $user = get_userdata($user)->user_login;
869 1160
870 1161 $post_args = array(
871 - $this->following_users_taxonomy => $user,
1162 + 'tax_query' => array(
1163 + array(
1164 + 'taxonomy' => $this->following_users_taxonomy,
1165 + 'field' => 'slug',
1166 + 'terms' => $user,
1167 + )
1168 + ),
872 1169 'posts_per_page' => '10',
873 1170 'orderby' => 'modified',
874 1171 'order' => 'DESC',
1172 + 'post_status' => 'any',
875 1173 );
876 1174 $post_args = apply_filters( 'ef_user_following_posts_query_args', $post_args );
877 1175 $posts = get_posts( $post_args );
878 1176 return $posts;
@@ -952,12 +1250,29 @@
952 1250 <?php do_settings_sections( $this->module->options_group_name ); ?>
953 1251 <?php
954 1252 echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />';
955 1253 ?>
956 - <p class="submit"><?php submit_button( null, 'primary', 'submit', false ); ?><a class="cancel-settings-link" href="<?php echo EDIT_FLOW_SETTINGS_PAGE; ?>"><?php _e( 'Back to Edit Flow' ); ?></a></p>
1254 + <p class="submit"><?php submit_button( null, 'primary', 'submit', false ); ?><a class="cancel-settings-link" href="<?php echo EDIT_FLOW_SETTINGS_PAGE; ?>"><?php _e( 'Back to Edit Flow', 'edit-flow' ); ?></a></p>
957 1255 </form>
958 1256 <?php
959 1257 }
960 -
1258 +
1259 + /**
1260 + * Gets a simple phrase containing the formatted date and time that the post is scheduled for.
1261 + *
1262 + * @since 0.8
1263 + *
1264 + * @param obj $post Post object
1265 + * @return str $scheduled_datetime The scheduled datetime in human-readable format
1266 + */
1267 + private function get_scheduled_datetime( $post ) {
1268 +
1269 + $scheduled_ts = strtotime( $post->post_date );
1270 +
1271 + $date = date_i18n( get_option( 'date_format' ), $scheduled_ts );
1272 + $time = date_i18n( get_option( 'time_format' ), $scheduled_ts );
1273 +
1274 + return sprintf( __( '%1$s at %2$s', 'edit-flow' ), $date, $time );
1275 + }
961 1276 }
962 1277
963 -}
1278 +}