PluginProbe
Email Post Changes / 0.6
Email Post Changes v0.6
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 / class.email-post-changes.php

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

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