PluginProbe
Email Post Changes / 0.4
Email Post Changes v0.4
trunk 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.7.1 1.7.2
email-post-changes / email-post-changes.php

email-post-changes.php in Email Post Changes 0.4, at email-post-changes.php

310 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
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.
6 Plugin URI: http://wordpress.org/extend/plugins/email-post-changes/
7 Version: 0.4
8 Author: Michael D Adams
9 Author URI: http://blogwaffe.com/
10 */
11
12 class Email_Post_Changes {
13 var $defaults;
14
15 var $left_post;
16 var $right_post;
17
18 var $text_diff;
19
20 const ADMIN_PAGE = 'email_post_changes';
21 const OPTION_GROUP = 'email_post_changes';
22 const OPTION = 'email_post_changes';
23
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->left_post->post_author ); // The revision
106 $the_title = get_the_title( $this->right_post->ID ); // New title (may be same as old title)
107 $right_date = new DateTime( $this->right_post->post_modified_gmt, new DateTimeZone( 'UTC' ) ); // Modified time
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( '&#8220;%s&#8221; [%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->right_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 if ( is_string( $options['emails'] ) )
231 $_emails = preg_split( '(\n|\r)', $options['emails'], -1, PREG_SPLIT_NO_EMPTY );
232 $_emails = array_unique( (array) $_emails );
233 $emails = array_filter( $_emails, 'is_email' );
234 if ( $diff = array_diff( $_emails, $emails ) )
235 $return['invalid_emails'] = $diff;
236 $return['emails'] = $emails ? $emails : $this->defaults['emails'];
237 }
238
239 if ( empty( $options['post_types'] ) || !is_array( $options ) ) {
240 $return['post_types'] = $this->defaults['post_types'];
241 } else {
242 $post_types = array_intersect( $options['post_types'], $this->get_post_types() );
243 $return['post_types'] = $post_types ? $post_types : $this->defaults['post_types'];
244 }
245
246 return $return;
247 }
248
249 function admin_page() {
250 $options = $this->get_options();
251 ?>
252
253 <div class="wrap">
254 <h2><?php _e( 'Email Post Changes' ); ?></h2>
255 <?php if ( !empty( $options['invalid_emails'] ) ) : ?>
256
257 <div class="error">
258 <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>
259 </div>
260 <?php endif; ?>
261
262 <form action="options.php" method="post">
263 <?php settings_fields( self::OPTION_GROUP ); ?>
264 <?php do_settings_sections( self::ADMIN_PAGE ); ?>
265 <p class="submit">
266 <input type="submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
267 </p>
268 </form>
269 </div>
270 <?php
271 }
272
273 function settings_section() {} // stub
274
275 function emails_setting() {
276 $options = $this->get_options();
277 ?>
278 <textarea rows="4" cols="40" style="width: 40em;" name="email_post_changes[emails]"><?php echo esc_html( join( "\n", $options['emails'] ) ); ?></textarea>
279 <p class="description"><?php _e( 'Send emails to these addresses. One per line.' ); ?></p>
280 <p class="description"><?php printf(
281 __( "If blank, send emails to this site&#8217;s <a href='%s'>admin email address</a>." ),
282 clean_url( get_bloginfo( 'wpurl' ) . '/wp-admin/options-general.php#admin_email' )
283 ); ?></p>
284 <?php
285 }
286
287 function post_types_setting() {
288 $options = $this->get_options();
289 ?>
290
291 <ul>
292 <?php foreach ( $this->get_post_types() as $post_type ) : ?>
293
294 <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>
295 <?php endforeach; ?>
296
297 </ul>
298 <p class="description"><?php _e( 'Send emails when a post of these post types changes.' ); ?></p>
299 <p class="description"><?php _e( 'If none are selected, &#8220;post&#8221; and &#8220;page&#8221; will be checked by default.' ); ?></p>
300 <?php
301 }
302
303 function plugin_action_links( $links ) {
304 array_unshift( $links, '<a href="options-general.php?page=' . self::ADMIN_PAGE . '">' . __( 'Settings' ) . "</a>" );
305 return $links;
306 }
307 }
308
309 add_action( 'init', array( 'Email_Post_Changes', 'init' ) );
310