Changeset 1871614
- Timestamp:
- 05/09/2018 04:11:42 PM (8 years ago)
- Location:
- publishpress/trunk
- Files:
-
- 2 deleted
- 8 edited
-
includes.php (modified) (1 diff)
-
libraries/Notifications/Traits/Dependency_Injector.php (modified) (2 diffs)
-
modules/async-notifications/async-notifications.php (modified) (1 diff)
-
modules/content-overview/content-overview.php (modified) (3 diffs)
-
modules/notifications/notifications.php (modified) (4 diffs)
-
modules/roles/roles.php (modified) (1 diff)
-
publishpress.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
-
vendor/twig/twig/.gitignore (deleted)
-
vendor/twig/twig/ext/twig/.gitignore (deleted)
Legend:
- Unmodified
- Added
- Removed
-
publishpress/trunk/includes.php
r1860640 r1871614 41 41 42 42 // Define contants 43 define('PUBLISHPRESS_VERSION', '1.12. 0');43 define('PUBLISHPRESS_VERSION', '1.12.1'); 44 44 define('PUBLISHPRESS_BASE_PATH', __DIR__); 45 45 define('PUBLISHPRESS_FILE_PATH', PUBLISHPRESS_BASE_PATH . '/' . basename(__FILE__)); -
publishpress/trunk/libraries/Notifications/Traits/Dependency_Injector.php
r1860640 r1871614 17 17 * Instance of the Pimple container 18 18 */ 19 protected $ pimple_container;19 protected $container; 20 20 21 21 /** … … 31 31 public function get_service($service_name) 32 32 { 33 if (empty($this-> pimple_container))33 if (empty($this->container)) 34 34 { 35 35 $this->init_pimple(); 36 36 } 37 37 38 if (!isset($this-> pimple_container[$service_name]))38 if (!isset($this->container[$service_name])) 39 39 { 40 40 throw new \Exception("Service \"{$service_name}\" not found in the container"); 41 41 } 42 42 43 return $this-> pimple_container[$service_name];43 return $this->container[$service_name]; 44 44 } 45 45 46 46 protected function init_pimple() 47 47 { 48 $this-> pimple_container = Pimple_Container::get_instance();48 $this->container = Pimple_Container::get_instance(); 49 49 } 50 50 } -
publishpress/trunk/modules/async-notifications/async-notifications.php
r1860640 r1871614 33 33 use PublishPress\Notifications\Traits\PublishPress_Module; 34 34 35 if (!class_exists('PP_Async_Notifications')) 36 { 37 /** 38 * class PP_Async_Notifications. Depends on the Improved Notifications module. 39 */ 40 class PP_Async_Notifications extends PP_Module 41 { 42 use Dependency_Injector, PublishPress_Module; 43 44 const SETTINGS_SLUG = 'pp-async-notifications-settings'; 45 46 const POST_STATUS_QUEUED = 'queued'; 47 48 const POST_STATUS_SENT = 'sent'; 49 50 const POST_STATUS_FAILED = 'failed'; 51 52 public $module_name = 'async-notifications'; 53 54 public $module_url; 55 56 /** 57 * Instace for the module 58 * 59 * @var stdClass 60 */ 61 public $module; 62 63 /** 64 * Construct the Notifications class 65 */ 66 public function __construct() 67 { 68 global $publishpress; 69 70 $this->twigPath = dirname(dirname(dirname(__FILE__))) . '/twig'; 71 72 $this->module_url = $this->get_module_url(__FILE__); 73 74 // Register the module with PublishPress 75 $args = [ 76 'title' => __('Async Notifications', 'publishpress'), 77 'short_description' => false, 78 'extended_description' => false, 79 'module_url' => $this->module_url, 80 'icon_class' => 'dashicons dashicons-feedback', 81 'slug' => 'async-notifications', 82 'default_options' => [ 83 'enabled' => 'on', 84 ], 85 'options_page' => false, 86 ]; 87 88 // Apply a filter to the default options 89 $args['default_options'] = apply_filters('publishpress_async_notif_default_options', $args['default_options']); 90 $this->module = $publishpress->register_module( 91 PublishPress\Legacy\Util::sanitize_module_name($this->module_name), 92 $args 93 ); 94 95 Auto_loader::register('\\PublishPress\\AsyncNotifications\\', __DIR__ . '/library'); 96 97 parent::__construct(); 98 } 99 100 /** 101 * Initialize the module. Conditionally loads if the module is enabled 102 * 103 * @throws Exception 104 */ 105 public function init() 106 { 107 add_filter('publishpress_notif_workflow_run_action', [$this, 'filter_workflow_run_action'], 10, 3); 108 109 add_action('publishpress_notif_queue', [$this, 'action_notif_queue'], 10, 5); 110 111 add_action('publishpress_cron_notify', [$this, 'action_cron_notify'], 10, 8); 112 } 113 114 /** 115 * Load default editorial metadata the first time the module is loaded 116 * 117 * @since 0.7 118 */ 119 public function install() 120 { 121 } 122 123 /** 124 * Upgrade our data in case we need to 125 * 126 * @since 0.7 127 */ 128 public function upgrade($previous_version) 129 { 130 } 131 132 /** 133 * @param string $action 134 * @param PublishPress\Notifications\Workflow\ $workflow 135 * @param string $channel 136 * 137 * @return string 138 */ 139 public function filter_workflow_run_action($action, $workflow, $channel) 140 { 141 // Change the action to send the notification to the queue, instead sending to receiver, directly. 142 $action = 'publishpress_notif_queue'; 143 144 return $action; 145 } 146 147 /** 148 * Enqueue the notification inse 149 * 150 * @param $workflow_post 151 * @param $action_args 152 * @param $receiver 153 * @param $content 154 * @param $channel 155 * 156 * @throws Exception; 157 */ 158 public function action_notif_queue($workflow_post, $action_args, $receiver, $content, $channel) 159 { 160 $queue = $this->get_service('notification_queue'); 161 162 $queue->enqueueNotification($workflow_post, $action_args, $receiver, $content, $channel); 163 } 164 165 /** 166 * @param $workflowPostId 167 * @param $action 168 * @param $postId 169 * @param $content 170 * @param $oldStatus 171 * @param $newStatus 172 * @param $channel 173 * @param $receiver 174 */ 175 public function action_cron_notify($workflowPostId, $action, $postId, $content, $oldStatus, $newStatus, $channel, $receiver) 176 { 177 $workflowPost = get_post($workflowPostId); 178 $actionArgs = [ 179 'action' => $action, 180 'post' => get_post($postId), 181 'new_status' => $newStatus, 182 'old_status' => $oldStatus, 183 ]; 184 $receivers = [$receiver]; 185 186 // Decode the content 187 $content = base64_decode(maybe_unserialize($content)); 188 189 /** 190 * Triggers the notification. This can be caught by notification channels. 191 * 192 * @param WP_Post $workflow_post 193 * @param array $action_args 194 * @param array $receivers 195 * @param array $content 196 * @param array $channel 197 */ 198 do_action('publishpress_notif_send_notification_' . $channel, $workflowPost, $actionArgs, $receivers, $content, $channel); 199 } 200 } 35 if ( ! class_exists( 'PP_Async_Notifications' ) ) { 36 /** 37 * class PP_Async_Notifications. Depends on the Improved Notifications module. 38 */ 39 class PP_Async_Notifications extends PP_Module { 40 use Dependency_Injector, PublishPress_Module; 41 42 const SETTINGS_SLUG = 'pp-async-notifications-settings'; 43 44 const POST_STATUS_QUEUED = 'queued'; 45 46 const POST_STATUS_SENT = 'sent'; 47 48 const POST_STATUS_FAILED = 'failed'; 49 50 const DEFAULT_DUPLICATED_NOTIFICATION_TIMEOUT = 60; 51 52 public $module_name = 'async-notifications'; 53 54 public $module_url; 55 56 /** 57 * Instace for the module 58 * 59 * @var stdClass 60 */ 61 public $module; 62 63 /** 64 * Construct the Notifications class 65 */ 66 public function __construct() { 67 global $publishpress; 68 69 $this->twigPath = dirname( dirname( dirname( __FILE__ ) ) ) . '/twig'; 70 71 $this->module_url = $this->get_module_url( __FILE__ ); 72 73 // Register the module with PublishPress 74 $args = [ 75 'title' => __( 'Async Notifications', 'publishpress' ), 76 'short_description' => false, 77 'extended_description' => false, 78 'module_url' => $this->module_url, 79 'icon_class' => 'dashicons dashicons-feedback', 80 'slug' => 'async-notifications', 81 'default_options' => [ 82 'enabled' => 'on', 83 ], 84 'options_page' => false, 85 ]; 86 87 // Apply a filter to the default options 88 $args['default_options'] = apply_filters( 'publishpress_async_notif_default_options', 89 $args['default_options'] ); 90 $this->module = $publishpress->register_module( 91 PublishPress\Legacy\Util::sanitize_module_name( $this->module_name ), 92 $args 93 ); 94 95 Auto_loader::register( '\\PublishPress\\AsyncNotifications\\', __DIR__ . '/library' ); 96 97 parent::__construct(); 98 } 99 100 /** 101 * Initialize the module. Conditionally loads if the module is enabled 102 * 103 * @throws Exception 104 */ 105 public function init() { 106 add_filter( 'publishpress_notif_workflow_run_action', [ $this, 'filter_workflow_run_action' ], 10, 3 ); 107 108 add_action( 'publishpress_notif_queue', [ $this, 'action_notif_queue' ], 10, 5 ); 109 110 add_action( 'publishpress_cron_notify', [ $this, 'action_cron_notify' ], 10, 8 ); 111 } 112 113 /** 114 * Load default editorial metadata the first time the module is loaded 115 * 116 * @since 0.7 117 */ 118 public function install() { 119 } 120 121 /** 122 * Upgrade our data in case we need to 123 * 124 * @since 0.7 125 */ 126 public function upgrade( $previous_version ) { 127 } 128 129 /** 130 * @param string $action 131 * @param PublishPress\Notifications\Workflow\ $workflow 132 * @param string $channel 133 * 134 * @return string 135 */ 136 public function filter_workflow_run_action( $action, $workflow, $channel ) { 137 // Change the action to send the notification to the queue, instead sending to receiver, directly. 138 $action = 'publishpress_notif_queue'; 139 140 return $action; 141 } 142 143 /** 144 * Enqueue the notification inse 145 * 146 * @param $workflow_post 147 * @param $action_args 148 * @param $receiver 149 * @param $content 150 * @param $channel 151 * 152 * @throws Exception; 153 */ 154 public function action_notif_queue( $workflow_post, $action_args, $receiver, $content, $channel ) { 155 $queue = $this->get_service( 'notification_queue' ); 156 157 $queue->enqueueNotification( $workflow_post, $action_args, $receiver, $content, $channel ); 158 } 159 160 /** 161 * Check if the notification was just sent, to avoid duplicated notifications when 162 * multiple requests try to run the same job. 163 * 164 * @param $args 165 * 166 * @return bool 167 */ 168 protected function is_duplicated_notification( $args ) { 169 // Calculate unique ID to avoid repeated notifications. 170 $uid = md5( maybe_serialize( func_get_args() ) ); 171 172 $transientName = 'ppnotif_' . $uid; 173 174 // Check if we already have the transient. 175 if ( get_transient( $transientName ) ) { 176 // Yes, duplicated notification. 177 return true; 178 } 179 180 // No, set the flag and return as non-duplicated. 181 set_transient( $transientName, 1, self::DEFAULT_DUPLICATED_NOTIFICATION_TIMEOUT ); 182 183 return false; 184 } 185 186 /** 187 * @param $workflowPostId 188 * @param $action 189 * @param $postId 190 * @param $content 191 * @param $oldStatus 192 * @param $newStatus 193 * @param $channel 194 * @param $receiver 195 */ 196 public function action_cron_notify( 197 $workflowPostId, 198 $action, 199 $postId, 200 $content, 201 $oldStatus, 202 $newStatus, 203 $channel, 204 $receiver 205 ) { 206 // Check if this is a duplicated notification and skip it. 207 // I hope this is a temporary fix. When scheduled, some notifications seems to be triggered multiple times 208 // by the same cron task. 209 if ( $this->is_duplicated_notification( func_get_args() ) ) { 210 return; 211 } 212 213 // Work the notification 214 $workflowPost = get_post( $workflowPostId ); 215 $actionArgs = [ 216 'action' => $action, 217 'post' => get_post( $postId ), 218 'new_status' => $newStatus, 219 'old_status' => $oldStatus, 220 ]; 221 $receivers = [ $receiver ]; 222 223 // Decode the content 224 $content = base64_decode( maybe_unserialize( $content ) ); 225 226 /** 227 * Triggers the notification. This can be caught by notification channels. 228 * 229 * @param WP_Post $workflow_post 230 * @param array $action_args 231 * @param array $receivers 232 * @param array $content 233 * @param array $channel 234 */ 235 do_action( 'publishpress_notif_send_notification_' . $channel, $workflowPost, $actionArgs, $receivers, 236 $content, $channel ); 237 } 238 } 201 239 } -
publishpress/trunk/modules/content-overview/content-overview.php
r1860640 r1871614 147 147 add_action('admin_init', array($this, 'handle_form_date_range_change')); 148 148 149 include_once PUBLISHPRESS_BASE_PATH . '/common/php/' . 'screen-options.php'; 150 151 if (function_exists('add_screen_options_panel')) 152 { 153 add_screen_options_panel( 154 self::USERMETA_KEY_PREFIX . 'screen_columns', 155 __('Screen Layout', 'publishpress'), 156 array($this, 'print_column_prefs'), 157 self::SCREEN_ID, 158 array($this, 'save_column_prefs'), 159 true 160 ); 161 } 149 add_action('admin_init', array($this, 'handle_screen_options')); 162 150 163 151 // Register the columns of data appearing on every term. This is hooked into admin_init … … 171 159 add_action('admin_enqueue_scripts', array($this, 'action_enqueue_admin_styles')); 172 160 } 161 162 public function handle_screen_options() { 163 include_once PUBLISHPRESS_BASE_PATH . '/common/php/' . 'screen-options.php'; 164 165 if (function_exists('add_screen_options_panel')) 166 { 167 add_screen_options_panel( 168 self::USERMETA_KEY_PREFIX . 'screen_columns', 169 __('Screen Layout', 'publishpress'), 170 array($this, 'print_column_prefs'), 171 self::SCREEN_ID, 172 array($this, 'save_column_prefs'), 173 true 174 ); 175 } 176 } 177 173 178 174 179 /** … … 860 865 $post_author = get_userdata($post->post_author); 861 866 862 return $post_author->display_name; 867 $author_name = is_object( $post_author ) ? $post_author->display_name : ''; 868 869 // @todo: Make this compatible with Multiple Authors 870 $author_name = apply_filters( 'the_author', $author_name ); 871 872 return $author_name; 863 873 break; 864 874 case 'post_date': -
publishpress/trunk/modules/notifications/notifications.php
r1860640 r1871614 29 29 */ 30 30 31 if (!defined('PP_NOTIFICATION_USE_CRON')) 32 { 33 define('PP_NOTIFICATION_USE_CRON', false); 31 if ( ! defined( 'PP_NOTIFICATION_USE_CRON' ) ) { 32 define( 'PP_NOTIFICATION_USE_CRON', false ); 34 33 } 35 34 36 if (!class_exists('PP_Notifications')) 37 { 38 /** 39 * Class PP_Notifications 40 * Notifications for PublishPress and more 41 */ 42 class PP_Notifications extends PP_Module 43 { 44 45 // Taxonomy name used to store users which will be notified for changes in the posts. 46 public $notify_user_taxonomy = 'pp_notify_user'; 47 48 // Taxonomy name used to store roles which will be notified for changes in the posts. 49 public $notify_role_taxonomy = 'pp_notify_role'; 50 51 public $module; 52 53 public $edit_post_subscriptions_cap = 'edit_post_subscriptions'; 54 55 /** 56 * Register the module with PublishPress but don't do anything else 57 */ 58 public function __construct() 59 { 60 61 // Register the module with PublishPress 62 $this->module_url = $this->get_module_url(__FILE__); 63 $args = array( 64 'title' => __('Default Notifications', 'publishpress'), 65 'short_description' => false, 66 'extended_description' => false, 67 'module_url' => $this->module_url, 68 'icon_class' => 'dashicons dashicons-email', 69 'slug' => 'notifications', 70 'default_options' => array( 71 'enabled' => 'on', 72 'post_types' => array( 73 'post' => 'on', 74 'page' => 'on', 75 ), 76 'notify_author_by_default' => '1', 77 'notify_current_user_by_default' => '1', 78 ), 79 'configure_page_cb' => 'print_configure_view', 80 'post_type_support' => 'pp_notification', 81 'autoload' => false, 82 'settings_help_tab' => array( 83 'id' => 'pp-notifications-overview', 84 'title' => __('Overview', 'publishpress'), 85 '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 roles.</p><p>When enabled, notifications can be sent when a post changes status or an editorial comment is left by a writer or an editor.</p>', 'publishpress'), 86 ), 87 'settings_help_sidebar' => __('<p><strong>For more information:</strong></p><p><a href="https://publishpress.com/features/notifications/">Notifications Documentation</a></p><p><a href="https://github.com/ostraining/PublishPress">PublishPress on Github</a></p>', 'publishpress'), 88 'general_options' => true, 89 ); 90 $this->module = PublishPress()->register_module('notifications', $args); 91 } 92 93 /** 94 * Initialize the notifications class if the plugin is enabled 95 */ 96 public function init() 97 { 98 99 // Register our taxonomies for managing relationships 100 $this->register_taxonomies(); 101 102 // Allow users to use a different user capability for editing post subscriptions 103 $this->edit_post_subscriptions_cap = apply_filters('pp_edit_post_subscriptions_cap', $this->edit_post_subscriptions_cap); 104 105 // Set up metabox and related actions 106 add_action('add_meta_boxes', array($this, 'add_post_meta_box')); 107 108 // Saving post actions 109 // self::save_post_subscriptions() is hooked into transition_post_status so we can ensure role data 110 // is properly saved before sending notifs 111 add_action('transition_post_status', array($this, 'notification_status_change'), PP_NOTIFICATION_PRIORITY_STATUS_CHANGE, 3); 112 add_action('pp_post_insert_editorial_comment', array($this, 'notification_comment')); 113 add_action('delete_user', array($this, 'delete_user_action')); 114 add_action('pp_send_scheduled_notification', array($this, 'send_single_email'), 10, 4); 115 116 add_action('admin_init', array($this, 'register_settings')); 117 118 // Javascript and CSS if we need it 119 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); 120 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_styles')); 121 122 // Add a "Notify" link to posts 123 if (apply_filters('pp_notifications_show_notify_link', true)) 124 { 125 // A little extra JS for the Notify button 126 add_action('admin_head', array($this, 'action_admin_head_notify_js')); 127 // Manage Posts 128 add_filter('post_row_actions', array($this, 'filter_post_row_actions'), 10, 2); 129 add_filter('page_row_actions', array($this, 'filter_post_row_actions'), 10, 2); 130 // Calendar and Content Overview 131 add_filter('pp_calendar_item_actions', array($this, 'filter_post_row_actions'), 10, 2); 132 add_filter('pp_story_budget_item_actions', array($this, 'filter_post_row_actions'), 10, 2); 133 } 134 135 add_filter('pp_notification_auto_subscribe_post_author', array($this, 'filter_pp_notification_auto_subscribe_post_author'), 10, 2); 136 add_filter('pp_notification_auto_subscribe_current_user', array($this, 'filter_pp_notification_auto_subscribe_current_user'), 10, 2); 137 138 add_action('save_post', array($this, 'action_save_post'), 10); 139 140 // Ajax for saving notification updates 141 add_action('wp_ajax_pp_notifications_user_post_subscription', array($this, 'handle_user_post_subscription')); 142 143 add_action('pp_send_notification_status_update', array($this, 'send_notification_status_update')); 144 add_action('pp_send_notification_comment', array($this, 'send_notification_comment')); 145 } 146 147 /** 148 * Load the capabilities onto users the first time the module is run 149 * 150 * @since 0.7 151 */ 152 public function install() 153 { 154 // Considering we could be moving from Edit Flow, we need to migrate the following users. 155 $this->migrateLegacyFollowingTerms(); 156 } 157 158 /** 159 * Upgrade our data in case we need to 160 * 161 * @since 0.7 162 */ 163 public function upgrade($previous_version) 164 { 165 global $publishpress; 166 167 // Upgrade path to v0.7 168 if (version_compare($previous_version, '0.7', '<')) 169 { 170 // Migrate whether notifications were enabled or not 171 if ($enabled = get_option('publishpress_notifications_enabled')) 172 { 173 $enabled = 'on'; 174 } else 175 { 176 $enabled = 'off'; 177 } 178 $publishpress->update_module_option($this->module->name, 'enabled', $enabled); 179 delete_option('publishpress_notifications_enabled'); 180 // Migrate whether to always notify the admin 181 // @todo: Remove after sometime. The setting always notify admin was removed. 182 if ($always_notify_admin = get_option('publishpress_always_notify_admin')) 183 { 184 $always_notify_admin = 'on'; 185 } else 186 { 187 $always_notify_admin = 'off'; 188 } 189 $publishpress->update_module_option($this->module->name, 'always_notify_admin', $always_notify_admin); 190 delete_option('publishpress_always_notify_admin'); 191 192 // Technically we've run this code before so we don't want to auto-install new data 193 $publishpress->update_module_option($this->module->name, 'loaded_once', true); 194 } 195 196 if (version_compare($previous_version, '1.10', '<=')) { 197 $this->migrateLegacyFollowingTerms(); 198 } 199 } 200 201 202 protected function migrateLegacyFollowingTerms() 203 { 204 global $wpdb; 205 206 // Migrate Following Users 207 $query = "UPDATE {$wpdb->prefix}term_taxonomy SET taxonomy = '{$this->notify_user_taxonomy}' WHERE taxonomy = 'following_users'"; 208 $wpdb->query($query); 209 } 210 211 /** 212 * Register the taxonomies we use to manage relationships 213 * 214 * @since 0.7 215 * 216 * @uses register_taxonomy() 217 */ 218 public function register_taxonomies() 219 { 220 // Load the currently supported post types so we only register against those 221 $supported_post_types = $this->get_post_types_for_module($this->module); 222 223 $args = array( 224 'hierarchical' => false, 225 'update_count_callback' => '_update_post_term_count', 226 'label' => false, 227 'query_var' => false, 228 'rewrite' => false, 229 'public' => false, 230 'show_ui' => false, 231 ); 232 233 register_taxonomy($this->notify_user_taxonomy, $supported_post_types, $args); 234 register_taxonomy($this->notify_role_taxonomy, $supported_post_types, $args); 235 } 236 237 /** 238 * Enqueue necessary admin scripts 239 * 240 * @since 0.7 241 * 242 * @uses wp_enqueue_script() 243 */ 244 public function enqueue_admin_scripts() 245 { 246 if ($this->is_whitelisted_functional_view()) 247 { 248 wp_enqueue_script( 249 'publishpress-notifications-js', 250 $this->module_url . 'assets/notifications.js', 251 array( 252 'jquery', 253 ), 254 PUBLISHPRESS_VERSION, 255 true 256 ); 257 258 wp_enqueue_script('publishpress-chosen-js', PUBLISHPRESS_URL . '/common/libs/chosen/chosen.jquery.js', 259 ['jquery'], PUBLISHPRESS_VERSION); 260 } 261 } 262 263 /** 264 * Enqueue necessary admin styles, but only on the proper pages 265 * 266 * @since 0.7 267 * 268 * @uses wp_enqueue_style() 269 */ 270 public function enqueue_admin_styles() 271 { 272 if ($this->is_whitelisted_functional_view() || $this->is_whitelisted_settings_view()) 273 { 274 wp_enqueue_style('jquery-listfilterizer'); 275 wp_enqueue_style( 276 'publishpress-notifications-css', 277 $this->module->module_url . 'assets/notifications.css', 278 false, 279 PUBLISHPRESS_VERSION 280 ); 281 282 wp_enqueue_style('publishpress-chosen-css', PUBLISHPRESS_URL . '/common/libs/chosen/chosen.css', false, 283 PUBLISHPRESS_VERSION); 284 } 285 } 286 287 /** 288 * JS required for the Notify link to work 289 * 290 * @since 0.8 291 */ 292 public function action_admin_head_notify_js() 293 { 294 ?> 35 if ( ! class_exists( 'PP_Notifications' ) ) { 36 /** 37 * Class PP_Notifications 38 * Notifications for PublishPress and more 39 */ 40 class PP_Notifications extends PP_Module { 41 42 // Taxonomy name used to store users which will be notified for changes in the posts. 43 public $notify_user_taxonomy = 'pp_notify_user'; 44 45 // Taxonomy name used to store roles which will be notified for changes in the posts. 46 public $notify_role_taxonomy = 'pp_notify_role'; 47 48 public $module; 49 50 public $edit_post_subscriptions_cap = 'edit_post_subscriptions'; 51 52 /** 53 * Register the module with PublishPress but don't do anything else 54 */ 55 public function __construct() { 56 57 // Register the module with PublishPress 58 $this->module_url = $this->get_module_url( __FILE__ ); 59 $args = [ 60 'title' => __( 'Default Notifications', 'publishpress' ), 61 'short_description' => false, 62 'extended_description' => false, 63 'module_url' => $this->module_url, 64 'icon_class' => 'dashicons dashicons-email', 65 'slug' => 'notifications', 66 'default_options' => [ 67 'enabled' => 'on', 68 'post_types' => [ 69 'post' => 'on', 70 'page' => 'on', 71 ], 72 'notify_author_by_default' => '1', 73 'notify_current_user_by_default' => '1', 74 ], 75 'configure_page_cb' => 'print_configure_view', 76 'post_type_support' => 'pp_notification', 77 'autoload' => false, 78 'settings_help_tab' => [ 79 'id' => 'pp-notifications-overview', 80 'title' => __( 'Overview', 'publishpress' ), 81 '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 roles.</p><p>When enabled, notifications can be sent when a post changes status or an editorial comment is left by a writer or an editor.</p>', 82 'publishpress' ), 83 ], 84 'settings_help_sidebar' => __( '<p><strong>For more information:</strong></p><p><a href="https://publishpress.com/features/notifications/">Notifications Documentation</a></p><p><a href="https://github.com/ostraining/PublishPress">PublishPress on Github</a></p>', 85 'publishpress' ), 86 'general_options' => true, 87 ]; 88 $this->module = PublishPress()->register_module( 'notifications', $args ); 89 } 90 91 /** 92 * Initialize the notifications class if the plugin is enabled 93 */ 94 public function init() { 95 96 // Register our taxonomies for managing relationships 97 $this->register_taxonomies(); 98 99 // Allow users to use a different user capability for editing post subscriptions 100 $this->edit_post_subscriptions_cap = apply_filters( 'pp_edit_post_subscriptions_cap', 101 $this->edit_post_subscriptions_cap ); 102 103 // Set up metabox and related actions 104 add_action( 'add_meta_boxes', [ $this, 'add_post_meta_box' ] ); 105 106 // Saving post actions 107 // self::save_post_subscriptions() is hooked into transition_post_status so we can ensure role data 108 // is properly saved before sending notifs 109 add_action( 'transition_post_status', [ $this, 'notification_status_change' ], 110 PP_NOTIFICATION_PRIORITY_STATUS_CHANGE, 3 ); 111 add_action( 'pp_post_insert_editorial_comment', [ $this, 'notification_comment' ] ); 112 add_action( 'delete_user', [ $this, 'delete_user_action' ] ); 113 add_action( 'pp_send_scheduled_notification', [ $this, 'send_single_email' ], 10, 4 ); 114 115 add_action( 'admin_init', [ $this, 'register_settings' ] ); 116 117 // Javascript and CSS if we need it 118 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] ); 119 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_styles' ] ); 120 121 // Add a "Notify" link to posts 122 if ( apply_filters( 'pp_notifications_show_notify_link', true ) ) { 123 // A little extra JS for the Notify button 124 add_action( 'admin_head', [ $this, 'action_admin_head_notify_js' ] ); 125 // Manage Posts 126 add_filter( 'post_row_actions', [ $this, 'filter_post_row_actions' ], 10, 2 ); 127 add_filter( 'page_row_actions', [ $this, 'filter_post_row_actions' ], 10, 2 ); 128 // Calendar and Content Overview 129 add_filter( 'pp_calendar_item_actions', [ $this, 'filter_post_row_actions' ], 10, 2 ); 130 add_filter( 'pp_story_budget_item_actions', [ $this, 'filter_post_row_actions' ], 10, 2 ); 131 } 132 133 add_filter( 'pp_notification_auto_subscribe_post_author', 134 [ $this, 'filter_pp_notification_auto_subscribe_post_author' ], 10, 2 ); 135 add_filter( 'pp_notification_auto_subscribe_current_user', 136 [ $this, 'filter_pp_notification_auto_subscribe_current_user' ], 10, 2 ); 137 138 add_action( 'save_post', [ $this, 'action_save_post' ], 10 ); 139 140 // Ajax for saving notification updates 141 add_action( 'wp_ajax_pp_notifications_user_post_subscription', [ $this, 'handle_user_post_subscription' ] ); 142 143 add_action( 'pp_send_notification_status_update', [ $this, 'send_notification_status_update' ] ); 144 add_action( 'pp_send_notification_comment', [ $this, 'send_notification_comment' ] ); 145 } 146 147 /** 148 * Load the capabilities onto users the first time the module is run 149 * 150 * @since 0.7 151 */ 152 public function install() { 153 // Considering we could be moving from Edit Flow, we need to migrate the following users. 154 $this->migrateLegacyFollowingTerms(); 155 } 156 157 /** 158 * Upgrade our data in case we need to 159 * 160 * @since 0.7 161 */ 162 public function upgrade( $previous_version ) { 163 global $publishpress; 164 165 // Upgrade path to v0.7 166 if ( version_compare( $previous_version, '0.7', '<' ) ) { 167 // Migrate whether notifications were enabled or not 168 if ( $enabled = get_option( 'publishpress_notifications_enabled' ) ) { 169 $enabled = 'on'; 170 } else { 171 $enabled = 'off'; 172 } 173 $publishpress->update_module_option( $this->module->name, 'enabled', $enabled ); 174 delete_option( 'publishpress_notifications_enabled' ); 175 // Migrate whether to always notify the admin 176 // @todo: Remove after sometime. The setting always notify admin was removed. 177 if ( $always_notify_admin = get_option( 'publishpress_always_notify_admin' ) ) { 178 $always_notify_admin = 'on'; 179 } else { 180 $always_notify_admin = 'off'; 181 } 182 $publishpress->update_module_option( $this->module->name, 'always_notify_admin', $always_notify_admin ); 183 delete_option( 'publishpress_always_notify_admin' ); 184 185 // Technically we've run this code before so we don't want to auto-install new data 186 $publishpress->update_module_option( $this->module->name, 'loaded_once', true ); 187 } 188 189 if ( version_compare( $previous_version, '1.10', '<=' ) ) { 190 $this->migrateLegacyFollowingTerms(); 191 } 192 } 193 194 195 protected function migrateLegacyFollowingTerms() { 196 global $wpdb; 197 198 // Migrate Following Users 199 $query = "UPDATE {$wpdb->prefix}term_taxonomy SET taxonomy = '{$this->notify_user_taxonomy}' WHERE taxonomy = 'following_users'"; 200 $wpdb->query( $query ); 201 } 202 203 /** 204 * Register the taxonomies we use to manage relationships 205 * 206 * @since 0.7 207 * 208 * @uses register_taxonomy() 209 */ 210 public function register_taxonomies() { 211 // Load the currently supported post types so we only register against those 212 $supported_post_types = $this->get_post_types_for_module( $this->module ); 213 214 $args = [ 215 'hierarchical' => false, 216 'update_count_callback' => '_update_post_term_count', 217 'label' => false, 218 'query_var' => false, 219 'rewrite' => false, 220 'public' => false, 221 'show_ui' => false, 222 ]; 223 224 register_taxonomy( $this->notify_user_taxonomy, $supported_post_types, $args ); 225 register_taxonomy( $this->notify_role_taxonomy, $supported_post_types, $args ); 226 } 227 228 /** 229 * Enqueue necessary admin scripts 230 * 231 * @since 0.7 232 * 233 * @uses wp_enqueue_script() 234 */ 235 public function enqueue_admin_scripts() { 236 if ( $this->is_whitelisted_functional_view() ) { 237 wp_enqueue_script( 238 'publishpress-notifications-js', 239 $this->module_url . 'assets/notifications.js', 240 [ 241 'jquery', 242 ], 243 PUBLISHPRESS_VERSION, 244 true 245 ); 246 247 wp_enqueue_script( 'publishpress-chosen-js', PUBLISHPRESS_URL . '/common/libs/chosen/chosen.jquery.js', 248 [ 'jquery' ], PUBLISHPRESS_VERSION ); 249 } 250 } 251 252 /** 253 * Enqueue necessary admin styles, but only on the proper pages 254 * 255 * @since 0.7 256 * 257 * @uses wp_enqueue_style() 258 */ 259 public function enqueue_admin_styles() { 260 if ( $this->is_whitelisted_functional_view() || $this->is_whitelisted_settings_view() ) { 261 wp_enqueue_style( 'jquery-listfilterizer' ); 262 wp_enqueue_style( 263 'publishpress-notifications-css', 264 $this->module->module_url . 'assets/notifications.css', 265 false, 266 PUBLISHPRESS_VERSION 267 ); 268 269 wp_enqueue_style( 'publishpress-chosen-css', PUBLISHPRESS_URL . '/common/libs/chosen/chosen.css', false, 270 PUBLISHPRESS_VERSION ); 271 } 272 } 273 274 /** 275 * JS required for the Notify link to work 276 * 277 * @since 0.8 278 */ 279 public function action_admin_head_notify_js() { 280 ?> 295 281 <script type='text/javascript'> 296 (function ($) {297 $(document).ready(function ($) {298 /**299 * Action to Notify / Stop Notifying posts on the manage posts screen300 */301 $('.wp-list-table, #pp-calendar-view, #pp-story-budget-wrap').on('click', '.pp_notify_link a', function (e) {302 303 e.preventDefault();304 305 var link = $(this);306 307 $.ajax({308 type: 'GET',309 url: link.attr('href'),310 success: function (data) {311 if ('success' === data.status) {312 link.attr('href', data.message.link);313 link.attr('title', data.message.title);314 link.text(data.message.text);315 }316 // @todo expose the error somehow317 }318 });319 320 return false;321 });322 });323 })(jQuery);282 (function ($) { 283 $(document).ready(function ($) { 284 /** 285 * Action to Notify / Stop Notifying posts on the manage posts screen 286 */ 287 $('.wp-list-table, #pp-calendar-view, #pp-story-budget-wrap').on('click', '.pp_notify_link a', function (e) { 288 289 e.preventDefault() 290 291 var link = $(this) 292 293 $.ajax({ 294 type: 'GET', 295 url: link.attr('href'), 296 success: function (data) { 297 if ('success' === data.status) { 298 link.attr('href', data.message.link) 299 link.attr('title', data.message.title) 300 link.text(data.message.text) 301 } 302 // @todo expose the error somehow 303 } 304 }) 305 306 return false 307 }) 308 }) 309 })(jQuery) 324 310 </script> 325 <?php 326 } 327 328 /** 329 * Add a "Notify" link to supported post types Manage Posts view 330 * 331 * @since 0.8 332 * 333 * @param array $actions Any existing item actions 334 * @param int|object $post Post id or object 335 * @return array $actions The follow link has been appended 336 */ 337 public function filter_post_row_actions($actions, $post) 338 { 339 $post = get_post($post); 340 341 if (!in_array($post->post_type, $this->get_post_types_for_module($this->module))) 342 { 343 return $actions; 344 } 345 346 if (!current_user_can($this->edit_post_subscriptions_cap) || !current_user_can('edit_post', $post->ID)) 347 { 348 return $actions; 349 } 350 351 $parts = $this->get_notify_action_parts($post); 352 $actions['pp_notify_link'] = '<a title="' . esc_attr($parts['title']) . '" href="' . esc_url($parts['link']) . '">' . $parts['text'] . '</a>'; 353 354 return $actions; 355 } 356 357 /** 358 * Get an action parts for a user to set Notify or Stop Notify for a post 359 * 360 * @since 0.8 361 */ 362 private function get_notify_action_parts($post) 363 { 364 $args = array( 365 'action' => 'pp_notifications_user_post_subscription', 366 'post_id' => $post->ID, 367 ); 368 369 $user_to_notify = $this->get_users_to_notify($post->ID); 370 371 if (in_array(wp_get_current_user()->user_login, $user_to_notify)) 372 { 373 $args['method'] = 'stop_notifying'; 374 $title_text = __('Click to stop being notified on updates for this post', 'publishpress'); 375 $link_text = __('Stop notifying me', 'publishpress'); 376 } else 377 { 378 $args['method'] = 'start_notifying'; 379 $title_text = __('Click to start being notified on updates for this post', 'publishpress'); 380 $link_text = __('Notify me', 'publishpress'); 381 } 382 383 // wp_nonce_url() has encoding issues: http://core.trac.wordpress.org/ticket/20771 384 $args['_wpnonce'] = wp_create_nonce('pp_notifications_user_post_subscription'); 385 386 return array( 387 'title' => $title_text, 388 'text' => $link_text, 389 'link' => add_query_arg($args, admin_url('admin-ajax.php')), 390 ); 391 } 392 393 /** 394 * Add the subscriptions meta box to relevant post types 395 */ 396 public function add_post_meta_box() 397 { 398 if (!current_user_can($this->edit_post_subscriptions_cap)) 399 { 400 return; 401 } 402 403 $role_post_types = $this->get_post_types_for_module($this->module); 404 foreach ($role_post_types as $post_type) 405 { 406 add_meta_box( 407 'publishpress-notifications', 408 __('Notify', 'publishpress'), 409 array($this, 'notifications_meta_box'), 410 $post_type, 411 'side', 412 'high' 413 ); 414 } 415 } 416 417 /** 418 * Outputs box used to subscribe users and roles to Posts 419 * 420 * @todo add_cap to set subscribers for posts; default to Admin and editors 421 */ 422 public function notifications_meta_box() 423 { 424 global $post, $post_ID, $publishpress; 425 426 ?> 311 <?php 312 } 313 314 /** 315 * Add a "Notify" link to supported post types Manage Posts view 316 * 317 * @since 0.8 318 * 319 * @param array $actions Any existing item actions 320 * @param int|object $post Post id or object 321 * 322 * @return array $actions The follow link has been appended 323 */ 324 public function filter_post_row_actions( $actions, $post ) { 325 $post = get_post( $post ); 326 327 if ( ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) ) { 328 return $actions; 329 } 330 331 if ( ! current_user_can( $this->edit_post_subscriptions_cap ) || ! current_user_can( 'edit_post', 332 $post->ID ) ) { 333 return $actions; 334 } 335 336 $parts = $this->get_notify_action_parts( $post ); 337 $actions['pp_notify_link'] = '<a title="' . esc_attr( $parts['title'] ) . '" href="' . esc_url( $parts['link'] ) . '">' . $parts['text'] . '</a>'; 338 339 return $actions; 340 } 341 342 /** 343 * Get an action parts for a user to set Notify or Stop Notify for a post 344 * 345 * @since 0.8 346 */ 347 private function get_notify_action_parts( $post ) { 348 $args = [ 349 'action' => 'pp_notifications_user_post_subscription', 350 'post_id' => $post->ID, 351 ]; 352 353 $user_to_notify = $this->get_users_to_notify( $post->ID ); 354 355 if ( in_array( wp_get_current_user()->user_login, $user_to_notify ) ) { 356 $args['method'] = 'stop_notifying'; 357 $title_text = __( 'Click to stop being notified on updates for this post', 'publishpress' ); 358 $link_text = __( 'Stop notifying me', 'publishpress' ); 359 } else { 360 $args['method'] = 'start_notifying'; 361 $title_text = __( 'Click to start being notified on updates for this post', 'publishpress' ); 362 $link_text = __( 'Notify me', 'publishpress' ); 363 } 364 365 // wp_nonce_url() has encoding issues: http://core.trac.wordpress.org/ticket/20771 366 $args['_wpnonce'] = wp_create_nonce( 'pp_notifications_user_post_subscription' ); 367 368 return [ 369 'title' => $title_text, 370 'text' => $link_text, 371 'link' => add_query_arg( $args, admin_url( 'admin-ajax.php' ) ), 372 ]; 373 } 374 375 /** 376 * Add the subscriptions meta box to relevant post types 377 */ 378 public function add_post_meta_box() { 379 if ( ! current_user_can( $this->edit_post_subscriptions_cap ) ) { 380 return; 381 } 382 383 $role_post_types = $this->get_post_types_for_module( $this->module ); 384 foreach ( $role_post_types as $post_type ) { 385 add_meta_box( 386 'publishpress-notifications', 387 __( 'Notify', 'publishpress' ), 388 [ $this, 'notifications_meta_box' ], 389 $post_type, 390 'side', 391 'high' 392 ); 393 } 394 } 395 396 /** 397 * Outputs box used to subscribe users and roles to Posts 398 * 399 * @todo add_cap to set subscribers for posts; default to Admin and editors 400 */ 401 public function notifications_meta_box() { 402 global $post, $post_ID, $publishpress; 403 404 ?> 427 405 <div id="pp_post_notify_box"> 428 406 <a name="subscriptions"></a> 429 407 430 408 <p> 431 <?php _e('Select the users and roles that should receive notifications from workflows.', 'publishpress'); ?> 409 <?php _e( 'Select the users and roles that should receive notifications from workflows.', 410 'publishpress' ); ?> 432 411 </p> 433 412 … … 439 418 $selected = array_merge($users_to_notify, $roles_to_notify); 440 419 441 $select_form_args = array(420 $select_form_args = [ 442 421 'list_class' => 'pp_post_notify_list', 443 );422 ]; 444 423 $this->users_select_form($selected , $select_form_args); 445 424 ?> … … 449 428 450 429 <p> 451 <a href="https://publishpress.com/docs/notifications/"><?php _e('Click to read more about notifications', 'publishpress'); ?></a> 430 <a href="https://publishpress.com/docs/notifications/"><?php _e( 'Click to read more about notifications', 431 'publishpress' ); ?></a> 452 432 </p> 453 433 … … 455 435 456 436 <input type="hidden" name="pp_save_notify" value="1"/> <?php // Extra protection against autosaves 457 ?>458 459 <?php wp_nonce_field('save_roles', 'pp_notifications_nonce', false); ?>437 ?> 438 439 <?php wp_nonce_field( 'save_roles', 'pp_notifications_nonce', false ); ?> 460 440 </div> 461 441 462 <?php 463 } 464 465 public function action_save_post($postId) 466 { 467 if (!isset($_POST['pp_notifications_nonce']) || !wp_verify_nonce($_POST['pp_notifications_nonce'], 'save_roles')) { 468 return; 469 } 470 471 if (isset($_POST['to_notify'])) { 472 // Remove current users 473 $terms = get_the_terms($postId, $this->notify_user_taxonomy); 474 $users = array(); 475 if (!empty($terms)) { 476 foreach ($terms as $term) { 477 $users[] = $term->term_id; 478 } 479 } 480 wp_remove_object_terms($postId, $users, $this->notify_user_taxonomy); 481 482 // Remove current roles 483 $terms = get_the_terms($postId, $this->notify_role_taxonomy); 484 $roles = array(); 485 if (!empty($terms)) { 486 foreach ($terms as $term) { 487 $roles[] = $term->term_id; 488 } 489 } 490 wp_remove_object_terms($postId, $roles, $this->notify_role_taxonomy); 491 492 foreach ($_POST['to_notify'] as $id) { 493 if (is_numeric($id)) { 494 // User id 495 $this->post_set_users_to_notify($postId, (int)$id, true); 496 } else { 497 // Role name 498 $this->post_set_roles_to_notify($postId, $id, true); 499 } 500 } 501 } 502 } 503 504 /** 505 * Handle a request to update a user's post subscription 506 * 507 * @since 0.8 508 */ 509 public function handle_user_post_subscription() 510 { 511 if (!wp_verify_nonce($_GET['_wpnonce'], 'pp_notifications_user_post_subscription')) 512 { 513 $this->print_ajax_response('error', $this->module->messages['nonce-failed']); 514 } 515 516 if (!current_user_can($this->edit_post_subscriptions_cap)) 517 { 518 $this->print_ajax_response('error', $this->module->messages['invalid-permissions']); 519 } 520 521 $post = get_post(($post_id = $_GET['post_id'])); 522 523 if (!$post) 524 { 525 $this->print_ajax_response('error', $this->module->messages['missing-post']); 526 } 527 528 if ('start_notifying' === $_GET['method']) 529 { 530 $retval = $this->post_set_users_to_notify($post, get_current_user_id()); 531 } else 532 { 533 $retval = $this->post_set_users_stop_notify($post, get_current_user_id()); 534 } 535 536 if (is_wp_error($retval)) 537 { 538 $this->print_ajax_response('error', $retval->get_error_message()); 539 } 540 541 $this->print_ajax_response('success', (object )$this->get_notify_action_parts($post)); 542 } 543 544 /** 545 * @param $default 546 * @param $context 547 * @return bool 548 */ 549 public function filter_pp_notification_auto_subscribe_post_author($default, $context) 550 { 551 if (!isset($this->module->options->notify_author_by_default)) 552 { 553 return $default; 554 } 555 556 return (booL)$this->module->options->notify_author_by_default; 557 } 558 559 /** 560 * @param $default 561 * @param $context 562 * @return bool 563 */ 564 public function filter_pp_notification_auto_subscribe_current_user($default, $context) 565 { 566 if (!isset($this->module->options->notify_current_user_by_default)) 567 { 568 return $default; 569 } 570 571 return (booL)$this->module->options->notify_current_user_by_default; 572 } 573 574 /** 575 * Sets users to be notified for the specified post 576 * 577 * @param int $post ID of the post 578 */ 579 public function save_post_notify_users($post, $users = null) 580 { 581 if (!is_array($users)) 582 { 583 $users = array(); 584 } 585 586 // Add current user to notify list 587 $user = wp_get_current_user(); 588 if ($user && apply_filters('pp_notification_auto_subscribe_current_user', true, 'subscription_action')) 589 { 590 $users[] = $user->ID; 591 } 592 593 // Add post author to notify list 594 if (apply_filters('pp_notification_auto_subscribe_post_author', true, 'subscription_action')) 595 { 596 $users[] = $post->post_author; 597 } 598 599 $users = array_unique(array_map('intval', $users)); 600 601 $this->post_set_users_to_notify($post, $users, false); 602 } 603 604 /** 605 * Sets roles to be notified for the specified post 606 * 607 * @param int $post ID of the post 608 * @param array $roles Roles to be notified for posts 609 */ 610 public function save_post_notify_roles($post, $roles = null) 611 { 612 if (!is_array($roles)) 613 { 614 $roles = array(); 615 } 616 $roles = array_map('intval', $roles); 617 618 $this->add_role_to_notify($post, $roles, false); 619 } 620 621 /** 622 * Set up and send post status change a notification 623 */ 624 public function notification_status_change($new_status, $old_status, $post) 625 { 626 global $publishpress; 627 628 629 // Kill switch for notification 630 if (!apply_filters('pp_notification_status_change', $new_status, $old_status, $post) || !apply_filters("pp_notification_{$post->post_type}_status_change", $new_status, $old_status, $post)) 631 { 632 return false; 633 } 634 635 $supported_post_types = $this->get_post_types_for_module($this->module); 636 if (!in_array($post->post_type, $supported_post_types)) 637 { 638 return; 639 } 640 641 // No need to notify if it's a revision, auto-draft, or if post status wasn't changed 642 $ignored_statuses = apply_filters('pp_notification_ignored_statuses', array($old_status, 'inherit', 'auto-draft'), $post->post_type); 643 644 if (!in_array($new_status, $ignored_statuses)) 645 { 646 $args = array( 647 'new_status' => $new_status, 648 'old_status' => $old_status, 649 'post' => $post, 650 ); 651 652 do_action('pp_send_notification_status_update', $args); 653 } 654 } 655 656 /** 657 * Set up and set editorial comment notification email 658 */ 659 public function notification_comment($comment) 660 { 661 $post = get_post($comment->comment_post_ID); 662 663 $supported_post_types = $this->get_post_types_for_module($this->module); 664 if (!in_array($post->post_type, $supported_post_types)) 665 { 666 return; 667 } 668 669 // Kill switch for notification 670 if (!apply_filters('pp_notification_editorial_comment', $comment, $post)) 671 { 672 return false; 673 } 674 675 $user = get_userdata($post->post_author); 676 $current_user = wp_get_current_user(); 677 678 $post_id = $post->ID; 679 $post_type = get_post_type_object($post->post_type)->labels->singular_name; 680 $post_title = pp_draft_or_post_title($post_id); 681 682 // Check if this a reply 683 //$parent_ID = isset( $comment->comment_parent_ID ) ? $comment->comment_parent_ID : 0; 684 //if( $parent_ID ) $parent = get_comment( $parent_ID ); 685 686 // Set user to be notified for a post, but make it filterable 687 if (apply_filters('pp_notification_auto_subscribe_current_user', true, 'comment')) 688 { 689 $this->post_set_users_to_notify($post, (int )$current_user->ID); 690 } 691 692 // Set the post author to be notified for the post but make it filterable 693 if (apply_filters('pp_notification_auto_subscribe_post_author', true, 'comment')) 694 { 695 $this->post_set_users_to_notify($post, (int )$post->post_author); 696 } 697 698 $blogname = get_option('blogname'); 699 700 // Send the notification 701 $args = array( 702 'blogname' => $blogname, 703 'post' => $post, 704 'post_title' => $post_title, 705 'post_id' => $post_id, 706 'post_type' => $post_type, 707 'current_user' => $current_user, 708 'comment' => $comment, 709 ); 710 711 do_action('pp_send_notification_comment', $args); 712 } 713 714 public function get_notification_footer($post) 715 { 716 $body = ""; 717 $body .= "\r\n--------------------\r\n"; 718 $body .= sprintf(__('You are receiving this email because you are subscribed to "%s".', 'publishpress'), pp_draft_or_post_title($post->ID)); 719 $body .= "\r\n"; 720 $body .= sprintf(__('This email was sent %s.', 'publishpress'), date('r')); 721 $body .= "\r\n \r\n"; 722 $body .= get_option('blogname') . " | " . get_bloginfo('url') . " | " . admin_url('/') . "\r\n"; 723 724 return $body; 725 } 726 727 /** 728 * send_email() 729 */ 730 public function send_email($action, $post, $subject, $message, $message_headers = '', $recipients = null) 731 { 732 if (is_null($recipients)) 733 { 734 // Get list of email recipients -- set them CC 735 $recipients = $this->_get_notification_recipients($post, true); 736 } 737 738 if ($recipients && !is_array($recipients)) 739 { 740 $recipients = explode(',', $recipients); 741 } 742 743 $subject = apply_filters('pp_notification_send_email_subject', $subject, $action, $post); 744 $message = apply_filters('pp_notification_send_email_message', $message, $action, $post); 745 $message_headers = apply_filters('pp_notification_send_email_message_headers', $message_headers, $action, $post); 746 747 if (PP_NOTIFICATION_USE_CRON) 748 { 749 $this->schedule_emails($recipients, $subject, $message, $message_headers); 750 } else if (!empty($recipients)) 751 { 752 foreach ($recipients as $recipient) 753 { 754 $this->send_single_email($recipient, $subject, $message, $message_headers); 755 } 756 } 757 } 758 759 /** 760 * Schedules emails to be sent in succession 761 * 762 * @param mixed $recipients Individual email or array of emails 763 * @param string $subject Subject of the email 764 * @param string $message Body of the email 765 * @param string $message_headers . (optional ) Message headers 766 * @param int $time_offset (optional ) Delay in seconds per email 767 */ 768 public function schedule_emails($recipients, $subject, $message, $message_headers = '', $time_offset = 1) 769 { 770 $recipients = (array)$recipients; 771 772 $send_time = time(); 773 774 foreach ($recipients as $recipient) 775 { 776 wp_schedule_single_event($send_time, 'pp_send_scheduled_notification', array($recipient, $subject, $message, $message_headers)); 777 $send_time += $time_offset; 778 } 779 } 780 781 /** 782 * Sends an individual email 783 * 784 * @param mixed $to Email to send to 785 * @param string $subject Subject of the email 786 * @param string $message Body of the email 787 * @param string $message_headers . (optional ) Message headers 788 */ 789 public function send_single_email($to, $subject, $message, $message_headers = '') 790 { 791 wp_mail($to, $subject, $message, $message_headers); 792 } 793 794 /** 795 * Returns a list of recipients for a given post 796 * 797 * @param $post object 798 * @param $string bool Whether to return recipients as comma-delimited string or array 799 * @return string or array of recipients to receive notification 800 */ 801 private function _get_notification_recipients($post, $string = false) 802 { 803 global $publishpress; 804 805 $post_id = $post->ID; 806 if (!$post_id) 807 { 808 return; 809 } 810 811 $authors = array(); 812 $admins = array(); 813 $recipients = array(); 814 815 $role_users = array(); 816 817 // Get users and roles to notify 818 $roles = $this->get_roles_to_notify($post_id, 'slugs'); 819 foreach ((array )$roles as $role_id) 820 { 821 $users = get_users( 822 [ 823 'role' => $role_id, 824 ] 825 ); 826 827 if (!empty($users)) { 828 foreach ($users as $user) 829 { 830 if (is_user_member_of_blog($user->ID)) 831 { 832 $role_users[] = $user->user_email; 833 } 834 } 835 } 836 } 837 838 $users = $this->get_users_to_notify($post_id, 'user_email'); 839 840 // Merge arrays and filter any duplicates 841 $recipients = array_merge($authors, $admins, $users, $role_users); 842 $recipients = array_unique($recipients); 843 844 // Process the recipients for this email to be sent 845 foreach ($recipients as $key => $user_email) 846 { 847 // Get rid of empty email entries 848 if (empty($recipients[$key])) 849 { 850 unset($recipients[$key]); 851 } 852 // Don't send the email to the current user unless we've explicitly indicated they should receive it 853 if (false === apply_filters('publishpress_notify_current_user', false) && wp_get_current_user()->user_email == $user_email) 854 { 855 unset($recipients[$key]); 856 } 857 } 858 859 // Filter to allow further modification of recipients 860 $recipients = apply_filters('pp_notification_recipients', $recipients, $post, $string); 861 862 // If string set to true, return comma-delimited 863 if ($string && is_array($recipients)) 864 { 865 return implode(',', $recipients); 866 } else 867 { 868 return $recipients; 869 } 870 } 871 872 /** 873 * Set a user or users to be notified for a post 874 * 875 * @param int|object $post Post object or ID 876 * @param string|array $users User or users to subscribe to post updates 877 * @param bool $append Whether users should be added to pp_notify_user list or replace existing list 878 * 879 * @return true|WP_Error $response True on success, WP_Error on failure 880 */ 881 public function post_set_users_to_notify($post, $users, $append = true) 882 { 883 $post = get_post($post); 884 if (!$post) 885 { 886 return new WP_Error('missing-post', $this->module->messages['missing-post']); 887 } 888 889 if (!is_array($users)) 890 { 891 $users = array($users); 892 } 893 894 $user_terms = array(); 895 896 foreach ($users as $user) 897 { 898 if (is_int($user)) 899 { 900 $user = get_user_by('id', $user); 901 } else if (is_string($user)) 902 { 903 $user = get_user_by('login', $user); 904 } 905 906 if (!is_object($user)) 907 { 908 continue; 909 } 910 911 $name = $user->user_login; 912 913 // Add user as a term if they don't exist 914 $term = $this->add_term_if_not_exists($name, $this->notify_user_taxonomy); 915 916 if (!is_wp_error($term)) 917 { 918 $user_terms[] = $name; 919 } 920 } 921 922 $set = wp_set_object_terms($post->ID, $user_terms, $this->notify_user_taxonomy, $append); 923 924 if (is_wp_error($set)) 925 { 926 return $set; 927 } else 928 { 929 return true; 930 } 931 } 932 933 /** 934 * Set a role or roles to be notified for a post 935 * 936 * @param int|object $post Post object or ID 937 * @param string|array $roles Role or roles to subscribe to post updates 938 * @param bool $append Whether roles should be added to pp_notify_role list or replace existing list 939 * 940 * @return true|WP_Error $response True on success, WP_Error on failure 941 */ 942 public function post_set_roles_to_notify($post, $roles, $append = true) 943 { 944 $post = get_post($post); 945 if (!$post) 946 { 947 return new WP_Error('missing-post', $this->module->messages['missing-post']); 948 } 949 950 if (!is_array($roles)) 951 { 952 $roles = array($roles); 953 } 954 955 $role_terms = array(); 956 957 foreach ($roles as $role) 958 { 959 $role = get_role($role); 960 961 if (!is_object($role)) 962 { 963 continue; 964 } 965 966 // Add user as a term if they don't exist 967 $term = $this->add_term_if_not_exists($role->name, $this->notify_role_taxonomy); 968 969 if (!is_wp_error($term)) 970 { 971 $role_terms[] = $role->name; 972 } 973 } 974 975 $set = wp_set_object_terms($post->ID, $role_terms, $this->notify_role_taxonomy, $append); 976 977 if (is_wp_error($set)) 978 { 979 return $set; 980 } else 981 { 982 return true; 983 } 984 } 985 986 /** 987 * Removes user from pp_notify_user taxonomy for the given Post, 988 * so they no longer receive future notifications. 989 * 990 * @param object $post Post object or ID 991 * @param int|string|array $users One or more users to stop being notified for the post 992 * @return true|WP_Error $response True on success, WP_Error on failure 993 */ 994 public function post_set_users_stop_notify($post, $users) 995 { 996 $post = get_post($post); 997 if (!$post) 998 { 999 return new WP_Error('missing-post', $this->module->messages['missing-post']); 1000 } 1001 1002 if (!is_array($users)) 1003 { 1004 $users = array($users); 1005 } 1006 1007 $terms = get_the_terms($post->ID, $this->notify_user_taxonomy); 1008 if (is_wp_error($terms)) 1009 { 1010 return $terms; 1011 } 1012 1013 $user_terms = wp_list_pluck($terms, 'slug'); 1014 foreach ($users as $user) 1015 { 1016 if (is_int($user)) 1017 { 1018 $user = get_user_by('id', $user); 1019 } else if (is_string($user)) 1020 { 1021 $user = get_user_by('login', $user); 1022 } 1023 1024 if (!is_object($user)) 1025 { 1026 continue; 1027 } 1028 1029 $key = array_search($user->user_login, $user_terms); 1030 if (false !== $key) 1031 { 1032 unset($user_terms[$key]); 1033 } 1034 } 1035 $set = wp_set_object_terms($post->ID, $user_terms, $this->notify_user_taxonomy, false); 1036 1037 if (is_wp_error($set)) 1038 { 1039 return $set; 1040 } else 1041 { 1042 return true; 1043 } 1044 } 1045 1046 /** 1047 * add_role_to_notify() 1048 * 1049 */ 1050 public function add_role_to_notify($post, $roles = 0, $append = true) 1051 { 1052 $post_id = (is_int($post)) ? $post : $post->ID; 1053 if (!is_array($roles)) 1054 { 1055 $roles = array($roles); 1056 } 1057 1058 // make sure each role id is an integer and not a number stored as a string 1059 foreach ($roles as $key => $role) 1060 { 1061 $roles[$key] = intval($role); 1062 } 1063 1064 wp_set_object_terms($post_id, $roles, $this->notify_role_taxonomy, $append); 1065 1066 return; 1067 } 1068 1069 /** 1070 * Removes users that are deleted from receiving future notifications (i.e. makes them out of notify list for posts FOREVER! ) 1071 * 1072 * @param $id int ID of the user 1073 */ 1074 public function delete_user_action($id) 1075 { 1076 if (!$id) 1077 { 1078 return; 1079 } 1080 1081 // get user data 1082 $user = get_userdata($id); 1083 1084 if ($user) 1085 { 1086 // Delete term from the pp_notify_user taxonomy 1087 $notify_user_term = get_term_by('name', $user->user_login, $this->notify_user_taxonomy); 1088 if ($notify_user_term) 1089 { 1090 wp_delete_term($notify_user_term->term_id, $this->notify_user_taxonomy); 1091 } 1092 } 1093 1094 return; 1095 } 1096 1097 /** 1098 * Add user as a term if they aren't already 1099 * 1100 * @param $term string term to be added 1101 * @param $taxonomy string taxonomy to add term to 1102 * @return WP_error if insert fails, true otherwise 1103 */ 1104 public function add_term_if_not_exists($term, $taxonomy) 1105 { 1106 if (!term_exists($term, $taxonomy)) 1107 { 1108 $args = array('slug' => sanitize_title($term)); 1109 1110 return wp_insert_term($term, $taxonomy, $args); 1111 } 1112 1113 return true; 1114 } 1115 1116 /** 1117 * Gets a list of the users to be notified for the specified post 1118 * 1119 * @param int $post_id The ID of the post 1120 * @param string $return The field to return 1121 * @return array $users Users to notify for the specified posts 1122 */ 1123 public function get_users_to_notify($post_id, $return = 'user_login') 1124 { 1125 // Get pp_notify_user terms for the post 1126 $users = wp_get_object_terms($post_id, $this->notify_user_taxonomy, array('fields' => 'names')); 1127 1128 // Don't have any users to notify 1129 if (!$users || is_wp_error($users)) 1130 { 1131 return array(); 1132 } 1133 1134 // if just want user_login, return as is 1135 if ($return == 'user_login') 1136 { 1137 return $users; 1138 } 1139 1140 foreach ((array )$users as $key => $user) 1141 { 1142 switch ($user) 1143 { 1144 case is_int($user): 1145 $search = 'id'; 1146 break; 1147 case is_email($user): 1148 $search = 'email'; 1149 break; 1150 default: 1151 $search = 'login'; 1152 break; 1153 } 1154 $new_user = get_user_by($search, $user); 1155 if (!$new_user || !is_user_member_of_blog($new_user->ID)) 1156 { 1157 unset($users[$key]); 1158 continue; 1159 } 1160 switch ($return) 1161 { 1162 case 'user_login': 1163 $users[$key] = $new_user->user_login; 1164 break; 1165 case 'id': 1166 $users[$key] = $new_user->ID; 1167 break; 1168 case 'user_email': 1169 $users[$key] = $new_user->user_email; 1170 break; 1171 case 'object': 1172 $users[$key] = $new_user; 1173 break; 1174 } 1175 } 1176 if (!$users || is_wp_error($users)) 1177 { 1178 $users = array(); 1179 } 1180 1181 return $users; 1182 } 1183 1184 /** 1185 * Gets a list of the roles that should be notified for the specified post 1186 * 1187 * @param int $post_id 1188 * @return array $roles All of the role slugs 1189 */ 1190 public function get_roles_to_notify($post_id, $return = 'all') 1191 { 1192 global $publishpress; 1193 1194 // Workaround for the fact that get_object_terms doesn't return just slugs 1195 if ($return == 'slugs') 1196 { 1197 $fields = 'all'; 1198 } else 1199 { 1200 $fields = $return; 1201 } 1202 1203 $roles = wp_get_object_terms($post_id, $this->notify_role_taxonomy, array('fields' => $fields)); 1204 1205 if ($return == 'slugs') 1206 { 1207 $slugs = array(); 1208 foreach ($roles as $role) 1209 { 1210 $slugs[] = $role->slug; 1211 } 1212 $roles = $slugs; 1213 } 1214 1215 return $roles; 1216 } 1217 1218 /** 1219 * Gets a list of posts that a user is selected to be notified 1220 * 1221 * @param string|int $user user_login or id of user 1222 * @param array $args 1223 * @return array $posts Posts a user is selected to be notified 1224 */ 1225 public function get_user_to_notify_posts($user = 0, $args = null) 1226 { 1227 if (!$user) 1228 { 1229 $user = (int )wp_get_current_user()->ID; 1230 } 1231 1232 if (is_int($user)) 1233 { 1234 $user = get_userdata($user)->user_login; 1235 } 1236 1237 $post_args = array( 1238 'tax_query' => array( 1239 array( 1240 'taxonomy' => $this->notify_user_taxonomy, 1241 'field' => 'slug', 1242 'terms' => $user, 1243 ), 1244 ), 1245 'posts_per_page' => '10', 1246 'orderby' => 'modified', 1247 'order' => 'DESC', 1248 'post_status' => 'any', 1249 ); 1250 $post_args = apply_filters('pp_user_to_notify_posts_query_args', $post_args); 1251 $posts = get_posts($post_args); 1252 1253 return $posts; 1254 } 1255 1256 /** 1257 * Register settings for notifications so we can partially use the Settings API 1258 * (We use the Settings API for form generation, but not saving ) 1259 * 1260 * @since 0.7 1261 */ 1262 public function register_settings() 1263 { 1264 add_settings_section($this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name); 1265 add_settings_field( 1266 'post_types', 1267 __('Show the "Notify me" and "Stop notifying me" links for these post types:', 'publishpress'), 1268 array($this, 'settings_post_types_option'), 1269 $this->module->options_group_name, 1270 $this->module->options_group_name . '_general' 1271 ); 1272 1273 add_settings_field( 1274 'email_from', 1275 __('Email from:', 'publishpress'), 1276 array($this, 'settings_email_from_option'), 1277 $this->module->options_group_name, 1278 $this->module->options_group_name . '_general' 1279 ); 1280 1281 add_settings_field( 1282 'notify_author_by_default', 1283 __('Always notify the author of the content:', 'publishpress'), 1284 array($this, 'settings_notify_author_by_default_option'), 1285 $this->module->options_group_name, 1286 $this->module->options_group_name . '_general' 1287 ); 1288 1289 add_settings_field( 1290 'notify_current_user_by_default', 1291 __('Always notify users who have edited the content:', 'publishpress'), 1292 array($this, 'settings_notify_current_user_by_default_option'), 1293 $this->module->options_group_name, 1294 $this->module->options_group_name . '_general' 1295 ); 1296 } 1297 1298 /** 1299 * Chose the post types for notifications 1300 * 1301 * @since 0.7 1302 */ 1303 public function settings_post_types_option() 1304 { 1305 global $publishpress; 1306 $publishpress->settings->helper_option_custom_post_type($this->module); 1307 } 1308 1309 public function get_email_from() 1310 { 1311 if (!isset($this->module->options->email_from_name)) 1312 { 1313 $name = get_bloginfo('name'); 1314 } else 1315 { 1316 $name = $this->module->options->email_from_name; 1317 } 1318 1319 if (!isset($this->module->options->email_from)) 1320 { 1321 $email = get_bloginfo('admin_email'); 1322 } else 1323 { 1324 $email = $this->module->options->email_from; 1325 } 1326 1327 return array( 1328 'name' => $name, 1329 'email' => $email, 1330 ); 1331 } 1332 1333 /** 1334 * Field to customize the email for the "email from". 1335 */ 1336 public function settings_email_from_option() 1337 { 1338 $email_from = $this->get_email_from(); 1339 1340 echo '<input 442 <?php 443 } 444 445 public function action_save_post( $postId ) { 446 if ( ! isset( $_POST['pp_notifications_nonce'] ) || ! wp_verify_nonce( $_POST['pp_notifications_nonce'], 447 'save_roles' ) ) { 448 return; 449 } 450 451 if ( isset( $_POST['to_notify'] ) ) { 452 // Remove current users 453 $terms = get_the_terms( $postId, $this->notify_user_taxonomy ); 454 $users = []; 455 if ( ! empty( $terms ) ) { 456 foreach ( $terms as $term ) { 457 $users[] = $term->term_id; 458 } 459 } 460 wp_remove_object_terms( $postId, $users, $this->notify_user_taxonomy ); 461 462 // Remove current roles 463 $terms = get_the_terms( $postId, $this->notify_role_taxonomy ); 464 $roles = []; 465 if ( ! empty( $terms ) ) { 466 foreach ( $terms as $term ) { 467 $roles[] = $term->term_id; 468 } 469 } 470 wp_remove_object_terms( $postId, $roles, $this->notify_role_taxonomy ); 471 472 foreach ( $_POST['to_notify'] as $id ) { 473 if ( is_numeric( $id ) ) { 474 // User id 475 $this->post_set_users_to_notify( $postId, (int) $id, true ); 476 } else { 477 // Role name 478 $this->post_set_roles_to_notify( $postId, $id, true ); 479 } 480 } 481 } 482 } 483 484 /** 485 * Handle a request to update a user's post subscription 486 * 487 * @since 0.8 488 */ 489 public function handle_user_post_subscription() { 490 if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'pp_notifications_user_post_subscription' ) ) { 491 $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); 492 } 493 494 if ( ! current_user_can( $this->edit_post_subscriptions_cap ) ) { 495 $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); 496 } 497 498 $post = get_post( ( $post_id = $_GET['post_id'] ) ); 499 500 if ( ! $post ) { 501 $this->print_ajax_response( 'error', $this->module->messages['missing-post'] ); 502 } 503 504 if ( 'start_notifying' === $_GET['method'] ) { 505 $retval = $this->post_set_users_to_notify( $post, get_current_user_id() ); 506 } else { 507 $retval = $this->post_set_users_stop_notify( $post, get_current_user_id() ); 508 } 509 510 if ( is_wp_error( $retval ) ) { 511 $this->print_ajax_response( 'error', $retval->get_error_message() ); 512 } 513 514 $this->print_ajax_response( 'success', (object ) $this->get_notify_action_parts( $post ) ); 515 } 516 517 /** 518 * @param $default 519 * @param $context 520 * 521 * @return bool 522 */ 523 public function filter_pp_notification_auto_subscribe_post_author( $default, $context ) { 524 if ( ! isset( $this->module->options->notify_author_by_default ) ) { 525 return $default; 526 } 527 528 return (booL) $this->module->options->notify_author_by_default; 529 } 530 531 /** 532 * @param $default 533 * @param $context 534 * 535 * @return bool 536 */ 537 public function filter_pp_notification_auto_subscribe_current_user( $default, $context ) { 538 if ( ! isset( $this->module->options->notify_current_user_by_default ) ) { 539 return $default; 540 } 541 542 return (booL) $this->module->options->notify_current_user_by_default; 543 } 544 545 /** 546 * Sets users to be notified for the specified post 547 * 548 * @param int $post ID of the post 549 */ 550 public function save_post_notify_users( $post, $users = null ) { 551 if ( ! is_array( $users ) ) { 552 $users = []; 553 } 554 555 // Add current user to notify list 556 $user = wp_get_current_user(); 557 if ( $user && apply_filters( 'pp_notification_auto_subscribe_current_user', true, 558 'subscription_action' ) ) { 559 $users[] = $user->ID; 560 } 561 562 // Add post author to notify list 563 if ( apply_filters( 'pp_notification_auto_subscribe_post_author', true, 'subscription_action' ) ) { 564 $users[] = $post->post_author; 565 } 566 567 $users = array_unique( array_map( 'intval', $users ) ); 568 569 $this->post_set_users_to_notify( $post, $users, false ); 570 } 571 572 /** 573 * Sets roles to be notified for the specified post 574 * 575 * @param int $post ID of the post 576 * @param array $roles Roles to be notified for posts 577 */ 578 public function save_post_notify_roles( $post, $roles = null ) { 579 if ( ! is_array( $roles ) ) { 580 $roles = []; 581 } 582 $roles = array_map( 'intval', $roles ); 583 584 $this->add_role_to_notify( $post, $roles, false ); 585 } 586 587 /** 588 * Set up and send post status change a notification 589 */ 590 public function notification_status_change( $new_status, $old_status, $post ) { 591 global $publishpress; 592 593 594 // Kill switch for notification 595 if ( ! apply_filters( 'pp_notification_status_change', $new_status, $old_status, 596 $post ) || ! apply_filters( "pp_notification_{$post->post_type}_status_change", $new_status, 597 $old_status, $post ) ) { 598 return false; 599 } 600 601 $supported_post_types = $this->get_post_types_for_module( $this->module ); 602 if ( ! in_array( $post->post_type, $supported_post_types ) ) { 603 return; 604 } 605 606 // No need to notify if it's a revision, auto-draft, or if post status wasn't changed 607 $ignored_statuses = apply_filters( 'pp_notification_ignored_statuses', 608 [ $old_status, 'inherit', 'auto-draft' ], $post->post_type ); 609 610 if ( ! in_array( $new_status, $ignored_statuses ) ) { 611 $args = [ 612 'new_status' => $new_status, 613 'old_status' => $old_status, 614 'post' => $post, 615 ]; 616 617 do_action( 'pp_send_notification_status_update', $args ); 618 } 619 } 620 621 /** 622 * Set up and set editorial comment notification email 623 */ 624 public function notification_comment( $comment ) { 625 $post = get_post( $comment->comment_post_ID ); 626 627 $supported_post_types = $this->get_post_types_for_module( $this->module ); 628 if ( ! in_array( $post->post_type, $supported_post_types ) ) { 629 return; 630 } 631 632 // Kill switch for notification 633 if ( ! apply_filters( 'pp_notification_editorial_comment', $comment, $post ) ) { 634 return false; 635 } 636 637 $user = get_userdata( $post->post_author ); 638 $current_user = wp_get_current_user(); 639 640 $post_id = $post->ID; 641 $post_type = get_post_type_object( $post->post_type )->labels->singular_name; 642 $post_title = pp_draft_or_post_title( $post_id ); 643 644 // Check if this a reply 645 //$parent_ID = isset( $comment->comment_parent_ID ) ? $comment->comment_parent_ID : 0; 646 //if( $parent_ID ) $parent = get_comment( $parent_ID ); 647 648 // Set user to be notified for a post, but make it filterable 649 if ( apply_filters( 'pp_notification_auto_subscribe_current_user', true, 'comment' ) ) { 650 $this->post_set_users_to_notify( $post, (int ) $current_user->ID ); 651 } 652 653 // Set the post author to be notified for the post but make it filterable 654 if ( apply_filters( 'pp_notification_auto_subscribe_post_author', true, 'comment' ) ) { 655 $this->post_set_users_to_notify( $post, (int ) $post->post_author ); 656 } 657 658 $blogname = get_option( 'blogname' ); 659 660 // Send the notification 661 $args = [ 662 'blogname' => $blogname, 663 'post' => $post, 664 'post_title' => $post_title, 665 'post_id' => $post_id, 666 'post_type' => $post_type, 667 'current_user' => $current_user, 668 'comment' => $comment, 669 ]; 670 671 do_action( 'pp_send_notification_comment', $args ); 672 } 673 674 public function get_notification_footer( $post ) { 675 $body = ""; 676 $body .= "\r\n--------------------\r\n"; 677 $body .= sprintf( __( 'You are receiving this email because you are subscribed to "%s".', 'publishpress' ), 678 pp_draft_or_post_title( $post->ID ) ); 679 $body .= "\r\n"; 680 $body .= sprintf( __( 'This email was sent %s.', 'publishpress' ), date( 'r' ) ); 681 $body .= "\r\n \r\n"; 682 $body .= get_option( 'blogname' ) . " | " . get_bloginfo( 'url' ) . " | " . admin_url( '/' ) . "\r\n"; 683 684 return $body; 685 } 686 687 /** 688 * send_email() 689 */ 690 public function send_email( $action, $post, $subject, $message, $message_headers = '', $recipients = null ) { 691 if ( is_null( $recipients ) ) { 692 // Get list of email recipients -- set them CC 693 $recipients = $this->_get_notification_recipients( $post, true ); 694 } 695 696 if ( $recipients && ! is_array( $recipients ) ) { 697 $recipients = explode( ',', $recipients ); 698 } 699 700 $subject = apply_filters( 'pp_notification_send_email_subject', $subject, $action, $post ); 701 $message = apply_filters( 'pp_notification_send_email_message', $message, $action, $post ); 702 $message_headers = apply_filters( 'pp_notification_send_email_message_headers', $message_headers, $action, 703 $post ); 704 705 if ( PP_NOTIFICATION_USE_CRON ) { 706 $this->schedule_emails( $recipients, $subject, $message, $message_headers ); 707 } elseif ( ! empty( $recipients ) ) { 708 foreach ( $recipients as $recipient ) { 709 $this->send_single_email( $recipient, $subject, $message, $message_headers ); 710 } 711 } 712 } 713 714 /** 715 * Schedules emails to be sent in succession 716 * 717 * @param mixed $recipients Individual email or array of emails 718 * @param string $subject Subject of the email 719 * @param string $message Body of the email 720 * @param string $message_headers . (optional ) Message headers 721 * @param int $time_offset (optional ) Delay in seconds per email 722 */ 723 public function schedule_emails( $recipients, $subject, $message, $message_headers = '', $time_offset = 1 ) { 724 $recipients = (array) $recipients; 725 726 $send_time = time(); 727 728 foreach ( $recipients as $recipient ) { 729 wp_schedule_single_event( $send_time, 'pp_send_scheduled_notification', 730 [ $recipient, $subject, $message, $message_headers ] ); 731 $send_time += $time_offset; 732 } 733 } 734 735 /** 736 * Sends an individual email 737 * 738 * @param mixed $to Email to send to 739 * @param string $subject Subject of the email 740 * @param string $message Body of the email 741 * @param string $message_headers . (optional ) Message headers 742 */ 743 public function send_single_email( $to, $subject, $message, $message_headers = '' ) { 744 wp_mail( $to, $subject, $message, $message_headers ); 745 } 746 747 /** 748 * Returns a list of recipients for a given post 749 * 750 * @param $post object 751 * @param $string bool Whether to return recipients as comma-delimited string or array 752 * 753 * @return string|array 754 */ 755 private function _get_notification_recipients( $post, $string = false ) { 756 $post_id = $post->ID; 757 if ( ! $post_id ) { 758 return []; 759 } 760 761 $authors = []; 762 $admins = []; 763 $role_users = []; 764 765 // Get users and roles to notify 766 $roles = $this->get_roles_to_notify( $post_id, 'slugs' ); 767 foreach ( (array ) $roles as $role_id ) { 768 $users = get_users( 769 [ 770 'role' => $role_id, 771 ] 772 ); 773 774 if ( ! empty( $users ) ) { 775 foreach ( $users as $user ) { 776 if ( is_user_member_of_blog( $user->ID ) ) { 777 $role_users[] = $user->user_email; 778 } 779 } 780 } 781 } 782 783 $users = $this->get_users_to_notify( $post_id, 'user_email' ); 784 785 // Merge arrays and filter any duplicates 786 $recipients = array_merge( $authors, $admins, $users, $role_users ); 787 $recipients = array_unique( $recipients ); 788 789 // Process the recipients for this email to be sent 790 foreach ( $recipients as $key => $user_email ) { 791 // Get rid of empty email entries 792 if ( empty( $recipients[ $key ] ) ) { 793 unset( $recipients[ $key ] ); 794 } 795 // Don't send the email to the current user unless we've explicitly indicated they should receive it 796 if ( false === apply_filters( 'publishpress_notify_current_user', 797 false ) && wp_get_current_user()->user_email == $user_email ) { 798 unset( $recipients[ $key ] ); 799 } 800 } 801 802 // Filter to allow further modification of recipients 803 $recipients = apply_filters( 'pp_notification_recipients', $recipients, $post, $string ); 804 805 // If string set to true, return comma-delimited 806 if ( $string && is_array( $recipients ) ) { 807 return implode( ',', $recipients ); 808 } else { 809 return $recipients; 810 } 811 } 812 813 /** 814 * Set a user or users to be notified for a post 815 * 816 * @param int|object $post Post object or ID 817 * @param string|array $users User or users to subscribe to post updates 818 * @param bool $append Whether users should be added to pp_notify_user list or replace existing list 819 * 820 * @return true|WP_Error $response True on success, WP_Error on failure 821 */ 822 public function post_set_users_to_notify( $post, $users, $append = true ) { 823 $post = get_post( $post ); 824 if ( ! $post ) { 825 return new WP_Error( 'missing-post', $this->module->messages['missing-post'] ); 826 } 827 828 if ( ! is_array( $users ) ) { 829 $users = [ $users ]; 830 } 831 832 $user_terms = []; 833 834 foreach ( $users as $user ) { 835 if ( is_int( $user ) ) { 836 $user = get_user_by( 'id', $user ); 837 } elseif ( is_string( $user ) ) { 838 $user = get_user_by( 'login', $user ); 839 } 840 841 if ( ! is_object( $user ) ) { 842 continue; 843 } 844 845 $name = $user->user_login; 846 847 // Add user as a term if they don't exist 848 $term = $this->add_term_if_not_exists( $name, $this->notify_user_taxonomy ); 849 850 if ( ! is_wp_error( $term ) ) { 851 $user_terms[] = $name; 852 } 853 } 854 855 $set = wp_set_object_terms( $post->ID, $user_terms, $this->notify_user_taxonomy, $append ); 856 857 if ( is_wp_error( $set ) ) { 858 return $set; 859 } else { 860 return true; 861 } 862 } 863 864 /** 865 * Set a role or roles to be notified for a post 866 * 867 * @param int|object $post Post object or ID 868 * @param string|array $roles Role or roles to subscribe to post updates 869 * @param bool $append Whether roles should be added to pp_notify_role list or replace existing list 870 * 871 * @return true|WP_Error $response True on success, WP_Error on failure 872 */ 873 public function post_set_roles_to_notify( $post, $roles, $append = true ) { 874 $post = get_post( $post ); 875 if ( ! $post ) { 876 return new WP_Error( 'missing-post', $this->module->messages['missing-post'] ); 877 } 878 879 if ( ! is_array( $roles ) ) { 880 $roles = [ $roles ]; 881 } 882 883 $role_terms = []; 884 885 foreach ( $roles as $role ) { 886 $role = get_role( $role ); 887 888 if ( ! is_object( $role ) ) { 889 continue; 890 } 891 892 // Add user as a term if they don't exist 893 $term = $this->add_term_if_not_exists( $role->name, $this->notify_role_taxonomy ); 894 895 if ( ! is_wp_error( $term ) ) { 896 $role_terms[] = $role->name; 897 } 898 } 899 900 $set = wp_set_object_terms( $post->ID, $role_terms, $this->notify_role_taxonomy, $append ); 901 902 if ( is_wp_error( $set ) ) { 903 return $set; 904 } else { 905 return true; 906 } 907 } 908 909 /** 910 * Removes user from pp_notify_user taxonomy for the given Post, 911 * so they no longer receive future notifications. 912 * 913 * @param object $post Post object or ID 914 * @param int|string|array $users One or more users to stop being notified for the post 915 * 916 * @return true|WP_Error $response True on success, WP_Error on failure 917 */ 918 public function post_set_users_stop_notify( $post, $users ) { 919 $post = get_post( $post ); 920 if ( ! $post ) { 921 return new WP_Error( 'missing-post', $this->module->messages['missing-post'] ); 922 } 923 924 if ( ! is_array( $users ) ) { 925 $users = [ $users ]; 926 } 927 928 $terms = get_the_terms( $post->ID, $this->notify_user_taxonomy ); 929 if ( is_wp_error( $terms ) ) { 930 return $terms; 931 } 932 933 $user_terms = wp_list_pluck( $terms, 'slug' ); 934 foreach ( $users as $user ) { 935 if ( is_int( $user ) ) { 936 $user = get_user_by( 'id', $user ); 937 } elseif ( is_string( $user ) ) { 938 $user = get_user_by( 'login', $user ); 939 } 940 941 if ( ! is_object( $user ) ) { 942 continue; 943 } 944 945 $key = array_search( $user->user_login, $user_terms ); 946 if ( false !== $key ) { 947 unset( $user_terms[ $key ] ); 948 } 949 } 950 $set = wp_set_object_terms( $post->ID, $user_terms, $this->notify_user_taxonomy, false ); 951 952 if ( is_wp_error( $set ) ) { 953 return $set; 954 } else { 955 return true; 956 } 957 } 958 959 /** 960 * add_role_to_notify() 961 * 962 */ 963 public function add_role_to_notify( $post, $roles = 0, $append = true ) { 964 $post_id = ( is_int( $post ) ) ? $post : $post->ID; 965 if ( ! is_array( $roles ) ) { 966 $roles = [ $roles ]; 967 } 968 969 // make sure each role id is an integer and not a number stored as a string 970 foreach ( $roles as $key => $role ) { 971 $roles[ $key ] = intval( $role ); 972 } 973 974 wp_set_object_terms( $post_id, $roles, $this->notify_role_taxonomy, $append ); 975 976 return; 977 } 978 979 /** 980 * Removes users that are deleted from receiving future notifications (i.e. makes them out of notify list for posts FOREVER! ) 981 * 982 * @param $id int ID of the user 983 */ 984 public function delete_user_action( $id ) { 985 if ( ! $id ) { 986 return; 987 } 988 989 // get user data 990 $user = get_userdata( $id ); 991 992 if ( $user ) { 993 // Delete term from the pp_notify_user taxonomy 994 $notify_user_term = get_term_by( 'name', $user->user_login, $this->notify_user_taxonomy ); 995 if ( $notify_user_term ) { 996 wp_delete_term( $notify_user_term->term_id, $this->notify_user_taxonomy ); 997 } 998 } 999 1000 return; 1001 } 1002 1003 /** 1004 * Add user as a term if they aren't already 1005 * 1006 * @param $term string term to be added 1007 * @param $taxonomy string taxonomy to add term to 1008 * 1009 * @return WP_error if insert fails, true otherwise 1010 */ 1011 public function add_term_if_not_exists( $term, $taxonomy ) { 1012 if ( ! term_exists( $term, $taxonomy ) ) { 1013 $args = [ 'slug' => sanitize_title( $term ) ]; 1014 1015 return wp_insert_term( $term, $taxonomy, $args ); 1016 } 1017 1018 return true; 1019 } 1020 1021 /** 1022 * Gets a list of the users to be notified for the specified post 1023 * 1024 * @param int $post_id The ID of the post 1025 * @param string $return The field to return 1026 * 1027 * @return array $users Users to notify for the specified posts 1028 */ 1029 public function get_users_to_notify( $post_id, $return = 'user_login' ) { 1030 // Get pp_notify_user terms for the post 1031 $users = wp_get_object_terms( $post_id, $this->notify_user_taxonomy, [ 'fields' => 'names' ] ); 1032 1033 // Don't have any users to notify 1034 if ( ! $users || is_wp_error( $users ) ) { 1035 return []; 1036 } 1037 1038 // if just want user_login, return as is 1039 if ( $return == 'user_login' ) { 1040 return $users; 1041 } 1042 1043 foreach ( (array ) $users as $key => $user ) { 1044 switch ( $user ) { 1045 case is_int( $user ): 1046 $search = 'id'; 1047 break; 1048 case is_email( $user ): 1049 $search = 'email'; 1050 break; 1051 default: 1052 $search = 'login'; 1053 break; 1054 } 1055 $new_user = get_user_by( $search, $user ); 1056 if ( ! $new_user || ! is_user_member_of_blog( $new_user->ID ) ) { 1057 unset( $users[ $key ] ); 1058 continue; 1059 } 1060 switch ( $return ) { 1061 case 'user_login': 1062 $users[ $key ] = $new_user->user_login; 1063 break; 1064 case 'id': 1065 $users[ $key ] = $new_user->ID; 1066 break; 1067 case 'user_email': 1068 $users[ $key ] = $new_user->user_email; 1069 break; 1070 case 'object': 1071 $users[ $key ] = $new_user; 1072 break; 1073 } 1074 } 1075 if ( ! $users || is_wp_error( $users ) ) { 1076 $users = []; 1077 } 1078 1079 return $users; 1080 } 1081 1082 /** 1083 * Gets a list of the roles that should be notified for the specified post 1084 * 1085 * @param int $post_id 1086 * 1087 * @return array $roles All of the role slugs 1088 */ 1089 public function get_roles_to_notify( $post_id, $return = 'all' ) { 1090 global $publishpress; 1091 1092 // Workaround for the fact that get_object_terms doesn't return just slugs 1093 if ( $return == 'slugs' ) { 1094 $fields = 'all'; 1095 } else { 1096 $fields = $return; 1097 } 1098 1099 $roles = wp_get_object_terms( $post_id, $this->notify_role_taxonomy, [ 'fields' => $fields ] ); 1100 1101 if ( $return == 'slugs' ) { 1102 $slugs = []; 1103 foreach ( $roles as $role ) { 1104 $slugs[] = $role->slug; 1105 } 1106 $roles = $slugs; 1107 } 1108 1109 return $roles; 1110 } 1111 1112 /** 1113 * Gets a list of posts that a user is selected to be notified 1114 * 1115 * @param string|int $user user_login or id of user 1116 * @param array $args 1117 * 1118 * @return array $posts Posts a user is selected to be notified 1119 */ 1120 public function get_user_to_notify_posts( $user = 0, $args = null ) { 1121 if ( ! $user ) { 1122 $user = (int ) wp_get_current_user()->ID; 1123 } 1124 1125 if ( is_int( $user ) ) { 1126 $user = get_userdata( $user )->user_login; 1127 } 1128 1129 $post_args = [ 1130 'tax_query' => [ 1131 [ 1132 'taxonomy' => $this->notify_user_taxonomy, 1133 'field' => 'slug', 1134 'terms' => $user, 1135 ], 1136 ], 1137 'posts_per_page' => '10', 1138 'orderby' => 'modified', 1139 'order' => 'DESC', 1140 'post_status' => 'any', 1141 ]; 1142 $post_args = apply_filters( 'pp_user_to_notify_posts_query_args', $post_args ); 1143 $posts = get_posts( $post_args ); 1144 1145 return $posts; 1146 } 1147 1148 /** 1149 * Register settings for notifications so we can partially use the Settings API 1150 * (We use the Settings API for form generation, but not saving ) 1151 * 1152 * @since 0.7 1153 */ 1154 public function register_settings() { 1155 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', 1156 $this->module->options_group_name ); 1157 add_settings_field( 1158 'post_types', 1159 __( 'Show the "Notify me" and "Stop notifying me" links for these post types:', 'publishpress' ), 1160 [ $this, 'settings_post_types_option' ], 1161 $this->module->options_group_name, 1162 $this->module->options_group_name . '_general' 1163 ); 1164 1165 add_settings_field( 1166 'email_from', 1167 __( 'Email from:', 'publishpress' ), 1168 [ $this, 'settings_email_from_option' ], 1169 $this->module->options_group_name, 1170 $this->module->options_group_name . '_general' 1171 ); 1172 1173 add_settings_field( 1174 'notify_author_by_default', 1175 __( 'Always notify the author of the content:', 'publishpress' ), 1176 [ $this, 'settings_notify_author_by_default_option' ], 1177 $this->module->options_group_name, 1178 $this->module->options_group_name . '_general' 1179 ); 1180 1181 add_settings_field( 1182 'notify_current_user_by_default', 1183 __( 'Always notify users who have edited the content:', 'publishpress' ), 1184 [ $this, 'settings_notify_current_user_by_default_option' ], 1185 $this->module->options_group_name, 1186 $this->module->options_group_name . '_general' 1187 ); 1188 } 1189 1190 /** 1191 * Chose the post types for notifications 1192 * 1193 * @since 0.7 1194 */ 1195 public function settings_post_types_option() { 1196 global $publishpress; 1197 $publishpress->settings->helper_option_custom_post_type( $this->module ); 1198 } 1199 1200 public function get_email_from() { 1201 if ( ! isset( $this->module->options->email_from_name ) ) { 1202 $name = get_bloginfo( 'name' ); 1203 } else { 1204 $name = $this->module->options->email_from_name; 1205 } 1206 1207 if ( ! isset( $this->module->options->email_from ) ) { 1208 $email = get_bloginfo( 'admin_email' ); 1209 } else { 1210 $email = $this->module->options->email_from; 1211 } 1212 1213 return [ 1214 'name' => $name, 1215 'email' => $email, 1216 ]; 1217 } 1218 1219 /** 1220 * Field to customize the email for the "email from". 1221 */ 1222 public function settings_email_from_option() { 1223 $email_from = $this->get_email_from(); 1224 1225 echo '<input 1341 1226 id="' . $this->module->slug . '_email_from_name" 1342 1227 type="text" 1343 1228 style="min-width: 300px" 1344 placeholder="' . get_bloginfo( 'name') . '"1229 placeholder="' . get_bloginfo( 'name' ) . '" 1345 1230 name="' . $this->module->options_group_name . '[email_from_name]" 1346 1231 value="' . $email_from['name'] . '" /> 1347 1232 </label>'; 1348 echo '<br />';1349 echo '<input1233 echo '<br />'; 1234 echo '<input 1350 1235 id="' . $this->module->slug . '_email_from" 1351 1236 type="email" 1352 1237 style="min-width: 300px" 1353 placeholder="' . get_bloginfo( 'admin_email') . '"1238 placeholder="' . get_bloginfo( 'admin_email' ) . '" 1354 1239 name="' . $this->module->options_group_name . '[email_from]" 1355 1240 value="' . $email_from['email'] . '" /> 1356 1241 </label>'; 1357 } 1358 1359 /** 1360 * Field to choose between auto select author for notifications. 1361 */ 1362 public function settings_notify_author_by_default_option() 1363 { 1364 $checked = '1'; 1365 if (isset($this->module->options->notify_author_by_default)) 1366 { 1367 $checked = $this->module->options->notify_author_by_default; 1368 } 1369 1370 $checked = (bool)$checked ? 'checked="checked"' : ''; 1371 1372 echo '<input 1242 } 1243 1244 /** 1245 * Field to choose between auto select author for notifications. 1246 */ 1247 public function settings_notify_author_by_default_option() { 1248 $checked = '1'; 1249 if ( isset( $this->module->options->notify_author_by_default ) ) { 1250 $checked = $this->module->options->notify_author_by_default; 1251 } 1252 1253 $checked = (bool) $checked ? 'checked="checked"' : ''; 1254 1255 echo '<input 1373 1256 id="' . $this->module->slug . '_notify_author_by_default" 1374 1257 type="checkbox" 1375 1258 name="' . $this->module->options_group_name . '[notify_author_by_default]" 1376 1259 value="1" ' . $checked . '/>'; 1377 } 1378 1379 /** 1380 * Field to choose between auto select current user for notifications. 1381 */ 1382 public function settings_notify_current_user_by_default_option() 1383 { 1384 $checked = '1'; 1385 if (isset($this->module->options->notify_current_user_by_default)) 1386 { 1387 $checked = $this->module->options->notify_current_user_by_default; 1388 } 1389 1390 $checked = (bool)$checked ? 'checked="checked"' : ''; 1391 1392 echo '<input 1260 } 1261 1262 /** 1263 * Field to choose between auto select current user for notifications. 1264 */ 1265 public function settings_notify_current_user_by_default_option() { 1266 $checked = '1'; 1267 if ( isset( $this->module->options->notify_current_user_by_default ) ) { 1268 $checked = $this->module->options->notify_current_user_by_default; 1269 } 1270 1271 $checked = (bool) $checked ? 'checked="checked"' : ''; 1272 1273 echo '<input 1393 1274 id="' . $this->module->slug . '_notify_current_user_by_default" 1394 1275 type="checkbox" 1395 1276 name="' . $this->module->options_group_name . '[notify_current_user_by_default]" 1396 1277 value="1" ' . $checked . '/>'; 1397 } 1398 1399 /** 1400 * Validate our user input as the settings are being saved 1401 * 1402 * @since 0.7 1403 */ 1404 public function settings_validate($new_options) 1405 { 1406 1407 // Whitelist validation for the post type options 1408 if (!isset($new_options['post_types'])) 1409 { 1410 $new_options['post_types'] = array(); 1411 } 1412 $new_options['post_types'] = $this->clean_post_type_options($new_options['post_types'], $this->module->post_type_support); 1413 1414 if (isset($new_options['email_from'])) 1415 { 1416 $new_options['email_from_name'] = filter_var($new_options['email_from_name'], FILTER_SANITIZE_STRING); 1417 $new_options['email_from'] = filter_var($new_options['email_from'], FILTER_SANITIZE_EMAIL); 1418 } 1419 1420 1421 if (isset($new_options['notify_author_by_default'])) 1422 { 1423 $new_options['notify_author_by_default'] = (bool)$new_options['notify_author_by_default'] ? '1' : '0'; 1424 } else 1425 { 1426 $new_options['notify_author_by_default'] = '0'; 1427 } 1428 1429 1430 if (isset($new_options['notify_current_user_by_default'])) 1431 { 1432 $new_options['notify_current_user_by_default'] = (bool)$new_options['notify_current_user_by_default'] ? '1' : '0'; 1433 } else 1434 { 1435 $new_options['notify_current_user_by_default'] = '0'; 1436 } 1437 1438 return $new_options; 1439 } 1440 1441 /** 1442 * Settings page for notifications 1443 * 1444 * @since 0.7 1445 */ 1446 public function print_configure_view() 1447 { 1448 settings_fields($this->module->options_group_name); 1449 do_settings_sections($this->module->options_group_name); 1450 } 1451 1452 /** 1453 * Gets a simple phrase containing the formatted date and time that the post is scheduled for. 1454 * 1455 * @since 0.8 1456 * 1457 * @param obj $post Post object 1458 * @return str $scheduled_datetime The scheduled datetime in human-readable format 1459 */ 1460 private function get_scheduled_datetime($post) 1461 { 1462 $scheduled_ts = strtotime($post->post_date); 1463 1464 $date = date_i18n(get_option('date_format'), $scheduled_ts); 1465 $time = date_i18n(get_option('time_format'), $scheduled_ts); 1466 1467 return sprintf(__('%1$s at %2$s', 'publishpress'), $date, $time); 1468 } 1469 1470 public function send_notification_status_update($args) 1471 { 1472 $new_status = $args['new_status']; 1473 $old_status = $args['old_status']; 1474 $post = $args['post']; 1475 1476 // Get current user 1477 $current_user = wp_get_current_user(); 1478 1479 $post_author = get_userdata($post->post_author); 1480 //$duedate = $publishpress->post_metadata->get_post_meta( $post->ID, 'duedate', true ); 1481 1482 $blogname = get_option('blogname'); 1483 1484 $body = ''; 1485 1486 $post_id = $post->ID; 1487 $post_title = pp_draft_or_post_title($post_id); 1488 $post_type = get_post_type_object($post->post_type)->labels->singular_name; 1489 1490 if (0 != $current_user->ID) 1491 { 1492 $current_user_display_name = $current_user->display_name; 1493 $current_user_email = sprintf('(%s )', $current_user->user_email); 1494 } else 1495 { 1496 $current_user_display_name = __('WordPress Scheduler', 'publishpress'); 1497 $current_user_email = ''; 1498 } 1499 1500 // Email subject and first line of body 1501 // Set message subjects according to what action is being taken on the Post 1502 if ($old_status == 'new' || $old_status == 'auto-draft') 1503 { 1504 /* translators: 1: site name, 2: post type, 3. post title */ 1505 $subject = sprintf(__('[%1$s] New %2$s Created: "%3$s"', 'publishpress'), $blogname, $post_type, $post_title); 1506 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1507 $body .= sprintf(__('A new %1$s (#%2$s "%3$s" ) was created by %4$s %5$s', 'publishpress'), $post_type, $post_id, $post_title, $current_user->display_name, $current_user->user_email) . "\r\n"; 1508 } else if ($new_status == 'trash') 1509 { 1510 /* translators: 1: site name, 2: post type, 3. post title */ 1511 $subject = sprintf(__('[%1$s] %2$s Trashed: "%3$s"', 'publishpress'), $blogname, $post_type, $post_title); 1512 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1513 $body .= sprintf(__('%1$s #%2$s "%3$s" was moved to the trash by %4$s %5$s', 'publishpress'), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email) . "\r\n"; 1514 } else if ($old_status == 'trash') 1515 { 1516 /* translators: 1: site name, 2: post type, 3. post title */ 1517 $subject = sprintf(__('[%1$s] %2$s Restored (from Trash ): "%3$s"', 'publishpress'), $blogname, $post_type, $post_title); 1518 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1519 $body .= sprintf(__('%1$s #%2$s "%3$s" was restored from trash by %4$s %5$s', 'publishpress'), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email) . "\r\n"; 1520 } else if ($new_status == 'future') 1521 { 1522 /* translators: 1: site name, 2: post type, 3. post title */ 1523 $subject = sprintf(__('[%1$s] %2$s Scheduled: "%3$s"'), $blogname, $post_type, $post_title); 1524 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email 6. scheduled date */ 1525 $body .= sprintf(__('%1$s #%2$s "%3$s" was scheduled by %4$s %5$s. It will be published on %6$s'), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email, $this->get_scheduled_datetime($post)) . "\r\n"; 1526 } else if ($new_status == 'publish') 1527 { 1528 /* translators: 1: site name, 2: post type, 3. post title */ 1529 $subject = sprintf(__('[%1$s] %2$s Published: "%3$s"', 'publishpress'), $blogname, $post_type, $post_title); 1530 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1531 $body .= sprintf(__('%1$s #%2$s "%3$s" was published by %4$s %5$s', 'publishpress'), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email) . "\r\n"; 1532 } else if ($old_status == 'publish') 1533 { 1534 /* translators: 1: site name, 2: post type, 3. post title */ 1535 $subject = sprintf(__('[%1$s] %2$s Unpublished: "%3$s"', 'publishpress'), $blogname, $post_type, $post_title); 1536 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1537 $body .= sprintf(__('%1$s #%2$s "%3$s" was unpublished by %4$s %5$s', 'publishpress'), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email) . "\r\n"; 1538 } else 1539 { 1540 /* translators: 1: site name, 2: post type, 3. post title */ 1541 $subject = sprintf(__('[%1$s] %2$s Status Changed for "%3$s"', 'publishpress'), $blogname, $post_type, $post_title); 1542 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1543 $body .= sprintf(__('Status was changed for %1$s #%2$s "%3$s" by %4$s %5$s', 'publishpress'), $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email) . "\r\n"; 1544 } 1545 1546 /* translators: 1: date, 2: time, 3: timezone */ 1547 $body .= sprintf(__('This action was taken on %1$s at %2$s %3$s', 'publishpress'), date_i18n(get_option('date_format')), date_i18n(get_option('time_format')), get_option('timezone_string')) . "\r\n"; 1548 1549 $old_status_friendly_name = $this->get_post_status_friendly_name($old_status); 1550 $new_status_friendly_name = $this->get_post_status_friendly_name($new_status); 1551 1552 // Email body 1553 $body .= "\r\n"; 1554 /* translators: 1: old status, 2: new status */ 1555 $body .= sprintf(__('%1$s => %2$s', 'publishpress'), $old_status_friendly_name, $new_status_friendly_name); 1556 $body .= "\r\n\r\n"; 1557 1558 $body .= "--------------------\r\n\r\n"; 1559 1560 $body .= sprintf(__('== %s Details ==', 'publishpress'), $post_type) . "\r\n"; 1561 $body .= sprintf(__('Title: %s', 'publishpress'), $post_title) . "\r\n"; 1562 if (!empty($post_author)) 1563 { 1564 /* translators: 1: author name, 2: author email */ 1565 $body .= sprintf(__('Author: %1$s (%2$s )', 'publishpress'), $post_author->display_name, $post_author->user_email) . "\r\n"; 1566 } 1567 1568 $admin_path = 'post.php?post=' . $post_id . '&action=edit'; 1569 $edit_link = htmlspecialchars_decode(admin_url($admin_path)); 1570 if ($new_status != 'publish') 1571 { 1572 $view_link = add_query_arg(array('preview' => 'true'), wp_get_shortlink($post_id)); 1573 } else 1574 { 1575 $view_link = htmlspecialchars_decode(get_permalink($post_id)); 1576 } 1577 $body .= "\r\n"; 1578 $body .= __('== Actions ==', 'publishpress') . "\r\n"; 1579 $body .= sprintf(__('Add editorial comment: %s', 'publishpress'), $edit_link . '#editorialcomments/add') . "\r\n"; 1580 $body .= sprintf(__('Edit: %s', 'publishpress'), $edit_link) . "\r\n"; 1581 $body .= sprintf(__('View: %s', 'publishpress'), $view_link) . "\r\n"; 1582 1583 $body .= $this->get_notification_footer($post); 1584 1585 $this->send_email('status-change', $post, $subject, $body); 1586 } 1587 1588 public function send_notification_comment($args) 1589 { 1590 /* translators: 1: blog name, 2: post title */ 1591 $subject = sprintf(__('[%1$s] New Editorial Comment: "%2$s"', 'publishpress'), $args['blogname'], $args['post_title']); 1592 1593 /* translators: 1: post id, 2: post title, 3. post type */ 1594 $body = sprintf(__('A new editorial comment was added to %3$s #%1$s "%2$s"', 'publishpress'), $args['post_id'], $args['post_title'], $args['post_type']) . "\r\n\r\n"; 1595 /* translators: 1: comment author, 2: author email, 3: date, 4: time */ 1596 $body .= sprintf(__('%1$s (%2$s ) said on %3$s at %4$s:', 'publishpress'), $args['current_user']->display_name, $args['current_user']->user_email, mysql2date(get_option('date_format'), $args['comment']->comment_date), mysql2date(get_option('time_format'), $args['comment']->comment_date)) . "\r\n"; 1597 $body .= "\r\n" . $args['comment']->comment_content . "\r\n"; 1598 1599 // @TODO: mention if it was a reply 1600 /* 1601 if( $parent ) { 1602 1603 } 1604 */ 1605 1606 $body .= "\r\n--------------------\r\n"; 1607 1608 $admin_path = 'post.php?post=' . $args['post_id'] . '&action=edit'; 1609 $edit_link = htmlspecialchars_decode(admin_url($admin_path)); 1610 $view_link = htmlspecialchars_decode(get_permalink($args['post_id'])); 1611 1612 $body .= "\r\n"; 1613 $body .= __('== Actions ==', 'publishpress') . "\r\n"; 1614 $body .= sprintf(__('Reply: %s', 'publishpress'), $edit_link . '#editorialcomments/reply/' . $args['comment']->comment_ID) . "\r\n"; 1615 $body .= sprintf(__('Add editorial comment: %s', 'publishpress'), $edit_link . '#editorialcomments/add') . "\r\n"; 1616 $body .= sprintf(__('Edit: %s', 'publishpress'), $edit_link) . "\r\n"; 1617 $body .= sprintf(__('View: %s', 'publishpress'), $view_link) . "\r\n"; 1618 1619 $body .= "\r\n" . sprintf(__('You can see all editorial comments on this %s here: ', 'publishpress'), $args['post_type']) . "\r\n"; 1620 $body .= $edit_link . "#editorialcomments" . "\r\n\r\n"; 1621 1622 $body .= $this->get_notification_footer($args['post']); 1623 1624 $this->send_email('comment', $args['post'], $subject, $body); 1625 } 1626 } 1278 } 1279 1280 /** 1281 * Validate our user input as the settings are being saved 1282 * 1283 * @since 0.7 1284 */ 1285 public function settings_validate( $new_options ) { 1286 1287 // Whitelist validation for the post type options 1288 if ( ! isset( $new_options['post_types'] ) ) { 1289 $new_options['post_types'] = []; 1290 } 1291 $new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], 1292 $this->module->post_type_support ); 1293 1294 if ( isset( $new_options['email_from'] ) ) { 1295 $new_options['email_from_name'] = filter_var( $new_options['email_from_name'], FILTER_SANITIZE_STRING ); 1296 $new_options['email_from'] = filter_var( $new_options['email_from'], FILTER_SANITIZE_EMAIL ); 1297 } 1298 1299 1300 if ( isset( $new_options['notify_author_by_default'] ) ) { 1301 $new_options['notify_author_by_default'] = (bool) $new_options['notify_author_by_default'] ? '1' : '0'; 1302 } else { 1303 $new_options['notify_author_by_default'] = '0'; 1304 } 1305 1306 1307 if ( isset( $new_options['notify_current_user_by_default'] ) ) { 1308 $new_options['notify_current_user_by_default'] = (bool) $new_options['notify_current_user_by_default'] ? '1' : '0'; 1309 } else { 1310 $new_options['notify_current_user_by_default'] = '0'; 1311 } 1312 1313 return $new_options; 1314 } 1315 1316 /** 1317 * Settings page for notifications 1318 * 1319 * @since 0.7 1320 */ 1321 public function print_configure_view() { 1322 settings_fields( $this->module->options_group_name ); 1323 do_settings_sections( $this->module->options_group_name ); 1324 } 1325 1326 /** 1327 * Gets a simple phrase containing the formatted date and time that the post is scheduled for. 1328 * 1329 * @since 0.8 1330 * 1331 * @param obj $post Post object 1332 * 1333 * @return str $scheduled_datetime The scheduled datetime in human-readable format 1334 */ 1335 private function get_scheduled_datetime( $post ) { 1336 $scheduled_ts = strtotime( $post->post_date ); 1337 1338 $date = date_i18n( get_option( 'date_format' ), $scheduled_ts ); 1339 $time = date_i18n( get_option( 'time_format' ), $scheduled_ts ); 1340 1341 return sprintf( __( '%1$s at %2$s', 'publishpress' ), $date, $time ); 1342 } 1343 1344 public function send_notification_status_update( $args ) { 1345 $new_status = $args['new_status']; 1346 $old_status = $args['old_status']; 1347 $post = $args['post']; 1348 1349 // Get current user 1350 $current_user = wp_get_current_user(); 1351 1352 $post_author = get_userdata( $post->post_author ); 1353 //$duedate = $publishpress->post_metadata->get_post_meta( $post->ID, 'duedate', true ); 1354 1355 $blogname = get_option( 'blogname' ); 1356 1357 $body = ''; 1358 1359 $post_id = $post->ID; 1360 $post_title = pp_draft_or_post_title( $post_id ); 1361 $post_type = get_post_type_object( $post->post_type )->labels->singular_name; 1362 1363 if ( 0 != $current_user->ID ) { 1364 $current_user_display_name = $current_user->display_name; 1365 $current_user_email = sprintf( '(%s )', $current_user->user_email ); 1366 } else { 1367 $current_user_display_name = __( 'WordPress Scheduler', 'publishpress' ); 1368 $current_user_email = ''; 1369 } 1370 1371 // Email subject and first line of body 1372 // Set message subjects according to what action is being taken on the Post 1373 if ( $old_status == 'new' || $old_status == 'auto-draft' ) { 1374 /* translators: 1: site name, 2: post type, 3. post title */ 1375 $subject = sprintf( __( '[%1$s] New %2$s Created: "%3$s"', 'publishpress' ), $blogname, $post_type, 1376 $post_title ); 1377 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1378 $body .= sprintf( __( 'A new %1$s (#%2$s "%3$s" ) was created by %4$s %5$s', 'publishpress' ), 1379 $post_type, $post_id, $post_title, $current_user->display_name, 1380 $current_user->user_email ) . "\r\n"; 1381 } elseif ( $new_status == 'trash' ) { 1382 /* translators: 1: site name, 2: post type, 3. post title */ 1383 $subject = sprintf( __( '[%1$s] %2$s Trashed: "%3$s"', 'publishpress' ), $blogname, $post_type, 1384 $post_title ); 1385 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1386 $body .= sprintf( __( '%1$s #%2$s "%3$s" was moved to the trash by %4$s %5$s', 'publishpress' ), 1387 $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n"; 1388 } elseif ( $old_status == 'trash' ) { 1389 /* translators: 1: site name, 2: post type, 3. post title */ 1390 $subject = sprintf( __( '[%1$s] %2$s Restored (from Trash ): "%3$s"', 'publishpress' ), $blogname, 1391 $post_type, $post_title ); 1392 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1393 $body .= sprintf( __( '%1$s #%2$s "%3$s" was restored from trash by %4$s %5$s', 'publishpress' ), 1394 $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n"; 1395 } elseif ( $new_status == 'future' ) { 1396 /* translators: 1: site name, 2: post type, 3. post title */ 1397 $subject = sprintf( __( '[%1$s] %2$s Scheduled: "%3$s"' ), $blogname, $post_type, $post_title ); 1398 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email 6. scheduled date */ 1399 $body .= sprintf( __( '%1$s #%2$s "%3$s" was scheduled by %4$s %5$s. It will be published on %6$s' ), 1400 $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email, 1401 $this->get_scheduled_datetime( $post ) ) . "\r\n"; 1402 } elseif ( $new_status == 'publish' ) { 1403 /* translators: 1: site name, 2: post type, 3. post title */ 1404 $subject = sprintf( __( '[%1$s] %2$s Published: "%3$s"', 'publishpress' ), $blogname, $post_type, 1405 $post_title ); 1406 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1407 $body .= sprintf( __( '%1$s #%2$s "%3$s" was published by %4$s %5$s', 'publishpress' ), $post_type, 1408 $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n"; 1409 } elseif ( $old_status == 'publish' ) { 1410 /* translators: 1: site name, 2: post type, 3. post title */ 1411 $subject = sprintf( __( '[%1$s] %2$s Unpublished: "%3$s"', 'publishpress' ), $blogname, $post_type, 1412 $post_title ); 1413 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1414 $body .= sprintf( __( '%1$s #%2$s "%3$s" was unpublished by %4$s %5$s', 'publishpress' ), $post_type, 1415 $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n"; 1416 } else { 1417 /* translators: 1: site name, 2: post type, 3. post title */ 1418 $subject = sprintf( __( '[%1$s] %2$s Status Changed for "%3$s"', 'publishpress' ), $blogname, 1419 $post_type, $post_title ); 1420 /* translators: 1: post type, 2: post id, 3. post title, 4. user name, 5. user email */ 1421 $body .= sprintf( __( 'Status was changed for %1$s #%2$s "%3$s" by %4$s %5$s', 'publishpress' ), 1422 $post_type, $post_id, $post_title, $current_user_display_name, $current_user_email ) . "\r\n"; 1423 } 1424 1425 /* translators: 1: date, 2: time, 3: timezone */ 1426 $body .= sprintf( __( 'This action was taken on %1$s at %2$s %3$s', 'publishpress' ), 1427 date_i18n( get_option( 'date_format' ) ), date_i18n( get_option( 'time_format' ) ), 1428 get_option( 'timezone_string' ) ) . "\r\n"; 1429 1430 $old_status_friendly_name = $this->get_post_status_friendly_name( $old_status ); 1431 $new_status_friendly_name = $this->get_post_status_friendly_name( $new_status ); 1432 1433 // Email body 1434 $body .= "\r\n"; 1435 /* translators: 1: old status, 2: new status */ 1436 $body .= sprintf( __( '%1$s => %2$s', 'publishpress' ), $old_status_friendly_name, 1437 $new_status_friendly_name ); 1438 $body .= "\r\n\r\n"; 1439 1440 $body .= "--------------------\r\n\r\n"; 1441 1442 $body .= sprintf( __( '== %s Details ==', 'publishpress' ), $post_type ) . "\r\n"; 1443 $body .= sprintf( __( 'Title: %s', 'publishpress' ), $post_title ) . "\r\n"; 1444 if ( ! empty( $post_author ) ) { 1445 /* translators: 1: author name, 2: author email */ 1446 $body .= sprintf( __( 'Author: %1$s (%2$s )', 'publishpress' ), $post_author->display_name, 1447 $post_author->user_email ) . "\r\n"; 1448 } 1449 1450 $admin_path = 'post.php?post=' . $post_id . '&action=edit'; 1451 $edit_link = htmlspecialchars_decode( admin_url( $admin_path ) ); 1452 if ( $new_status != 'publish' ) { 1453 $view_link = add_query_arg( [ 'preview' => 'true' ], wp_get_shortlink( $post_id ) ); 1454 } else { 1455 $view_link = htmlspecialchars_decode( get_permalink( $post_id ) ); 1456 } 1457 $body .= "\r\n"; 1458 $body .= __( '== Actions ==', 'publishpress' ) . "\r\n"; 1459 $body .= sprintf( __( 'Add editorial comment: %s', 'publishpress' ), 1460 $edit_link . '#editorialcomments/add' ) . "\r\n"; 1461 $body .= sprintf( __( 'Edit: %s', 'publishpress' ), $edit_link ) . "\r\n"; 1462 $body .= sprintf( __( 'View: %s', 'publishpress' ), $view_link ) . "\r\n"; 1463 1464 $body .= $this->get_notification_footer( $post ); 1465 1466 $this->send_email( 'status-change', $post, $subject, $body ); 1467 } 1468 1469 public function send_notification_comment( $args ) { 1470 /* translators: 1: blog name, 2: post title */ 1471 $subject = sprintf( __( '[%1$s] New Editorial Comment: "%2$s"', 'publishpress' ), $args['blogname'], 1472 $args['post_title'] ); 1473 1474 /* translators: 1: post id, 2: post title, 3. post type */ 1475 $body = sprintf( __( 'A new editorial comment was added to %3$s #%1$s "%2$s"', 'publishpress' ), 1476 $args['post_id'], $args['post_title'], $args['post_type'] ) . "\r\n\r\n"; 1477 /* translators: 1: comment author, 2: author email, 3: date, 4: time */ 1478 $body .= sprintf( __( '%1$s (%2$s ) said on %3$s at %4$s:', 'publishpress' ), 1479 $args['current_user']->display_name, $args['current_user']->user_email, 1480 mysql2date( get_option( 'date_format' ), $args['comment']->comment_date ), 1481 mysql2date( get_option( 'time_format' ), $args['comment']->comment_date ) ) . "\r\n"; 1482 $body .= "\r\n" . $args['comment']->comment_content . "\r\n"; 1483 1484 // @TODO: mention if it was a reply 1485 /* 1486 if( $parent ) { 1487 1488 } 1489 */ 1490 1491 $body .= "\r\n--------------------\r\n"; 1492 1493 $admin_path = 'post.php?post=' . $args['post_id'] . '&action=edit'; 1494 $edit_link = htmlspecialchars_decode( admin_url( $admin_path ) ); 1495 $view_link = htmlspecialchars_decode( get_permalink( $args['post_id'] ) ); 1496 1497 $body .= "\r\n"; 1498 $body .= __( '== Actions ==', 'publishpress' ) . "\r\n"; 1499 $body .= sprintf( __( 'Reply: %s', 'publishpress' ), 1500 $edit_link . '#editorialcomments/reply/' . $args['comment']->comment_ID ) . "\r\n"; 1501 $body .= sprintf( __( 'Add editorial comment: %s', 'publishpress' ), 1502 $edit_link . '#editorialcomments/add' ) . "\r\n"; 1503 $body .= sprintf( __( 'Edit: %s', 'publishpress' ), $edit_link ) . "\r\n"; 1504 $body .= sprintf( __( 'View: %s', 'publishpress' ), $view_link ) . "\r\n"; 1505 1506 $body .= "\r\n" . sprintf( __( 'You can see all editorial comments on this %s here: ', 'publishpress' ), 1507 $args['post_type'] ) . "\r\n"; 1508 $body .= $edit_link . "#editorialcomments" . "\r\n\r\n"; 1509 1510 $body .= $this->get_notification_footer( $args['post'] ); 1511 1512 $this->send_email( 'comment', $args['post'], $subject, $body ); 1513 } 1514 } 1627 1515 } -
publishpress/trunk/modules/roles/roles.php
r1860640 r1871614 150 150 151 151 add_action('profile_update', [$this, 'action_profile_update'], 10, 2); 152 add_action('user_register', [$this, 'action_profile_update'], 10);152 add_action('user_register', [$this, 'action_profile_update'], 9); 153 153 154 154 if ($this->wasPublishPressInstalledBefore()) { -
publishpress/trunk/publishpress.php
r1860640 r1871614 6 6 * Author: PublishPress 7 7 * Author URI: https://publishpress.com 8 * Version: 1.12. 08 * Version: 1.12.1 9 9 * Text Domain: publishpress 10 10 * Domain Path: /languages -
publishpress/trunk/readme.txt
r1860640 r1871614 6 6 Requires at least: 4.6 7 7 Requires PHP: 5.4 8 Tested up to: 4.9. 49 Stable tag: 1.12. 08 Tested up to: 4.9.5 9 Stable tag: 1.12.1 10 10 License: GPLv2 or later 11 11 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 133 133 The format is based on [Keep a Changelog](http://keepachangelog.com/) 134 134 and this project adheres to [Semantic Versioning](http://semver.org/). 135 136 = [1.12.1] - 2018-05-09 = 137 138 *Fixed:* 139 140 * Fixed PHP strict warning about trait and a redefined property; 141 * Fixed duplicated notifications on some scenarios; 142 143 *Changed:* 144 145 * Increased the priority of the hook user_register to have the multiple roles loaded and available for add-ons; 135 146 136 147 = [1.12.0] - 2018-04-18 =
Note: See TracChangeset
for help on using the changeset viewer.