PluginProbe
Edit Flow / 0.7.4
Edit Flow v0.7.4
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 / notifications / notifications.php

notifications.php in Edit Flow 0.7.4, at modules/notifications/notifications.php

966 lines 36.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class EF_Notifications
4 * Email notifications for Edit Flow and more
5 */
6
7 if( ! defined( 'EF_NOTIFICATION_USE_CRON' ) )
8 define( 'EF_NOTIFICATION_USE_CRON', false );
9
10 if ( !class_exists('EF_Notifications') ) {
11
12 class EF_Notifications extends EF_Module {
13
14 // Taxonomy name used to store users following posts
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 // Taxonomy name used to store user groups following posts
19 var $following_usergroups_taxonomy = EF_User_Groups::taxonomy_key;
20
21 var $module;
22
23 var $edit_post_subscriptions_cap = 'edit_post_subscriptions';
24
25 /**
26 * Register the module with Edit Flow but don't do anything else
27 */
28 function __construct () {
29 global $edit_flow;
30
31 // Register the module with Edit Flow
32 $this->module_url = $this->get_module_url( __FILE__ );
33 $args = array(
34 'title' => __( 'Notifications', 'edit-flow' ),
35 'short_description' => __( 'Update your team of important changes to your content.', 'edit-flow' ),
36 'extended_description' => __( 'With email notifications, you can keep everyone updated about what’s happening with a given content. Each status change or editorial comment sends out an email notification to users subscribed to a post. User groups can be used to manage who receives notifications on what.', 'edit-flow' ),
37 'module_url' => $this->module_url,
38 'img_url' => $this->module_url . 'lib/notifications_s128.png',
39 'slug' => 'notifications',
40 'default_options' => array(
41 'enabled' => 'on',
42 'post_types' => array(
43 'post' => 'on',
44 'page' => 'on',
45 ),
46 'always_notify_admin' => 'off',
47 ),
48 'configure_page_cb' => 'print_configure_view',
49 'post_type_support' => 'ef_notification',
50 'autoload' => false,
51 'settings_help_tab' => array(
52 'id' => 'ef-notifications-overview',
53 'title' => __('Overview', 'edit-flow'),
54 '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 ),
56 '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 );
58 $this->module = EditFlow()->register_module( 'notifications', $args );
59
60 }
61
62 /**
63 * Initialize the notifications class if the plugin is enabled
64 */
65 function init() {
66
67 // Register our taxonomies for managing relationships
68 $this->register_taxonomies();
69
70 // Allow users to use a different user capability for editing post subscriptions
71 $this->edit_post_subscriptions_cap = apply_filters( 'ef_edit_post_subscriptions_cap', $this->edit_post_subscriptions_cap );
72
73 // Set up metabox and related actions
74 add_action( 'add_meta_boxes', array( $this, 'add_post_meta_box' ) );
75
76 // Saving post actions
77 // self::save_post_subscriptions() is hooked into transition_post_status so we can ensure usergroup data
78 // is properly saved before sending notifs
79 add_action( 'transition_post_status', array( $this, 'save_post_subscriptions' ), 0, 3 );
80 add_action( 'transition_post_status', array( $this, 'notification_status_change' ), 10, 3 );
81 add_action( 'ef_post_insert_editorial_comment', array( $this, 'notification_comment') );
82 add_action( 'delete_user', array($this, 'delete_user_action') );
83 add_action( 'ef_send_scheduled_email', array( $this, 'send_single_email' ), 10, 4 );
84
85 add_action( 'admin_init', array( $this, 'register_settings' ) );
86
87 // Javascript and CSS if we need it
88 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
89 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
90
91 }
92
93 /**
94 * Load the capabilities onto users the first time the module is run
95 *
96 * @since 0.7
97 */
98 function install() {
99
100 // Add necessary capabilities to allow management of notifications
101 $notifications_roles = array(
102 'administrator' => array('edit_post_subscriptions'),
103 'editor' => array('edit_post_subscriptions'),
104 'author' => array('edit_post_subscriptions'),
105 );
106
107 foreach ( $notifications_roles as $role => $caps ) {
108 $this->add_caps_to_role( $role, $caps );
109 }
110
111 }
112
113 /**
114 * Upgrade our data in case we need to
115 *
116 * @since 0.7
117 */
118 function upgrade( $previous_version ) {
119 global $edit_flow;
120
121 // Upgrade path to v0.7
122 if ( version_compare( $previous_version, '0.7' , '<' ) ) {
123 // Migrate whether notifications were enabled or not
124 if ( $enabled = get_option( 'edit_flow_notifications_enabled' ) )
125 $enabled = 'on';
126 else
127 $enabled = 'off';
128 $edit_flow->update_module_option( $this->module->name, 'enabled', $enabled );
129 delete_option( 'edit_flow_notifications_enabled' );
130 // Migrate whether to always notify the admin
131 if ( $always_notify_admin = get_option( 'edit_flow_always_notify_admin' ) )
132 $always_notify_admin = 'on';
133 else
134 $always_notify_admin = 'off';
135 $edit_flow->update_module_option( $this->module->name, 'always_notify_admin', $always_notify_admin );
136 delete_option( 'edit_flow_always_notify_admin' );
137
138 // Technically we've run this code before so we don't want to auto-install new data
139 $edit_flow->update_module_option( $this->module->name, 'loaded_once', true );
140 }
141
142 }
143
144 /**
145 * Register the taxonomies we use to manage relationships
146 *
147 * @since 0.7
148 *
149 * @uses register_taxonomy()
150 */
151 function register_taxonomies() {
152
153 // Load the currently supported post types so we only register against those
154 $supported_post_types = $this->get_post_types_for_module( $this->module );
155
156 $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
164 );
165 foreach( array( $this->following_users_taxonomy, $this->unfollowing_users_taxonomy ) as $taxonomy ) {
166 register_taxonomy( $taxonomy, $supported_post_types, $args );
167 }
168 }
169
170 /**
171 * Enqueue necessary admin scripts
172 *
173 * @since 0.7
174 *
175 * @uses wp_enqueue_script()
176 */
177 function enqueue_admin_scripts() {
178
179 if ( $this->is_whitelisted_functional_view() ) {
180 wp_enqueue_script( 'jquery-listfilterizer' );
181 wp_enqueue_script( 'jquery-quicksearch' );
182 wp_enqueue_script( 'edit-flow-notifications-js', $this->module_url . 'lib/notifications.js', array( 'jquery', 'jquery-listfilterizer', 'jquery-quicksearch' ), EDIT_FLOW_VERSION, true );
183 }
184 }
185
186 /**
187 * Enqueue necessary admin styles, but only on the proper pages
188 *
189 * @since 0.7
190 *
191 * @uses wp_enqueue_style()
192 */
193 function enqueue_admin_styles() {
194
195 if ( $this->is_whitelisted_functional_view() || $this->is_whitelisted_settings_view() ) {
196 wp_enqueue_style( 'jquery-listfilterizer' );
197 wp_enqueue_style( 'edit-flow-notifications-css', $this->module->module_url . 'lib/notifications.css', false, EDIT_FLOW_VERSION );
198 }
199 }
200
201 /**
202 * Add the subscriptions meta box to relevant post types
203 */
204 function add_post_meta_box() {
205
206 if ( !current_user_can( $this->edit_post_subscriptions_cap ) )
207 return;
208
209 $usergroup_post_types = $this->get_post_types_for_module( $this->module );
210 foreach ( $usergroup_post_types as $post_type ) {
211 add_meta_box( 'edit-flow-notifications', __( 'Notifications', 'edit-flow'), array( $this, 'notifications_meta_box'), $post_type, 'advanced' );
212 }
213 }
214
215 /**
216 * Outputs box used to subscribe users and usergroups to Posts
217 *
218 * @todo add_cap to set subscribers for posts; default to Admin and editors
219 */
220 function notifications_meta_box() {
221 global $post, $post_ID, $edit_flow;
222
223 ?>
224 <div id="ef-post_following_box">
225 <a name="subscriptions"></a>
226
227 <p><?php _e( 'Select the users and user groups that should receive notifications when the status of this post is updated or when an editorial comment is added.', 'edit-flow' ); ?></p>
228 <div id="ef-post_following_users_box">
229 <h4><?php _e( 'Users', 'edit-flow' ); ?></h4>
230 <?php
231 $followers = $this->get_following_users( $post->ID, 'id' );
232 $select_form_args = array(
233 'list_class' => 'ef-post_following_list',
234 );
235 $this->users_select_form( $followers, $select_form_args ); ?>
236 </div>
237
238 <?php if ( $this->module_enabled( 'user_groups' ) && in_array( $this->get_current_post_type(), $this->get_post_types_for_module( $edit_flow->user_groups->module ) ) ): ?>
239 <div id="ef-post_following_usergroups_box">
240 <h4><?php _e('User Groups', 'edit-flow') ?></h4>
241 <?php
242 $following_usergroups = $this->get_following_usergroups( $post->ID, 'ids' );
243 $edit_flow->user_groups->usergroups_select_form( $following_usergroups ); ?>
244 </div>
245 <?php endif; ?>
246 <div class="clear"></div>
247 <input type="hidden" name="ef-save_followers" value="1" /> <?php // Extra protection against autosaves ?>
248 </div>
249
250 <?php
251 }
252
253 /**
254 * Called when post is saved. Handles saving of user/usergroup followers
255 *
256 * @param int $post ID of the post
257 */
258 function save_post_subscriptions( $new_status, $old_status, $post ) {
259 global $edit_flow;
260 // only if has edit_post_subscriptions cap
261 if( ( !wp_is_post_revision($post) && !wp_is_post_autosave($post) ) && isset($_POST['ef-save_followers']) && current_user_can( $this->edit_post_subscriptions_cap ) ) {
262 $users = isset( $_POST['ef-selected-users'] ) ? $_POST['ef-selected-users'] : array();
263 $usergroups = isset( $_POST['following_usergroups'] ) ? $_POST['following_usergroups'] : array();
264 $this->save_post_following_users( $post, $users );
265 if ( $this->module_enabled( 'user_groups' ) && in_array( $this->get_current_post_type(), $this->get_post_types_for_module( $edit_flow->user_groups->module ) ) )
266 $this->save_post_following_usergroups( $post, $usergroups );
267 }
268
269 }
270
271 /**
272 * Sets users to follow specified post
273 *
274 * @param int $post ID of the post
275 */
276 function save_post_following_users( $post, $users = null ) {
277 if( !is_array( $users ) )
278 $users = array();
279
280 // Add current user to following users
281 $user = wp_get_current_user();
282 if ( $user && apply_filters( 'ef_notification_auto_subscribe_current_user', true, 'subscription_action' ) )
283 $users[] = $user->ID;
284
285 // Add post author to following users
286 if ( apply_filters( 'ef_notification_auto_subscribe_post_author', true, 'subscription_action' ) )
287 $users[] = $post->post_author;
288
289 $users = array_unique( array_map( 'intval', $users ) );
290
291 $follow = $this->follow_post_user( $post, $users, false );
292
293 }
294
295 /**
296 * Sets usergroups to follow specified post
297 *
298 * @param int $post ID of the post
299 * @param array $usergroups Usergroups to follow posts
300 */
301 function save_post_following_usergroups( $post, $usergroups = null ) {
302
303 if( !is_array($usergroups) ) $usergroups = array();
304 $usergroups = array_map( 'intval', $usergroups );
305
306 $follow = $this->follow_post_usergroups($post, $usergroups, false);
307 }
308
309 /**
310 * Set up and send post status change notification email
311 */
312 function notification_status_change( $new_status, $old_status, $post ) {
313 global $edit_flow;
314
315 // Kill switch for notification
316 if ( ! apply_filters( 'ef_notification_status_change', $new_status, $old_status, $post ) || ! apply_filters( "ef_notification_{$post->post_type}_status_change", $new_status, $old_status, $post ) )
317 return false;
318
319 $supported_post_types = $this->get_post_types_for_module( $this->module );
320 if ( !in_array( $post->post_type, $supported_post_types ) )
321 return;
322
323 // No need to notify if it's a revision, auto-draft, or if post status wasn't changed
324 $ignored_statuses = apply_filters( 'ef_notification_ignored_statuses', array( $old_status, 'inherit', 'auto-draft' ), $post->post_type );
325
326 if ( !in_array( $new_status, $ignored_statuses ) ) {
327
328 // Get current user
329 $current_user = wp_get_current_user();
330
331 $post_author = get_userdata( $post->post_author );
332 //$duedate = $edit_flow->post_metadata->get_post_meta($post->ID, 'duedate', true);
333
334 $blogname = get_option('blogname');
335
336 $body = '';
337
338 $post_id = $post->ID;
339 $post_title = ef_draft_or_post_title( $post_id );
340 $post_type = get_post_type_object( $post->post_type )->labels->singular_name;
341
342 if( 0 != $current_user->ID ) {
343 $current_user_display_name = $current_user->display_name;
344 $current_user_email = sprintf( '(%s)', $current_user->user_email );
345 } else {
346 $current_user_display_name = __( 'WordPress Scheduler', 'edit-flow' );
347 $current_user_email = '';
348 }
349
350 // Email subject and first line of body
351 // Set message subjects according to what action is being taken on the Post
352 if ( $old_status == 'new' || $old_status == 'auto-draft' ) {
353 /* translators: 1: site name, 2: post type, 3. post title */
354 $subject = sprintf( __( '[%1$s] New %2$s Created: "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
355 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
356 $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";
357 } else if ( $new_status == 'trash' ) {
358 /* translators: 1: site name, 2: post type, 3. post title */
359 $subject = sprintf( __( '[%1$s] %2$s Trashed: "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
360 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
361 $body .= sprintf( __( '%1$s #%2$s "%3$s" was moved to the trash by %4$s %5$s', 'edit-flow' ), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n";
362 } else if ( $old_status == 'trash' ) {
363 /* translators: 1: site name, 2: post type, 3. post title */
364 $subject = sprintf( __( '[%1$s] %2$s Restored (from Trash): "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
365 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
366 $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 } else if ( $new_status == 'future' ) {
368 /* translators: 1: site name, 2: post type, 3. post title */
369 $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";
372 } else if ( $new_status == 'publish' ) {
373 /* translators: 1: site name, 2: post type, 3. post title */
374 $subject = sprintf( __( '[%1$s] %2$s Published: "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
375 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
376 $body .= sprintf( __( '%1$s #%2$s "%3$s" was published by %4$s %5$s', 'edit-flow' ), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n";
377 } else if ( $old_status == 'publish' ) {
378 /* translators: 1: site name, 2: post type, 3. post title */
379 $subject = sprintf( __( '[%1$s] %2$s Unpublished: "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
380 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
381 $body .= sprintf( __( '%1$s #%2$s "%3$s" was unpublished by %4$s %5$s', 'edit-flow' ), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n";
382 } else {
383 /* translators: 1: site name, 2: post type, 3. post title */
384 $subject = sprintf( __( '[%1$s] %2$s Status Changed for "%3$s"', 'edit-flow' ), $blogname, $post_type, $post_title );
385 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */
386 $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";
387 }
388
389 /* translators: 1: date, 2: time, 3: timezone */
390 $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
395 // Email body
396 $body .= "\r\n";
397 /* translators: 1: old status, 2: new status */
398 $body .= sprintf( __( '%1$s => %2$s', 'edit-flow' ), $old_status_friendly_name, $new_status_friendly_name );
399 $body .= "\r\n\r\n";
400
401 $body .= "--------------------\r\n\r\n";
402
403 $body .= sprintf( __( '== %s Details ==', 'edit-flow' ), $post_type ) . "\r\n";
404 $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";
407
408 $edit_link = htmlspecialchars_decode( get_edit_post_link( $post_id ) );
409 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) );
412 } else {
413 $view_link = htmlspecialchars_decode( get_permalink( $post_id ) );
414 }
415 $body .= "\r\n";
416 $body .= __( '== Actions ==', 'edit-flow' ) . "\r\n";
417 $body .= sprintf( __( 'Add editorial comment: %s', 'edit-flow' ), $edit_link . '#editorialcomments/add' ) . "\r\n";
418 $body .= sprintf( __( 'Edit: %s', 'edit-flow' ), $edit_link ) . "\r\n";
419 $body .= sprintf( __( 'View: %s', 'edit-flow' ), $view_link ) . "\r\n";
420
421 $body .= $this->get_notification_footer($post);
422
423 $this->send_email( 'status-change', $post, $subject, $body );
424 }
425
426 }
427
428 /**
429 * Set up and set editorial comment notification email
430 */
431 function notification_comment( $comment ) {
432
433 $post = get_post($comment->comment_post_ID);
434
435 $supported_post_types = $this->get_post_types_for_module( $this->module );
436 if ( !in_array( $post->post_type, $supported_post_types ) )
437 return;
438
439 // Kill switch for notification
440 if ( ! apply_filters( 'ef_notification_editorial_comment', $comment, $post ) )
441 return false;
442
443 $user = get_userdata( $post->post_author );
444 $current_user = wp_get_current_user();
445
446 $post_id = $post->ID;
447 $post_type = get_post_type_object( $post->post_type )->labels->singular_name;
448 $post_title = ef_draft_or_post_title( $post_id );
449
450 // Check if this a reply
451 //$parent_ID = isset( $comment->comment_parent_ID ) ? $comment->comment_parent_ID : 0;
452 //if($parent_ID) $parent = get_comment($parent_ID);
453
454 // Set user to follow post, but make it filterable
455 if ( apply_filters( 'ef_notification_auto_subscribe_current_user', true, 'comment' ) )
456 $this->follow_post_user($post, (int) $current_user->ID);
457
458 // Set the post author to follow the post but make it filterable
459 if ( apply_filters( 'ef_notification_auto_subscribe_post_author', true, 'comment' ) )
460 $this->follow_post_user( $post, (int) $post->post_author );
461
462 $blogname = get_option('blogname');
463
464 /* translators: 1: blog name, 2: post title */
465 $subject = sprintf( __( '[%1$s] New Editorial Comment: "%2$s"', 'edit-flow' ), $blogname, $post_title );
466
467 /* translators: 1: post id, 2: post title, 3. post type */
468 $body = sprintf( __( 'A new editorial comment was added to %3$s #%1$s "%2$s"', 'edit-flow' ), $post_id, $post_title, $post_type ) . "\r\n\r\n";
469 /* translators: 1: comment author, 2: author email, 3: date, 4: time */
470 $body .= sprintf( __( '%1$s (%2$s) said on %3$s at %4$s:', 'edit-flow' ), $current_user->display_name, $current_user->user_email, mysql2date(get_option('date_format'), $comment->comment_date), mysql2date(get_option('time_format'), $comment->comment_date) ) . "\r\n";
471 $body .= "\r\n" . $comment->comment_content . "\r\n";
472
473 // @TODO: mention if it was a reply
474 /*
475 if($parent) {
476
477 }
478 */
479
480 $body .= "\r\n--------------------\r\n";
481
482 $edit_link = htmlspecialchars_decode( get_edit_post_link( $post_id ) );
483 $view_link = htmlspecialchars_decode( get_permalink( $post_id ) );
484
485 $body .= "\r\n";
486 $body .= __( '== Actions ==', 'edit-flow' ) . "\r\n";
487 $body .= sprintf( __( 'Reply: %s', 'edit-flow' ), $edit_link . '#editorialcomments/reply/' . $comment->comment_ID ) . "\r\n";
488 $body .= sprintf( __( 'Add editorial comment: %s', 'edit-flow' ), $edit_link . '#editorialcomments/add' ) . "\r\n";
489 $body .= sprintf( __( 'Edit: %s', 'edit-flow' ), $edit_link ) . "\r\n";
490 $body .= sprintf( __( 'View: %s', 'edit-flow' ), $view_link ) . "\r\n";
491
492 $body .= "\r\n" . sprintf( __( 'You can see all editorial comments on this %s here: ', 'edit-flow' ), $post_type ). "\r\n";
493 $body .= $edit_link . "#editorialcomments" . "\r\n\r\n";
494
495 $body .= $this->get_notification_footer($post);
496
497 $this->send_email( 'comment', $post, $subject, $body );
498 }
499
500 function get_notification_footer( $post ) {
501 $body = "";
502 $body .= "\r\n--------------------\r\n";
503 $body .= sprintf( __( 'You are receiving this email because you are subscribed to "%s".', 'edit-flow' ), ef_draft_or_post_title ($post->ID ) );
504 $body .= "\r\n";
505 $body .= sprintf( __( 'This email was sent %s.', 'edit-flow' ), date( 'r' ) );
506 $body .= "\r\n \r\n";
507 $body .= get_option('blogname') ." | ". get_bloginfo('url') . " | " . admin_url('/') . "\r\n";
508 return $body;
509 }
510
511 /**
512 * send_email()
513 */
514 function send_email( $action, $post, $subject, $message, $message_headers = '' ) {
515
516 // Get list of email recipients -- set them CC
517 $recipients = $this->_get_notification_recipients( $post, true );
518
519 if( $recipients && ! is_array( $recipients ) )
520 $recipients = explode( ',', $recipients );
521
522 $subject = apply_filters( 'ef_notification_send_email_subject', $subject, $action, $post );
523 $message = apply_filters( 'ef_notification_send_email_message', $message, $action, $post );
524
525 if( EF_NOTIFICATION_USE_CRON ) {
526 $this->schedule_emails( $recipients, $subject, $message, $message_headers );
527 } else if ( !empty( $recipients ) ) {
528 foreach( $recipients as $recipient ) {
529 $this->send_single_email( $recipient, $subject, $message, $message_headers );
530 }
531 }
532 }
533
534 /**
535 * Schedules emails to be sent in succession
536 *
537 * @param mixed $recipients Individual email or array of emails
538 * @param string $subject Subject of the email
539 * @param string $message Body of the email
540 * @param string $message_headers. (optional) Message headers
541 * @param int $time_offset (optional) Delay in seconds per email
542 */
543 function schedule_emails( $recipients, $subject, $message, $message_headers = '', $time_offset = 1 ) {
544 $recipients = (array) $recipients;
545
546 $send_time = time();
547
548 foreach( $recipients as $recipient ) {
549 wp_schedule_single_event( $send_time, 'ef_send_scheduled_email', array( $recipient, $subject, $message, $message_headers ) );
550 $send_time += $time_offset;
551 }
552
553 }
554
555 /**
556 * Sends an individual email
557 *
558 * @param mixed $to Email to send to
559 * @param string $subject Subject of the email
560 * @param string $message Body of the email
561 * @param string $message_headers. (optional) Message headers
562 */
563 function send_single_email( $to, $subject, $message, $message_headers = '' ) {
564 wp_mail( $to, $subject, $message, $message_headers );
565 }
566
567 /**
568 * Returns a list of recipients for a given post
569 *
570 * @param $post object
571 * @param $string bool Whether to return recipients as comma-delimited string or array
572 * @return string or array of recipients to receive notification
573 */
574 private function _get_notification_recipients( $post, $string = false ) {
575 global $edit_flow;
576
577 $post_id = $post->ID;
578 if( !$post_id ) return;
579
580 $authors = array();
581 $admins = array();
582 $recipients = array();
583
584 // Email all admins, if enabled
585 if( 'on' == $this->module->options->always_notify_admin )
586 $admins[] = get_option('admin_email');
587
588 $usergroup_users = array();
589 if ( $this->module_enabled( 'user_groups' ) ) {
590 // Get following users and usergroups
591 $usergroups = $this->get_following_usergroups( $post_id, 'ids' );
592 foreach( (array)$usergroups as $usergroup_id ) {
593 $usergroup = $edit_flow->user_groups->get_usergroup_by( 'id', $usergroup_id );
594 foreach( (array)$usergroup->user_ids as $user_id ) {
595 $usergroup_user = get_user_by( 'id', $user_id );
596 if ( $usergroup_user )
597 $usergroup_users[] = $usergroup_user->user_email;
598 }
599 }
600 }
601
602 $users = $this->get_following_users( $post_id, 'user_email' );
603
604 // Merge arrays and filter any duplicates
605 $recipients = array_merge( $authors, $admins, $users, $usergroup_users );
606 $recipients = array_unique( $recipients );
607
608 // Process the recipients for this email to be sent
609 foreach( $recipients as $key => $user_email ) {
610 // Get rid of empty email entries
611 if ( empty( $recipients[$key] ) )
612 unset( $recipients[$key] );
613 // Don't send the email to the current user unless we've explicitly indicated they should receive it
614 if ( false === apply_filters( 'ef_notification_email_current_user', false ) && wp_get_current_user()->user_email == $user_email )
615 unset( $recipients[$key] );
616 }
617
618 // Filter to allow further modification of recipients
619 $recipients = apply_filters( 'ef_notification_recipients', $recipients, $post, $string );
620
621 // If string set to true, return comma-delimited
622 if ( $string && is_array( $recipients ) ) {
623 return implode( ',', $recipients );
624 } else {
625 return $recipients;
626 }
627 }
628
629 /**
630 * Set set user to follow posts
631 *
632 * @param $post Post object
633 * @param $users array (optional) List of users to be added. If not included, the current user is added.
634 * @param $append bool Whether users should be added to following_users list or replace existing list
635 */
636 function follow_post_user( $post, $users, $append = true ) {
637
638 // Clean up data we're using
639 $post_id = ( is_int($post) ) ? $post : $post->ID;
640 if( !is_array($users) ) $users = array($users);
641
642 $user_terms = array();
643
644 foreach( $users as $user ) {
645 if( is_int($user) )
646 $user_data = get_userdata($user);
647 else if( is_string($user) )
648 $user_data = get_userdatabylogin($user);
649 else
650 $user_data = $user;
651
652 if( $user_data ) {
653 // Name and slug of term are the username;
654 $name = $user_data->user_login;
655
656 /** TODO: ONLY ADD IF USER IS NOT PART OF $this->exclude_users_taxonomy for the post **/
657 // if( !user_excluding_post($post_id, $user) )
658
659 // Add user as a term if they don't exist
660 $term = $this->add_term_if_not_exists($name, $this->following_users_taxonomy);
661
662 if(!is_wp_error($term)) {
663 $user_terms[] = $name;
664 }
665 }
666 }
667 $set = wp_set_object_terms( $post_id, $user_terms, $this->following_users_taxonomy, $append );
668
669 return;
670 }
671
672 /**
673 * Removes user from following_users taxonomy for the given Post, so they no longer receive future notifications
674 * Called when delete_user action is fired
675 *
676 * @param $post Post object
677 */
678 function unfollow_post_user( $post, $user = 0 ) {
679 global $current_user;
680
681 // TODO: Finish this
682
683 $post_id = $post->ID;
684 //if(!$user) $user = wp_get_current_user();
685
686 if(!$post_id || !$user || $user->ID == 0)
687 return;
688
689 // Name and slug of term are the username;
690 $name = $user->user_login;
691
692 // Remove the user from the following_users taxonomy
693 if( term_exists($name, $this->following_users_taxonomy) ) {
694 $set = wp_set_object_terms( $post->ID, $name, $this->following_users_taxonomy, true );
695 $old_term_ids = wp_get_object_terms($post_id, $this->following_users_taxonomy, array('fields' => 'ids', 'orderby' => 'none'));
696
697 /*
698 $delete_terms = array_diff($old_tt_ids, $tt_ids);
699 if ( $delete_terms ) {
700 $in_delete_terms = "'" . implode("', '", $delete_terms) . "'";
701 do_action( 'delete_term_relationships', $object_id, $delete_terms );
702 $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id) );
703 do_action( 'deleted_term_relationships', $object_id, $delete_terms );
704 wp_update_term_count($delete_terms, $taxonomy);
705 }
706 */
707 }
708
709 // Add user to the unfollowing_users taxonomy
710 $insert = $this->add_term_if_not_exists($name, $this->unfollowing_users_taxonomy);
711
712 if(!is_wp_error($insert)) {
713 $exclude = wp_set_object_terms( $post_id, $name, $this->unfollowing_users_taxonomy, true );
714 }
715
716 return;
717 }
718
719 /**
720 * follow_post_usergroups()
721 *
722 */
723 function follow_post_usergroups( $post, $usergroups = 0, $append = true ) {
724 global $edit_flow;
725
726 if ( !$this->module_enabled( 'user_groups' ) )
727 return;
728
729 $post_id = ( is_int($post) ) ? $post : $post->ID;
730 if( !is_array($usergroups) )
731 $usergroups = array($usergroups);
732
733 $usergroup_terms = array();
734
735 foreach( $usergroups as $usergroup ) {
736 // Name and slug of term is the usergroup slug
737 $usergroup_data = $edit_flow->user_groups->get_usergroup_by( 'id', $usergroup );
738 }
739 $set = wp_set_object_terms( $post_id, $usergroups, $this->following_usergroups_taxonomy, $append );
740 return;
741 }
742
743 /**
744 * Removes users that are deleted from receiving future notifications (i.e. makes them unfollow posts FOREVER!)
745 *
746 * @param $id int ID of the user
747 */
748 function delete_user_action( $id ) {
749 if( !$id ) return;
750
751 // get user data
752 $user = get_userdata($id);
753
754 if( $user ) {
755 // Delete term from the following_users taxonomy
756 $user_following_term = get_term_by('name', $user->user_login, $this->following_users_taxonomy);
757 if( $user_following_term ) wp_delete_term($user_following_term->term_id, $this->following_users_taxonomy);
758 // Delete term from the unfollowing_users taxonomy
759 $user_unfollowing_term = get_term_by('name', $user->user_login, $this->unfollowing_users_taxonomy);
760 if( $user_unfollowing_term ) wp_delete_term($user_unfollowing_term->term_id, $this->unfollowing_users_taxonomy);
761 }
762 return;
763 }
764
765 /**
766 * Add user as a term if they aren't already
767 * @param $term string term to be added
768 * @param $taxonomy string taxonomy to add term to
769 * @return WP_error if insert fails, true otherwise
770 */
771 function add_term_if_not_exists( $term, $taxonomy ) {
772 if ( !term_exists($term, $taxonomy) ) {
773 $args = array( 'slug' => sanitize_title($term) );
774 return wp_insert_term( $term, $taxonomy, $args );
775 }
776 return true;
777 }
778
779 /**
780 * Gets a list of the users following the specified post
781 *
782 * @param int $post_id The ID of the post
783 * @param string $return The field to return
784 * @return array $users Users following the specified posts
785 */
786 function get_following_users( $post_id, $return = 'user_login' ) {
787
788 // Get following_users terms for the post
789 $users = wp_get_object_terms( $post_id, $this->following_users_taxonomy, array('fields' => 'names') );
790
791 // Don't have any following users
792 if( !$users || is_wp_error($users) )
793 return array();
794
795 // if just want user_login, return as is
796 if ( $return == 'user_login' )
797 return $users;
798
799 foreach( (array)$users as $key => $user ) {
800 switch( $user ) {
801 case is_int( $user ):
802 $search = 'id';
803 break;
804 case is_email( $user ):
805 $search = 'email';
806 break;
807 default:
808 $search = 'login';
809 break;
810 }
811 $new_user = get_user_by( $search, $user );
812 if ( !$new_user )
813 continue;
814 switch( $return ) {
815 case 'user_login':
816 $users[$key] = $new_user->user_login;
817 break;
818 case 'id':
819 $users[$key] = $new_user->ID;
820 break;
821 case 'user_email':
822 $users[$key] = $new_user->user_email;
823 break;
824 }
825 }
826 if( !$users || is_wp_error($users) )
827 $users = array();
828 return $users;
829
830 }
831
832 /**
833 * Gets a list of the usergroups that are following specified post
834 *
835 * @param int $post_id
836 * @return array $usergroups All of the usergroup slugs
837 */
838 function get_following_usergroups( $post_id, $return = 'all' ) {
839 global $edit_flow;
840
841 // Workaround for the fact that get_object_terms doesn't return just slugs
842 if( $return == 'slugs' )
843 $fields = 'all';
844 else
845 $fields = $return;
846
847 $usergroups = wp_get_object_terms( $post_id, $this->following_usergroups_taxonomy, array( 'fields' => $fields ) );
848
849 if( $return == 'slugs' ) {
850 $slugs = array();
851 foreach($usergroups as $usergroup) {
852 $slugs[] = $usergroup->slug;
853 }
854 $usergroups = $slugs;
855 }
856 return $usergroups;
857 }
858
859 /**
860 * Gets a list of posts that a user is following
861 *
862 * @param string|int $user user_login or id of user
863 * @param array $args
864 * @return array $posts Posts a user is following
865 */
866 function get_user_following_posts( $user = 0, $args = null ) {
867 if ( !$user )
868 $user = (int) wp_get_current_user()->ID;
869
870 if ( is_int($user) )
871 $user = get_userdata($user)->user_login;
872
873 $post_args = array(
874 $this->following_users_taxonomy => $user,
875 'posts_per_page' => '10',
876 'orderby' => 'modified',
877 'order' => 'DESC',
878 );
879 $post_args = apply_filters( 'ef_user_following_posts_query_args', $post_args );
880 $posts = get_posts( $post_args );
881 return $posts;
882
883 }
884
885 /**
886 * Register settings for notifications so we can partially use the Settings API
887 * (We use the Settings API for form generation, but not saving)
888 *
889 * @since 0.7
890 */
891 function register_settings() {
892 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name );
893 add_settings_field( 'post_types', __( 'Post types for notifications:', 'edit-flow' ), array( $this, 'settings_post_types_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
894 add_settings_field( 'always_notify_admin', __( 'Always notify blog admin', 'edit-flow' ), array( $this, 'settings_always_notify_admin_option'), $this->module->options_group_name, $this->module->options_group_name . '_general' );
895 }
896
897 /**
898 * Chose the post types for notifications
899 *
900 * @since 0.7
901 */
902 function settings_post_types_option() {
903 global $edit_flow;
904 $edit_flow->settings->helper_option_custom_post_type( $this->module );
905 }
906
907 /**
908 * Option for whether the blog admin email address should be always notified or not
909 *
910 * @since 0.7
911 */
912 function settings_always_notify_admin_option() {
913 $options = array(
914 'off' => __( 'Disabled', 'edit-flow' ),
915 'on' => __( 'Enabled', 'edit-flow' ),
916 );
917 echo '<select id="always_notify_admin" name="' . $this->module->options_group_name . '[always_notify_admin]">';
918 foreach ( $options as $value => $label ) {
919 echo '<option value="' . esc_attr( $value ) . '"';
920 echo selected( $this->module->options->always_notify_admin, $value );
921 echo '>' . esc_html( $label ) . '</option>';
922 }
923 echo '</select>';
924 }
925
926 /**
927 * Validate our user input as the settings are being saved
928 *
929 * @since 0.7
930 */
931 function settings_validate( $new_options ) {
932
933 // Whitelist validation for the post type options
934 if ( !isset( $new_options['post_types'] ) )
935 $new_options['post_types'] = array();
936 $new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support );
937
938 // Whitelist validation for the 'always_notify_admin' options
939 if ( !isset( $new_options['always_notify_admin'] ) || $new_options['always_notify_admin'] != 'on' )
940 $new_options['always_notify_admin'] = 'off';
941
942 return $new_options;
943
944 }
945
946 /**
947 * Settings page for notifications
948 *
949 * @since 0.7
950 */
951 function print_configure_view() {
952 ?>
953 <form class="basic-settings" action="<?php echo esc_url( menu_page_url( $this->module->settings_slug, false ) ); ?>" method="post">
954 <?php settings_fields( $this->module->options_group_name ); ?>
955 <?php do_settings_sections( $this->module->options_group_name ); ?>
956 <?php
957 echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />';
958 ?>
959 <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>
960 </form>
961 <?php
962 }
963
964 }
965
966 }