PluginProbe
Edit Flow / 0.7.5
Edit Flow v0.7.5
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.5, at modules/notifications/notifications.php

967 lines 36.3 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' => '_update_post_term_count',
159 'label' => false,
160 'query_var' => false,
161 'rewrite' => false,
162 'public' => 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 $message_headers = apply_filters( 'ef_notification_send_email_message_headers', $message_headers, $action, $post );
525
526 if( EF_NOTIFICATION_USE_CRON ) {
527 $this->schedule_emails( $recipients, $subject, $message, $message_headers );
528 } else if ( !empty( $recipients ) ) {
529 foreach( $recipients as $recipient ) {
530 $this->send_single_email( $recipient, $subject, $message, $message_headers );
531 }
532 }
533 }
534
535 /**
536 * Schedules emails to be sent in succession
537 *
538 * @param mixed $recipients Individual email or array of emails
539 * @param string $subject Subject of the email
540 * @param string $message Body of the email
541 * @param string $message_headers. (optional) Message headers
542 * @param int $time_offset (optional) Delay in seconds per email
543 */
544 function schedule_emails( $recipients, $subject, $message, $message_headers = '', $time_offset = 1 ) {
545 $recipients = (array) $recipients;
546
547 $send_time = time();
548
549 foreach( $recipients as $recipient ) {
550 wp_schedule_single_event( $send_time, 'ef_send_scheduled_email', array( $recipient, $subject, $message, $message_headers ) );
551 $send_time += $time_offset;
552 }
553
554 }
555
556 /**
557 * Sends an individual email
558 *
559 * @param mixed $to Email to send to
560 * @param string $subject Subject of the email
561 * @param string $message Body of the email
562 * @param string $message_headers. (optional) Message headers
563 */
564 function send_single_email( $to, $subject, $message, $message_headers = '' ) {
565 wp_mail( $to, $subject, $message, $message_headers );
566 }
567
568 /**
569 * Returns a list of recipients for a given post
570 *
571 * @param $post object
572 * @param $string bool Whether to return recipients as comma-delimited string or array
573 * @return string or array of recipients to receive notification
574 */
575 private function _get_notification_recipients( $post, $string = false ) {
576 global $edit_flow;
577
578 $post_id = $post->ID;
579 if( !$post_id ) return;
580
581 $authors = array();
582 $admins = array();
583 $recipients = array();
584
585 // Email all admins, if enabled
586 if( 'on' == $this->module->options->always_notify_admin )
587 $admins[] = get_option('admin_email');
588
589 $usergroup_users = array();
590 if ( $this->module_enabled( 'user_groups' ) ) {
591 // Get following users and usergroups
592 $usergroups = $this->get_following_usergroups( $post_id, 'ids' );
593 foreach( (array)$usergroups as $usergroup_id ) {
594 $usergroup = $edit_flow->user_groups->get_usergroup_by( 'id', $usergroup_id );
595 foreach( (array)$usergroup->user_ids as $user_id ) {
596 $usergroup_user = get_user_by( 'id', $user_id );
597 if ( $usergroup_user )
598 $usergroup_users[] = $usergroup_user->user_email;
599 }
600 }
601 }
602
603 $users = $this->get_following_users( $post_id, 'user_email' );
604
605 // Merge arrays and filter any duplicates
606 $recipients = array_merge( $authors, $admins, $users, $usergroup_users );
607 $recipients = array_unique( $recipients );
608
609 // Process the recipients for this email to be sent
610 foreach( $recipients as $key => $user_email ) {
611 // Get rid of empty email entries
612 if ( empty( $recipients[$key] ) )
613 unset( $recipients[$key] );
614 // Don't send the email to the current user unless we've explicitly indicated they should receive it
615 if ( false === apply_filters( 'ef_notification_email_current_user', false ) && wp_get_current_user()->user_email == $user_email )
616 unset( $recipients[$key] );
617 }
618
619 // Filter to allow further modification of recipients
620 $recipients = apply_filters( 'ef_notification_recipients', $recipients, $post, $string );
621
622 // If string set to true, return comma-delimited
623 if ( $string && is_array( $recipients ) ) {
624 return implode( ',', $recipients );
625 } else {
626 return $recipients;
627 }
628 }
629
630 /**
631 * Set set user to follow posts
632 *
633 * @param $post Post object
634 * @param $users array (optional) List of users to be added. If not included, the current user is added.
635 * @param $append bool Whether users should be added to following_users list or replace existing list
636 */
637 function follow_post_user( $post, $users, $append = true ) {
638
639 // Clean up data we're using
640 $post_id = ( is_int($post) ) ? $post : $post->ID;
641 if( !is_array($users) ) $users = array($users);
642
643 $user_terms = array();
644
645 foreach( $users as $user ) {
646 if( is_int($user) )
647 $user_data = get_userdata($user);
648 else if( is_string($user) )
649 $user_data = get_userdatabylogin($user);
650 else
651 $user_data = $user;
652
653 if( $user_data ) {
654 // Name and slug of term are the username;
655 $name = $user_data->user_login;
656
657 /** TODO: ONLY ADD IF USER IS NOT PART OF $this->exclude_users_taxonomy for the post **/
658 // if( !user_excluding_post($post_id, $user) )
659
660 // Add user as a term if they don't exist
661 $term = $this->add_term_if_not_exists($name, $this->following_users_taxonomy);
662
663 if(!is_wp_error($term)) {
664 $user_terms[] = $name;
665 }
666 }
667 }
668 $set = wp_set_object_terms( $post_id, $user_terms, $this->following_users_taxonomy, $append );
669
670 return;
671 }
672
673 /**
674 * Removes user from following_users taxonomy for the given Post, so they no longer receive future notifications
675 * Called when delete_user action is fired
676 *
677 * @param $post Post object
678 */
679 function unfollow_post_user( $post, $user = 0 ) {
680 global $current_user;
681
682 // TODO: Finish this
683
684 $post_id = $post->ID;
685 //if(!$user) $user = wp_get_current_user();
686
687 if(!$post_id || !$user || $user->ID == 0)
688 return;
689
690 // Name and slug of term are the username;
691 $name = $user->user_login;
692
693 // Remove the user from the following_users taxonomy
694 if( term_exists($name, $this->following_users_taxonomy) ) {
695 $set = wp_set_object_terms( $post->ID, $name, $this->following_users_taxonomy, true );
696 $old_term_ids = wp_get_object_terms($post_id, $this->following_users_taxonomy, array('fields' => 'ids', 'orderby' => 'none'));
697
698 /*
699 $delete_terms = array_diff($old_tt_ids, $tt_ids);
700 if ( $delete_terms ) {
701 $in_delete_terms = "'" . implode("', '", $delete_terms) . "'";
702 do_action( 'delete_term_relationships', $object_id, $delete_terms );
703 $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id) );
704 do_action( 'deleted_term_relationships', $object_id, $delete_terms );
705 wp_update_term_count($delete_terms, $taxonomy);
706 }
707 */
708 }
709
710 // Add user to the unfollowing_users taxonomy
711 $insert = $this->add_term_if_not_exists($name, $this->unfollowing_users_taxonomy);
712
713 if(!is_wp_error($insert)) {
714 $exclude = wp_set_object_terms( $post_id, $name, $this->unfollowing_users_taxonomy, true );
715 }
716
717 return;
718 }
719
720 /**
721 * follow_post_usergroups()
722 *
723 */
724 function follow_post_usergroups( $post, $usergroups = 0, $append = true ) {
725 global $edit_flow;
726
727 if ( !$this->module_enabled( 'user_groups' ) )
728 return;
729
730 $post_id = ( is_int($post) ) ? $post : $post->ID;
731 if( !is_array($usergroups) )
732 $usergroups = array($usergroups);
733
734 $usergroup_terms = array();
735
736 foreach( $usergroups as $usergroup ) {
737 // Name and slug of term is the usergroup slug
738 $usergroup_data = $edit_flow->user_groups->get_usergroup_by( 'id', $usergroup );
739 }
740 $set = wp_set_object_terms( $post_id, $usergroups, $this->following_usergroups_taxonomy, $append );
741 return;
742 }
743
744 /**
745 * Removes users that are deleted from receiving future notifications (i.e. makes them unfollow posts FOREVER!)
746 *
747 * @param $id int ID of the user
748 */
749 function delete_user_action( $id ) {
750 if( !$id ) return;
751
752 // get user data
753 $user = get_userdata($id);
754
755 if( $user ) {
756 // Delete term from the following_users taxonomy
757 $user_following_term = get_term_by('name', $user->user_login, $this->following_users_taxonomy);
758 if( $user_following_term ) wp_delete_term($user_following_term->term_id, $this->following_users_taxonomy);
759 // Delete term from the unfollowing_users taxonomy
760 $user_unfollowing_term = get_term_by('name', $user->user_login, $this->unfollowing_users_taxonomy);
761 if( $user_unfollowing_term ) wp_delete_term($user_unfollowing_term->term_id, $this->unfollowing_users_taxonomy);
762 }
763 return;
764 }
765
766 /**
767 * Add user as a term if they aren't already
768 * @param $term string term to be added
769 * @param $taxonomy string taxonomy to add term to
770 * @return WP_error if insert fails, true otherwise
771 */
772 function add_term_if_not_exists( $term, $taxonomy ) {
773 if ( !term_exists($term, $taxonomy) ) {
774 $args = array( 'slug' => sanitize_title($term) );
775 return wp_insert_term( $term, $taxonomy, $args );
776 }
777 return true;
778 }
779
780 /**
781 * Gets a list of the users following the specified post
782 *
783 * @param int $post_id The ID of the post
784 * @param string $return The field to return
785 * @return array $users Users following the specified posts
786 */
787 function get_following_users( $post_id, $return = 'user_login' ) {
788
789 // Get following_users terms for the post
790 $users = wp_get_object_terms( $post_id, $this->following_users_taxonomy, array('fields' => 'names') );
791
792 // Don't have any following users
793 if( !$users || is_wp_error($users) )
794 return array();
795
796 // if just want user_login, return as is
797 if ( $return == 'user_login' )
798 return $users;
799
800 foreach( (array)$users as $key => $user ) {
801 switch( $user ) {
802 case is_int( $user ):
803 $search = 'id';
804 break;
805 case is_email( $user ):
806 $search = 'email';
807 break;
808 default:
809 $search = 'login';
810 break;
811 }
812 $new_user = get_user_by( $search, $user );
813 if ( !$new_user )
814 continue;
815 switch( $return ) {
816 case 'user_login':
817 $users[$key] = $new_user->user_login;
818 break;
819 case 'id':
820 $users[$key] = $new_user->ID;
821 break;
822 case 'user_email':
823 $users[$key] = $new_user->user_email;
824 break;
825 }
826 }
827 if( !$users || is_wp_error($users) )
828 $users = array();
829 return $users;
830
831 }
832
833 /**
834 * Gets a list of the usergroups that are following specified post
835 *
836 * @param int $post_id
837 * @return array $usergroups All of the usergroup slugs
838 */
839 function get_following_usergroups( $post_id, $return = 'all' ) {
840 global $edit_flow;
841
842 // Workaround for the fact that get_object_terms doesn't return just slugs
843 if( $return == 'slugs' )
844 $fields = 'all';
845 else
846 $fields = $return;
847
848 $usergroups = wp_get_object_terms( $post_id, $this->following_usergroups_taxonomy, array( 'fields' => $fields ) );
849
850 if( $return == 'slugs' ) {
851 $slugs = array();
852 foreach($usergroups as $usergroup) {
853 $slugs[] = $usergroup->slug;
854 }
855 $usergroups = $slugs;
856 }
857 return $usergroups;
858 }
859
860 /**
861 * Gets a list of posts that a user is following
862 *
863 * @param string|int $user user_login or id of user
864 * @param array $args
865 * @return array $posts Posts a user is following
866 */
867 function get_user_following_posts( $user = 0, $args = null ) {
868 if ( !$user )
869 $user = (int) wp_get_current_user()->ID;
870
871 if ( is_int($user) )
872 $user = get_userdata($user)->user_login;
873
874 $post_args = array(
875 $this->following_users_taxonomy => $user,
876 'posts_per_page' => '10',
877 'orderby' => 'modified',
878 'order' => 'DESC',
879 );
880 $post_args = apply_filters( 'ef_user_following_posts_query_args', $post_args );
881 $posts = get_posts( $post_args );
882 return $posts;
883
884 }
885
886 /**
887 * Register settings for notifications so we can partially use the Settings API
888 * (We use the Settings API for form generation, but not saving)
889 *
890 * @since 0.7
891 */
892 function register_settings() {
893 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name );
894 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' );
895 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' );
896 }
897
898 /**
899 * Chose the post types for notifications
900 *
901 * @since 0.7
902 */
903 function settings_post_types_option() {
904 global $edit_flow;
905 $edit_flow->settings->helper_option_custom_post_type( $this->module );
906 }
907
908 /**
909 * Option for whether the blog admin email address should be always notified or not
910 *
911 * @since 0.7
912 */
913 function settings_always_notify_admin_option() {
914 $options = array(
915 'off' => __( 'Disabled', 'edit-flow' ),
916 'on' => __( 'Enabled', 'edit-flow' ),
917 );
918 echo '<select id="always_notify_admin" name="' . $this->module->options_group_name . '[always_notify_admin]">';
919 foreach ( $options as $value => $label ) {
920 echo '<option value="' . esc_attr( $value ) . '"';
921 echo selected( $this->module->options->always_notify_admin, $value );
922 echo '>' . esc_html( $label ) . '</option>';
923 }
924 echo '</select>';
925 }
926
927 /**
928 * Validate our user input as the settings are being saved
929 *
930 * @since 0.7
931 */
932 function settings_validate( $new_options ) {
933
934 // Whitelist validation for the post type options
935 if ( !isset( $new_options['post_types'] ) )
936 $new_options['post_types'] = array();
937 $new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support );
938
939 // Whitelist validation for the 'always_notify_admin' options
940 if ( !isset( $new_options['always_notify_admin'] ) || $new_options['always_notify_admin'] != 'on' )
941 $new_options['always_notify_admin'] = 'off';
942
943 return $new_options;
944
945 }
946
947 /**
948 * Settings page for notifications
949 *
950 * @since 0.7
951 */
952 function print_configure_view() {
953 ?>
954 <form class="basic-settings" action="<?php echo esc_url( menu_page_url( $this->module->settings_slug, false ) ); ?>" method="post">
955 <?php settings_fields( $this->module->options_group_name ); ?>
956 <?php do_settings_sections( $this->module->options_group_name ); ?>
957 <?php
958 echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />';
959 ?>
960 <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>
961 </form>
962 <?php
963 }
964
965 }
966
967 }