PluginProbe
Edit Flow / 0.7.3
Edit Flow v0.7.3
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.3, at modules/notifications/notifications.php

963 lines 35.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class EF_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 = $edit_flow->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 = ucwords( $post->post_type );
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 = ucwords( $post->post_type );
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 if( EF_NOTIFICATION_USE_CRON ) {
523 $this->schedule_emails( $recipients, $subject, $message, $message_headers );
524 } else if ( !empty( $recipients ) ) {
525 foreach( $recipients as $recipient ) {
526 $this->send_single_email( $recipient, $subject, $message, $message_headers );
527 }
528 }
529 }
530
531 /**
532 * Schedules emails to be sent in succession
533 *
534 * @param mixed $recipients Individual email or array of emails
535 * @param string $subject Subject of the email
536 * @param string $message Body of the email
537 * @param string $message_headers. (optional) Message headers
538 * @param int $time_offset (optional) Delay in seconds per email
539 */
540 function schedule_emails( $recipients, $subject, $message, $message_headers = '', $time_offset = 1 ) {
541 $recipients = (array) $recipients;
542
543 $send_time = time();
544
545 foreach( $recipients as $recipient ) {
546 wp_schedule_single_event( $send_time, 'ef_send_scheduled_email', array( $recipient, $subject, $message, $message_headers ) );
547 $send_time += $time_offset;
548 }
549
550 }
551
552 /**
553 * Sends an individual email
554 *
555 * @param mixed $to Email to send to
556 * @param string $subject Subject of the email
557 * @param string $message Body of the email
558 * @param string $message_headers. (optional) Message headers
559 */
560 function send_single_email( $to, $subject, $message, $message_headers = '' ) {
561 wp_mail( $to, $subject, $message, $message_headers );
562 }
563
564 /**
565 * Returns a list of recipients for a given post
566 *
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
570 */
571 private function _get_notification_recipients( $post, $string = false ) {
572 global $edit_flow;
573
574 $post_id = $post->ID;
575 if( !$post_id ) return;
576
577 $authors = array();
578 $admins = array();
579 $recipients = array();
580
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();
586 if ( $this->module_enabled( 'user_groups' ) ) {
587 // Get following users and usergroups
588 $usergroups = $this->get_following_usergroups( $post_id, 'ids' );
589 foreach( (array)$usergroups as $usergroup_id ) {
590 $usergroup = $edit_flow->user_groups->get_usergroup_by( 'id', $usergroup_id );
591 foreach( (array)$usergroup->user_ids as $user_id ) {
592 $usergroup_user = get_user_by( 'id', $user_id );
593 if ( $usergroup_user )
594 $usergroup_users[] = $usergroup_user->user_email;
595 }
596 }
597 }
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
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] );
613 }
614
615 // Filter to allow further modification of recipients
616 $recipients = apply_filters( 'ef_notification_recipients', $recipients, $post, $string );
617
618 // If string set to true, return comma-delimited
619 if ( $string && is_array( $recipients ) ) {
620 return implode( ',', $recipients );
621 } else {
622 return $recipients;
623 }
624 }
625
626 /**
627 * Set set user to follow posts
628 *
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
632 */
633 function follow_post_user( $post, $users, $append = true ) {
634
635 // Clean up data we're using
636 $post_id = ( is_int($post) ) ? $post : $post->ID;
637 if( !is_array($users) ) $users = array($users);
638
639 $user_terms = array();
640
641 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;
648
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 }
662 }
663 }
664 $set = wp_set_object_terms( $post_id, $user_terms, $this->following_users_taxonomy, $append );
665
666 return;
667 }
668
669 /**
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
672 *
673 * @param $post Post object
674 */
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();
682
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 */
704 }
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;
714 }
715
716 /**
717 * follow_post_usergroups()
718 *
719 */
720 function follow_post_usergroups( $post, $usergroups = 0, $append = true ) {
721 global $edit_flow;
722
723 if ( !$this->module_enabled( 'user_groups' ) )
724 return;
725
726 $post_id = ( is_int($post) ) ? $post : $post->ID;
727 if( !is_array($usergroups) )
728 $usergroups = array($usergroups);
729
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 );
735 }
736 $set = wp_set_object_terms( $post_id, $usergroups, $this->following_usergroups_taxonomy, $append );
737 return;
738 }
739
740 /**
741 * Removes users that are deleted from receiving future notifications (i.e. makes them unfollow posts FOREVER!)
742 *
743 * @param $id int ID of the user
744 */
745 function delete_user_action( $id ) {
746 if( !$id ) return;
747
748 // get user data
749 $user = get_userdata($id);
750
751 if( $user ) {
752 // Delete term from the following_users taxonomy
753 $user_following_term = get_term_by('name', $user->user_login, $this->following_users_taxonomy);
754 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 }
759 return;
760 }
761
762 /**
763 * Add user as a term if they aren't already
764 * @param $term string term to be added
765 * @param $taxonomy string taxonomy to add term to
766 * @return WP_error if insert fails, true otherwise
767 */
768 function add_term_if_not_exists( $term, $taxonomy ) {
769 if ( !term_exists($term, $taxonomy) ) {
770 $args = array( 'slug' => sanitize_title($term) );
771 return wp_insert_term( $term, $taxonomy, $args );
772 }
773 return true;
774 }
775
776 /**
777 * Gets a list of the users following the specified post
778 *
779 * @param int $post_id The ID of the post
780 * @param string $return The field to return
781 * @return array $users Users following the specified posts
782 */
783 function get_following_users( $post_id, $return = 'user_login' ) {
784
785 // Get following_users terms for the post
786 $users = wp_get_object_terms( $post_id, $this->following_users_taxonomy, array('fields' => 'names') );
787
788 // Don't have any following users
789 if( !$users || is_wp_error($users) )
790 return array();
791
792 // if just want user_login, return as is
793 if ( $return == 'user_login' )
794 return $users;
795
796 foreach( (array)$users as $key => $user ) {
797 switch( $user ) {
798 case is_int( $user ):
799 $search = 'id';
800 break;
801 case is_email( $user ):
802 $search = 'email';
803 break;
804 default:
805 $search = 'login';
806 break;
807 }
808 $new_user = get_user_by( $search, $user );
809 if ( !$new_user )
810 continue;
811 switch( $return ) {
812 case 'user_login':
813 $users[$key] = $new_user->user_login;
814 break;
815 case 'id':
816 $users[$key] = $new_user->ID;
817 break;
818 case 'user_email':
819 $users[$key] = $new_user->user_email;
820 break;
821 }
822 }
823 if( !$users || is_wp_error($users) )
824 $users = array();
825 return $users;
826
827 }
828
829 /**
830 * Gets a list of the usergroups that are following specified post
831 *
832 * @param int $post_id
833 * @return array $usergroups All of the usergroup slugs
834 */
835 function get_following_usergroups( $post_id, $return = 'all' ) {
836 global $edit_flow;
837
838 // Workaround for the fact that get_object_terms doesn't return just slugs
839 if( $return == 'slugs' )
840 $fields = 'all';
841 else
842 $fields = $return;
843
844 $usergroups = wp_get_object_terms( $post_id, $this->following_usergroups_taxonomy, array( 'fields' => $fields ) );
845
846 if( $return == 'slugs' ) {
847 $slugs = array();
848 foreach($usergroups as $usergroup) {
849 $slugs[] = $usergroup->slug;
850 }
851 $usergroups = $slugs;
852 }
853 return $usergroups;
854 }
855
856 /**
857 * Gets a list of posts that a user is following
858 *
859 * @param string|int $user user_login or id of user
860 * @param array $args
861 * @return array $posts Posts a user is following
862 */
863 function get_user_following_posts( $user = 0, $args = null ) {
864 if ( !$user )
865 $user = (int) wp_get_current_user()->ID;
866
867 if ( is_int($user) )
868 $user = get_userdata($user)->user_login;
869
870 $post_args = array(
871 $this->following_users_taxonomy => $user,
872 'posts_per_page' => '10',
873 'orderby' => 'modified',
874 'order' => 'DESC',
875 );
876 $post_args = apply_filters( 'ef_user_following_posts_query_args', $post_args );
877 $posts = get_posts( $post_args );
878 return $posts;
879
880 }
881
882 /**
883 * Register settings for notifications so we can partially use the Settings API
884 * (We use the Settings API for form generation, but not saving)
885 *
886 * @since 0.7
887 */
888 function register_settings() {
889 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name );
890 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' );
891 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' );
892 }
893
894 /**
895 * Chose the post types for notifications
896 *
897 * @since 0.7
898 */
899 function settings_post_types_option() {
900 global $edit_flow;
901 $edit_flow->settings->helper_option_custom_post_type( $this->module );
902 }
903
904 /**
905 * Option for whether the blog admin email address should be always notified or not
906 *
907 * @since 0.7
908 */
909 function settings_always_notify_admin_option() {
910 $options = array(
911 'off' => __( 'Disabled', 'edit-flow' ),
912 'on' => __( 'Enabled', 'edit-flow' ),
913 );
914 echo '<select id="always_notify_admin" name="' . $this->module->options_group_name . '[always_notify_admin]">';
915 foreach ( $options as $value => $label ) {
916 echo '<option value="' . esc_attr( $value ) . '"';
917 echo selected( $this->module->options->always_notify_admin, $value );
918 echo '>' . esc_html( $label ) . '</option>';
919 }
920 echo '</select>';
921 }
922
923 /**
924 * Validate our user input as the settings are being saved
925 *
926 * @since 0.7
927 */
928 function settings_validate( $new_options ) {
929
930 // Whitelist validation for the post type options
931 if ( !isset( $new_options['post_types'] ) )
932 $new_options['post_types'] = array();
933 $new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support );
934
935 // Whitelist validation for the 'always_notify_admin' options
936 if ( !isset( $new_options['always_notify_admin'] ) || $new_options['always_notify_admin'] != 'on' )
937 $new_options['always_notify_admin'] = 'off';
938
939 return $new_options;
940
941 }
942
943 /**
944 * Settings page for notifications
945 *
946 * @since 0.7
947 */
948 function print_configure_view() {
949 ?>
950 <form class="basic-settings" action="<?php echo esc_url( menu_page_url( $this->module->settings_slug, false ) ); ?>" method="post">
951 <?php settings_fields( $this->module->options_group_name ); ?>
952 <?php do_settings_sections( $this->module->options_group_name ); ?>
953 <?php
954 echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />';
955 ?>
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>
957 </form>
958 <?php
959 }
960
961 }
962
963 }