| @@ -1,307 +1,22 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | /* |
| 4 | 4 | Plugin Name: Email Post Changes |
| 5 | -Description: Whenever a change to a post or page is made, those changes are emailed to the blog's admin. | |
| 5 | +Description: Whenever a change to a post or page is made, those changes are emailed to the email addresses you specify. | |
| 6 | 6 | Plugin URI: http://wordpress.org/extend/plugins/email-post-changes/ |
| 7 | -Version: 0.3 | |
| 7 | +Version: 0.7 | |
| 8 | 8 | Author: Michael D Adams |
| 9 | 9 | Author URI: http://blogwaffe.com/ |
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | -class Email_Post_Changes { | |
| 13 | - var $defaults; | |
| 12 | +require_once 'class.email-post-changes.php'; | |
| 14 | 13 | |
| 15 | - var $left_post; | |
| 16 | - var $right_post; | |
| 14 | +function email_post_changes_action_links( $links ) { | |
| 15 | + array_unshift( $links, '<a href="options-general.php?page=' . Email_Post_Changes::ADMIN_PAGE . '">' . __( 'Settings' ) . "</a>" ); | |
| 16 | + return $links; | |
| 17 | +} | |
| 17 | 18 | |
| 18 | - var $text_diff; | |
| 19 | +add_action( 'init', array( 'Email_Post_Changes', 'init' ) ); | |
| 19 | 20 | |
| 20 | - const ADMIN_PAGE = 'email_post_changes'; | |
| 21 | - const OPTION_GROUP = 'email_post_changes'; | |
| 22 | - const OPTION = 'email_post_changes'; | |
| 21 | +add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'email_post_changes_action_links' ); | |
| 23 | 22 | |
| 24 | - function &init() { | |
| 25 | - static $instance = null; | |
| 26 | - | |
| 27 | - if ( $instance ) | |
| 28 | - return $instance; | |
| 29 | - | |
| 30 | - $class = __CLASS__; | |
| 31 | - $instance = new $class; | |
| 32 | - } | |
| 33 | - | |
| 34 | - function __construct() { | |
| 35 | - $this->defaults = array( | |
| 36 | - 'emails' => array( get_option( 'admin_email' ) ), | |
| 37 | - 'post_types' => array( 'post', 'page' ) | |
| 38 | - ); | |
| 39 | - | |
| 40 | - add_action( 'wp_insert_post', array( &$this, 'wp_insert_post' ), 10, 2 ); | |
| 41 | - add_action( 'admin_menu', array( &$this, 'admin_menu' ) ); | |
| 42 | - } | |
| 43 | - | |
| 44 | - function get_post_types() { | |
| 45 | - $post_types = get_post_types(); | |
| 46 | - if ( false !== $pos = array_search( 'revision', $post_types ) ) | |
| 47 | - unset( $post_types[$pos] ); | |
| 48 | - return $post_types; | |
| 49 | - } | |
| 50 | - | |
| 51 | - function get_options( $just_defaults = false ) { | |
| 52 | - if ( $just_defaults ) | |
| 53 | - return $this->defaults; | |
| 54 | - | |
| 55 | - return get_option( 'email_post_changes', $this->defaults ); | |
| 56 | - } | |
| 57 | - | |
| 58 | - // The meat of the plugin | |
| 59 | - function wp_insert_post( $post_id, $post ) { | |
| 60 | - $options = $this->get_options(); | |
| 61 | - | |
| 62 | - if ( 'revision' == $post->post_type ) { // Revision is saved first | |
| 63 | - if ( wp_is_post_autosave( $post ) ) | |
| 64 | - return; | |
| 65 | - $this->left_post = $post; | |
| 66 | - } elseif ( !empty( $this->left_post ) && $this->left_post->post_parent == $post->ID ) { // Then new post | |
| 67 | - if ( !in_array( $post->post_type, $options['post_types'] ) ) | |
| 68 | - return; | |
| 69 | - $this->right_post = $post; | |
| 70 | - } | |
| 71 | - | |
| 72 | - if ( !$this->left_post || !$this->right_post ) | |
| 73 | - return; | |
| 74 | - | |
| 75 | - $html_diffs = array(); | |
| 76 | - $text_diffs = array(); | |
| 77 | - $identical = true; | |
| 78 | - foreach ( _wp_post_revision_fields() as $field => $field_title ) { | |
| 79 | - $left = apply_filters( "_wp_post_revision_field_$field", $this->left_post->$field, $field ); | |
| 80 | - $right = apply_filters( "_wp_post_revision_field_$field", $this->right_post->$field, $field ); | |
| 81 | - | |
| 82 | - if ( !$diff = wp_text_diff( $left, $right ) ) | |
| 83 | - continue; | |
| 84 | - $html_diffs[$field_title] = $diff; | |
| 85 | - | |
| 86 | - $left = normalize_whitespace( $left ); | |
| 87 | - $right = normalize_whitespace( $right ); | |
| 88 | - | |
| 89 | - $left_lines = split( "\n", $left ); | |
| 90 | - $right_lines = split( "\n", $right ); | |
| 91 | - | |
| 92 | - require_once( dirname( __FILE__ ) . '/unified.php' ); | |
| 93 | - | |
| 94 | - $text_diff = new Text_Diff( $left_lines, $right_lines ); | |
| 95 | - $renderer = new Text_Diff_Renderer_unified(); | |
| 96 | - $text_diffs[$field_title] = $renderer->render($text_diff); | |
| 97 | - | |
| 98 | - $identical = false; | |
| 99 | - } | |
| 100 | - | |
| 101 | - if ( $identical ) | |
| 102 | - return; | |
| 103 | - | |
| 104 | - // Grab the meta data | |
| 105 | - $the_author = get_the_author_meta( 'display_name', $this->right_post->post_author ); | |
| 106 | - $the_title = get_the_title( $this->right_post->ID ); | |
| 107 | - $right_date = DateTime::createFromFormat( 'Y-m-d H:i:s', $this->right_post->post_modified_gmt, new DateTimeZone( 'UTC' ) ); | |
| 108 | - $the_date = $right_date->format( 'j F, Y \a\t G:i \U\T\C' ); | |
| 109 | - $the_permalink = clean_url( get_permalink( $this->right_post->ID ) ); | |
| 110 | - $the_edit_link = clean_url( get_edit_post_link( $this->right_post->ID ) ); | |
| 111 | - | |
| 112 | - $left_title = __( 'Revision' ); | |
| 113 | - $right_title = sprintf( __( 'Current %s' ), $post_type = ucfirst( $this->right_post->post_type ) ); | |
| 114 | - | |
| 115 | - $head_sprintf = __( '%s made the following changes to the %s %s on %s' ); | |
| 116 | - | |
| 117 | - | |
| 118 | - // HTML | |
| 119 | - $html_diff_head = '<h2>' . sprintf( __( '%s changed' ), $post_type ) . "</h2>\n"; | |
| 120 | - $html_diff_head .= '<p>' . sprintf( $head_sprintf, | |
| 121 | - esc_html( $the_author ), | |
| 122 | - sprintf( _x( '“%s” [%s]', '1 = link, 2 = "edit"' ), | |
| 123 | - "<a href='$the_permalink'>" . esc_html( $the_title ) . '</a>', | |
| 124 | - "<a href='$the_edit_link'>" . __( 'edit' ) . '</a>' | |
| 125 | - ), | |
| 126 | - $this->right_post->post_type, | |
| 127 | - $the_date | |
| 128 | - ) . "</p>\n\n"; | |
| 129 | - | |
| 130 | - $html_diff_head .= "<table style='width: 100%; border-collapse: collapse; border: none;'><tr>\n"; | |
| 131 | - $html_diff_head .= "<td style='width: 50%; padding: 0; margin: 0;'>" . esc_html( $left_title ) . ' @ ' . esc_html( $this->left_post->post_date_gmt ) . "</td>\n"; | |
| 132 | - $html_diff_head .= "<td style='width: 50%; padding: 0; margin: 0;'>" . esc_html( $right_title ) . ' @ ' . esc_html( $this->left_post->post_modified_gmt ) . "</td>\n"; | |
| 133 | - $html_diff_head .= "</tr></table>\n\n"; | |
| 134 | - | |
| 135 | - $html_diff = ''; | |
| 136 | - foreach ( $html_diffs as $field_title => $diff ) { | |
| 137 | - $html_diff .= '<h3>' . esc_html( $field_title ) . "</h3>\n"; | |
| 138 | - $html_diff .= "$diff\n\n"; | |
| 139 | - } | |
| 140 | - | |
| 141 | - $html_diff = rtrim( $html_diff ); | |
| 142 | - | |
| 143 | - // Replace classes with inline style | |
| 144 | - $html_diff = str_replace( "class='diff'", 'style="width: 100%; border-collapse: collapse; border: none; white-space: pre-wrap; word-wrap: break-word; font-family: Consolas,Monaco,Courier,monospace;"', $html_diff ); | |
| 145 | - $html_diff = preg_replace( '#<col[^>]+/?>#i', '', $html_diff ); | |
| 146 | - $html_diff = str_replace( "class='diff-deletedline'", 'style="padding: 5px; width: 50%; background-color: #fdd;"', $html_diff ); | |
| 147 | - $html_diff = str_replace( "class='diff-addedline'", 'style="padding: 5px; width: 50%; background-color: #dfd;"', $html_diff ); | |
| 148 | - $html_diff = str_replace( "class='diff-context'", 'style="padding: 5px; width: 50%;"', $html_diff ); | |
| 149 | - $html_diff = str_replace( '<td>', '<td style="padding: 5px;">', $html_diff ); | |
| 150 | - $html_diff = str_replace( '<del>', '<del style="text-decoration: none; background-color: #f99;">', $html_diff ); | |
| 151 | - $html_diff = str_replace( '<ins>', '<ins style="text-decoration: none; background-color: #9f9;">', $html_diff ); | |
| 152 | - $html_diff = str_replace( array( '</td>', '</tr>', '</tbody>' ), array( "</td>\n", "</tr>\n", "</tbody>\n" ), $html_diff ); | |
| 153 | - | |
| 154 | - $html_diff = $html_diff_head . $html_diff; | |
| 155 | - | |
| 156 | - | |
| 157 | - // Refactor some of the meta data for TEXT | |
| 158 | - $length = max( strlen( $left_title ), strlen( $right_title ) ); | |
| 159 | - $left_title = str_pad( $left_title, $length + 2 ); | |
| 160 | - $right_title = str_pad( $right_title, $length + 2 ); | |
| 161 | - | |
| 162 | - // TEXT | |
| 163 | - $text_diff = sprintf( $head_sprintf, $the_author, '"' . $the_title . '"', $this->right_post->post_type, $the_date ) . "\n"; | |
| 164 | - $text_diff .= "URL: $the_permalink\n"; | |
| 165 | - $text_diff .= "Edit: $the_edit_link\n\n"; | |
| 166 | - | |
| 167 | - foreach ( $text_diffs as $field_title => $diff ) { | |
| 168 | - $text_diff .= "$field_title\n"; | |
| 169 | - $text_diff .= "===================================================================\n"; | |
| 170 | - $text_diff .= "--- $left_title ({$this->left_post->post_date_gmt})\n"; | |
| 171 | - $text_diff .= "+++ $right_title ({$this->right_post->post_modified_gmt})\n"; | |
| 172 | - $text_diff .= "$diff\n\n"; | |
| 173 | - } | |
| 174 | - | |
| 175 | - $this->text_diff = $text_diff = rtrim( $text_diff ); | |
| 176 | - | |
| 177 | - | |
| 178 | - // Send email | |
| 179 | - $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); | |
| 180 | - $title = wp_specialchars_decode( $the_title, ENT_QUOTES ); | |
| 181 | - | |
| 182 | - add_action( 'phpmailer_init', array( &$this, 'phpmailer_init_once' ) ); | |
| 183 | - | |
| 184 | - wp_mail( | |
| 185 | - null, // see hack in ::phpmailer_init_once() | |
| 186 | - sprintf( __( '[%s] %s changed: %s' ), $blogname, $post_type, $title ), | |
| 187 | - $html_diff | |
| 188 | - ); | |
| 189 | - } | |
| 190 | - | |
| 191 | - /* Email hook */ | |
| 192 | - function phpmailer_init_once( &$phpmailer ) { | |
| 193 | - remove_action( 'phpmailer_init', array( &$this, 'phpmailer_init_once' ) ); | |
| 194 | - $phpmailer->AltBody = $this->text_diff; | |
| 195 | - | |
| 196 | - $phpmailer->to = array(); // Hack | |
| 197 | - | |
| 198 | - $options = $this->get_options(); | |
| 199 | - foreach ( $options['emails'] as $email ) | |
| 200 | - $phpmailer->AddBCC( $email ); | |
| 201 | - | |
| 202 | - $phpmailer->AddReplyTo( | |
| 203 | - get_the_author_meta( 'email', $this->right_post->post_author ), | |
| 204 | - get_the_author_meta( 'display_name', $this->right_post->post_author ) | |
| 205 | - ); | |
| 206 | - } | |
| 207 | - | |
| 208 | - /* Admin */ | |
| 209 | - function admin_menu() { | |
| 210 | - register_setting( self::OPTION_GROUP, self::OPTION, array( &$this, 'validate_options' ) ); | |
| 211 | - | |
| 212 | - add_settings_section( self::ADMIN_PAGE, __( 'Email Post Changes' ), array( &$this, 'settings_section' ), self::ADMIN_PAGE ); | |
| 213 | - add_settings_field( self::ADMIN_PAGE . '_emails', __( 'Email Addresses' ), array( &$this, 'emails_setting' ), self::ADMIN_PAGE, self::ADMIN_PAGE ); | |
| 214 | - add_settings_field( self::ADMIN_PAGE . '_post_types', __( 'Post Types' ), array( &$this, 'post_types_setting' ), self::ADMIN_PAGE, self::ADMIN_PAGE ); | |
| 215 | - | |
| 216 | - add_options_page( __( 'Email Post Changes' ), __( 'Email Post Changes' ), 'manage_options', self::ADMIN_PAGE, array( &$this, 'admin_page' ) ); | |
| 217 | - | |
| 218 | - add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( &$this, 'plugin_action_links' ) ); | |
| 219 | - } | |
| 220 | - | |
| 221 | - function validate_options( $options ) { | |
| 222 | - if ( !$options || !is_array( $options ) ) | |
| 223 | - return $this->defaults; | |
| 224 | - | |
| 225 | - $return = array(); | |
| 226 | - | |
| 227 | - if ( empty( $options['emails'] ) ) { | |
| 228 | - $return['emails'] = $this->defaults['emails']; | |
| 229 | - } else { | |
| 230 | - $_emails = array_unique( preg_split( '(\n|\r)', $options['emails'], -1, PREG_SPLIT_NO_EMPTY ) ); | |
| 231 | - $emails = array_filter( $_emails, 'is_email' ); | |
| 232 | - if ( $diff = array_diff( $_emails, $emails ) ) | |
| 233 | - $return['invalid_emails'] = $diff; | |
| 234 | - $return['emails'] = $emails ? $emails : $this->defaults['emails']; | |
| 235 | - } | |
| 236 | - | |
| 237 | - if ( empty( $options['post_types'] ) || !is_array( $options ) ) { | |
| 238 | - $return['post_types'] = $this->defaults['post_types']; | |
| 239 | - } else { | |
| 240 | - $post_types = array_intersect( $options['post_types'], $this->get_post_types() ); | |
| 241 | - $return['post_types'] = $post_types ? $post_types : $this->defaults['post_types']; | |
| 242 | - } | |
| 243 | - | |
| 244 | - return $return; | |
| 245 | - } | |
| 246 | - | |
| 247 | - function admin_page() { | |
| 248 | - $options = $this->get_options(); | |
| 249 | -?> | |
| 250 | - | |
| 251 | -<div class="wrap"> | |
| 252 | - <h2><?php _e( 'Email Post Changes' ); ?></h2> | |
| 253 | -<?php if ( !empty( $options['invalid_emails'] ) ) : ?> | |
| 254 | - | |
| 255 | - <div class="error"> | |
| 256 | - <p><?php printf( _n( 'Invalid Email: %s', 'Invalid Emails: %s', count( $options['invalid_emails'] ) ), '<kbd>' . join( '</kbd>, <kbd>', array_map( 'esc_html', $options['invalid_emails'] ) ) ); ?></p> | |
| 257 | - </div> | |
| 258 | -<?php endif; ?> | |
| 259 | - | |
| 260 | - <form action="options.php" method="post"> | |
| 261 | - <?php settings_fields( self::OPTION_GROUP ); ?> | |
| 262 | - <?php do_settings_sections( self::ADMIN_PAGE ); ?> | |
| 263 | - <p class="submit"> | |
| 264 | - <input type="submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" /> | |
| 265 | - </p> | |
| 266 | - </form> | |
| 267 | -</div> | |
| 268 | -<?php | |
| 269 | - } | |
| 270 | - | |
| 271 | - function settings_section() {} // stub | |
| 272 | - | |
| 273 | - function emails_setting() { | |
| 274 | - $options = $this->get_options(); | |
| 275 | -?> | |
| 276 | - <textarea rows="4" cols="40" style="width: 40em;" name="email_post_changes[emails]"><?php echo esc_html( join( "\n", $options['emails'] ) ); ?></textarea> | |
| 277 | - <p class="description"><?php _e( 'Send emails to these addresses. One per line.' ); ?></p> | |
| 278 | - <p class="description"><?php printf( | |
| 279 | - __( "If blank, send emails to this site’s <a href='%s'>admin email address</a>." ), | |
| 280 | - clean_url( get_bloginfo( 'wpurl' ) . '/wp-admin/options-general.php#admin_email' ) | |
| 281 | - ); ?></p> | |
| 282 | -<?php | |
| 283 | - } | |
| 284 | - | |
| 285 | - function post_types_setting() { | |
| 286 | - $options = $this->get_options(); | |
| 287 | -?> | |
| 288 | - | |
| 289 | - <ul> | |
| 290 | -<?php foreach ( $this->get_post_types() as $post_type ) : ?> | |
| 291 | - | |
| 292 | - <li><label><input type="checkbox" name="email_post_changes[post_types][]" value="<?php echo esc_attr( $post_type ); ?>"<?php checked( in_array( $post_type, $options['post_types'] ) ); ?> /> <?php echo esc_html( ucfirst( $post_type ) ); ?></label></li> | |
| 293 | -<?php endforeach; ?> | |
| 294 | - | |
| 295 | - </ul> | |
| 296 | - <p class="description"><?php _e( 'Send emails when a post of these post types changes.' ); ?></p> | |
| 297 | - <p class="description"><?php _e( 'If none are selected, “post” and “page” will be checked by default.' ); ?></p> | |
| 298 | -<?php | |
| 299 | - } | |
| 300 | - | |
| 301 | - function plugin_action_links( $links ) { | |
| 302 | - array_unshift( $links, '<a href="options-general.php?page=' . self::ADMIN_PAGE . '">' . __( 'Settings' ) . "</a>" ); | |
| 303 | - return $links; | |
| 304 | - } | |
| 305 | -} | |
| 306 | - | |
| 307 | -add_action( 'init', array( 'Email_Post_Changes', 'init' ) ); | |