PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 10.5.3
Jetpack – WP Security, Backup, Speed, & Growth v10.5.3
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / modules / contact-form / admin.php
admin.php
1,009 lines 29.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\Jetpack\Assets;
4
5 /**
6 * Add a contact form button to the post composition screen
7 */
8 add_action( 'media_buttons', 'grunion_media_button', 999 );
9 function grunion_media_button() {
10 global $post_ID, $temp_ID, $pagenow;
11
12 if ( 'press-this.php' === $pagenow ) {
13 return;
14 }
15
16 $iframe_post_id = (int) ( 0 == $post_ID ? $temp_ID : $post_ID );
17 $title = __( 'Add Contact Form', 'jetpack' );
18 $plugin_url = esc_url( GRUNION_PLUGIN_URL );
19 $site_url = esc_url( admin_url( "/admin-ajax.php?post_id={$iframe_post_id}&action=grunion_form_builder&TB_iframe=true&width=768" ) );
20 ?>
21
22 <a id="insert-jetpack-contact-form" class="button thickbox" title="<?php echo esc_attr( $title ); ?>" data-editor="content" href="<?php echo $site_url; ?>&id=add_form">
23 <span class="jetpack-contact-form-icon"></span> <?php echo esc_html( $title ); ?>
24 </a>
25
26 <?php
27 }
28
29 add_action( 'wp_ajax_grunion_form_builder', 'grunion_display_form_view' );
30
31 function grunion_display_form_view() {
32 if ( current_user_can( 'edit_posts' ) ) {
33 require_once GRUNION_PLUGIN_DIR . 'grunion-form-view.php';
34 }
35 exit;
36 }
37
38 // feedback specific css items
39 add_action( 'admin_print_styles', 'grunion_admin_css' );
40 function grunion_admin_css() {
41 global $current_screen;
42 if ( is_null( $current_screen ) ) {
43 return;
44 }
45 if ( 'edit-feedback' !== $current_screen->id ) {
46 return;
47 }
48
49 wp_enqueue_script( 'wp-lists' );
50 ?>
51
52 <style type='text/css'>
53 .add-new-h2, .view-switch, body.no-js .tablenav select[name^=action], body.no-js #doaction, body.no-js #doaction2 {
54 display: none
55 }
56
57 .column-feedback_from img {
58 float:left;
59 margin-right:10px;
60 margin-top:3px;
61 }
62
63 .widefat .column-feedback_from {
64 width: 17%;
65 }
66 .widefat .column-feedback_date {
67 width: 17%;
68 }
69
70 .spam a {
71 color: #BC0B0B;
72 }
73
74 .untrash a {
75 color: #D98500;
76 }
77
78 .unspam a {
79 color: #D98500;
80 }
81
82 </style>
83
84 <?php
85 }
86
87 /**
88 * Hack a 'Bulk Spam' option for bulk edit in other than spam view
89 * Hack a 'Bulk Delete' option for bulk edit in spam view
90 *
91 * There isn't a better way to do this until
92 * https://core.trac.wordpress.org/changeset/17297 is resolved
93 */
94 add_action( 'admin_head', 'grunion_add_bulk_edit_option' );
95 function grunion_add_bulk_edit_option() {
96
97 $screen = get_current_screen();
98
99 if ( is_null( $screen ) ) {
100 return;
101 }
102
103 if ( 'edit-feedback' != $screen->id ) {
104 return;
105 }
106
107 // When viewing spam we want to be able to be able to bulk delete
108 // When viewing anything we want to be able to bulk move to spam
109 if ( isset( $_GET['post_status'] ) && 'spam' == $_GET['post_status'] ) {
110 // Create Delete Permanently bulk item
111 $option_val = 'delete';
112 $option_txt = __( 'Delete Permanently', 'jetpack' );
113 $pseudo_selector = 'last-child';
114
115 } else {
116 // Create Mark Spam bulk item
117 $option_val = 'spam';
118 $option_txt = __( 'Mark as Spam', 'jetpack' );
119 $pseudo_selector = 'first-child';
120 }
121
122 ?>
123 <script type="text/javascript">
124 jQuery(document).ready(function($) {
125 $('#posts-filter .actions select').filter('[name=action], [name=action2]').find('option:<?php echo $pseudo_selector; ?>').after('<option value="<?php echo $option_val; ?>"><?php echo esc_attr( $option_txt ); ?></option>' );
126 })
127 </script>
128 <?php
129 }
130
131 /**
132 * Handle a bulk spam report
133 */
134 add_action( 'admin_init', 'grunion_handle_bulk_spam' );
135 function grunion_handle_bulk_spam() {
136 global $pagenow;
137
138 if ( 'edit.php' != $pagenow
139 || ( empty( $_REQUEST['post_type'] ) || 'feedback' != $_REQUEST['post_type'] ) ) {
140 return;
141 }
142
143 // Slip in a success message
144 if ( ! empty( $_REQUEST['message'] ) && 'marked-spam' == $_REQUEST['message'] ) {
145 add_action( 'admin_notices', 'grunion_message_bulk_spam' );
146 }
147
148 if ( ( empty( $_REQUEST['action'] ) || 'spam' != $_REQUEST['action'] ) && ( empty( $_REQUEST['action2'] ) || 'spam' != $_REQUEST['action2'] ) ) {
149 return;
150 }
151
152 check_admin_referer( 'bulk-posts' );
153
154 if ( empty( $_REQUEST['post'] ) ) {
155 wp_safe_redirect( wp_get_referer() );
156 exit;
157 }
158
159 $post_ids = array_map( 'intval', $_REQUEST['post'] );
160
161 foreach ( $post_ids as $post_id ) {
162 if ( ! current_user_can( 'edit_page', $post_id ) ) {
163 wp_die( __( 'You are not allowed to manage this item.', 'jetpack' ) );
164 }
165
166 $post = array(
167 'ID' => $post_id,
168 'post_status' => 'spam',
169 );
170 $akismet_values = get_post_meta( $post_id, '_feedback_akismet_values', true );
171 wp_update_post( $post );
172
173 /**
174 * Fires after a comment has been marked by Akismet.
175 *
176 * Typically this means the comment is spam.
177 *
178 * @module contact-form
179 *
180 * @since 2.2.0
181 *
182 * @param string $comment_status Usually is 'spam', otherwise 'ham'.
183 * @param array $akismet_values From '_feedback_akismet_values' in comment meta
184 */
185 do_action( 'contact_form_akismet', 'spam', $akismet_values );
186 }
187
188 $redirect_url = add_query_arg( 'message', 'marked-spam', wp_get_referer() );
189 wp_safe_redirect( $redirect_url );
190 exit;
191 }
192
193 function grunion_message_bulk_spam() {
194 echo '<div class="updated"><p>' . __( 'Feedback(s) marked as spam', 'jetpack' ) . '</p></div>';
195 }
196
197 add_filter( 'bulk_actions-edit-feedback', 'grunion_admin_bulk_actions' );
198 function grunion_admin_bulk_actions( $actions ) {
199 global $current_screen;
200 if ( 'edit-feedback' != $current_screen->id ) {
201 return $actions;
202 }
203
204 unset( $actions['edit'] );
205 return $actions;
206 }
207
208 add_filter( 'views_edit-feedback', 'grunion_admin_view_tabs' );
209 function grunion_admin_view_tabs( $views ) {
210 global $current_screen;
211 if ( 'edit-feedback' != $current_screen->id ) {
212 return $views;
213 }
214
215 unset( $views['publish'] );
216
217 preg_match( '|post_type=feedback\'( class="current")?\>(.*)\<span class=|', $views['all'], $match );
218 if ( ! empty( $match[2] ) ) {
219 $views['all'] = str_replace( $match[2], __( 'Messages', 'jetpack' ) . ' ', $views['all'] );
220 }
221
222 return $views;
223 }
224
225 add_filter( 'manage_feedback_posts_columns', 'grunion_post_type_columns_filter' );
226 function grunion_post_type_columns_filter( $cols ) {
227 $cols = array(
228 'cb' => '<input type="checkbox" />',
229 'feedback_from' => __( 'From', 'jetpack' ),
230 'feedback_message' => __( 'Message', 'jetpack' ),
231 'feedback_date' => __( 'Date', 'jetpack' ),
232 );
233
234 return $cols;
235 }
236
237 add_action( 'manage_posts_custom_column', 'grunion_manage_post_columns', 10, 2 );
238 function grunion_manage_post_columns( $col, $post_id ) {
239 global $post;
240
241 /**
242 * Only call parse_fields_from_content if we're dealing with a Grunion custom column.
243 */
244 if ( ! in_array( $col, array( 'feedback_date', 'feedback_from', 'feedback_message' ) ) ) {
245 return;
246 }
247
248 $content_fields = Grunion_Contact_Form_Plugin::parse_fields_from_content( $post_id );
249
250 switch ( $col ) {
251 case 'feedback_from':
252 $author_name = isset( $content_fields['_feedback_author'] ) ? $content_fields['_feedback_author'] : '';
253 $author_email = isset( $content_fields['_feedback_author_email'] ) ? $content_fields['_feedback_author_email'] : '';
254 $author_url = isset( $content_fields['_feedback_author_url'] ) ? $content_fields['_feedback_author_url'] : '';
255 $author_ip = isset( $content_fields['_feedback_ip'] ) ? $content_fields['_feedback_ip'] : '';
256 $form_url = isset( $post->post_parent ) ? get_permalink( $post->post_parent ) : null;
257
258 $author_name_line = '';
259 if ( ! empty( $author_name ) ) {
260 if ( ! empty( $author_email ) ) {
261 $author_name_line = get_avatar( $author_email, 32 );
262 }
263
264 $author_name_line .= sprintf( '<strong>%s</strong><br />', esc_html( $author_name ) );
265 }
266
267 $author_email_line = '';
268 if ( ! empty( $author_email ) ) {
269 $author_email_line = sprintf( "<a href='%1\$s' target='_blank'>%2\$s</a><br />", esc_url( 'mailto:' . $author_email ), esc_html( $author_email ) );
270 }
271
272 $author_url_line = '';
273 if ( ! empty( $author_url ) ) {
274 $author_url_line = sprintf( "<a href='%1\$s'>%1\$s</a><br />", esc_url( $author_url ) );
275 }
276
277 echo $author_name_line;
278 echo $author_email_line;
279 echo $author_url_line;
280 echo "<a href='edit.php?post_type=feedback&s=" . urlencode( $author_ip );
281 echo "&mode=detail'>" . esc_html( $author_ip ) . '</a><br />';
282 if ( $form_url ) {
283 echo '<a href="' . esc_url( $form_url ) . '">' . esc_html( $form_url ) . '</a>';
284 }
285 break;
286
287 case 'feedback_message':
288 $post_type_object = get_post_type_object( $post->post_type );
289 if ( isset( $content_fields['_feedback_subject'] ) ) {
290 echo '<strong>';
291 echo esc_html( $content_fields['_feedback_subject'] );
292 echo '</strong>';
293 echo '<br />';
294 }
295 echo sanitize_text_field( get_the_content( '' ) );
296 echo '<br />';
297
298 $extra_fields = get_post_meta( $post_id, '_feedback_extra_fields', true );
299 if ( ! empty( $extra_fields ) ) {
300 echo '<br /><hr />';
301 echo '<table cellspacing="0" cellpadding="0" style="">' . "\n";
302 foreach ( (array) $extra_fields as $k => $v ) {
303 // Remove prefix from exta fields
304 echo "<tr><td align='right'><b>" . esc_html( preg_replace( '#^\d+_#', '', $k ) ) . '</b></td><td>' . sanitize_text_field( $v ) . "</td></tr>\n";
305 }
306 echo '</table>';
307 }
308
309 echo '<div class="row-actions">';
310 if ( $post->post_status == 'trash' ) {
311 echo '<span class="untrash" id="feedback-restore-' . $post_id;
312 echo '"><a title="';
313 echo esc_attr__( 'Restore this item from the Trash', 'jetpack' );
314 echo '" href="' . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post->ID ) ), 'untrash-' . $post->post_type . '_' . $post->ID );
315 echo '">' . __( 'Restore', 'jetpack' ) . '</a></span> | ';
316
317 echo "<span class='delete'> <a class='submitdelete' title='";
318 echo esc_attr( __( 'Delete this item permanently', 'jetpack' ) );
319 echo "' href='" . get_delete_post_link( $post->ID, '', true );
320 echo "'>" . __( 'Delete Permanently', 'jetpack' ) . '</a></span>';
321 ?>
322
323 <script>
324 jQuery(document).ready(function($) {
325 $('#feedback-restore-<?php echo $post_id; ?>').click(function(e) {
326 e.preventDefault();
327 $.post(ajaxurl, {
328 action: 'grunion_ajax_spam',
329 post_id: '<?php echo $post_id; ?>',
330 make_it: 'publish',
331 sub_menu: jQuery('.subsubsub .current').attr('href'),
332 _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
333 },
334 function(r) {
335 $('#post-<?php echo $post_id; ?>')
336 .css({backgroundColor: '#59C859'})
337 .fadeOut(350, function() {
338 $(this).remove();
339 $('.subsubsub').html(r);
340 });
341 }
342 );
343 });
344 });
345 </script>
346
347 <?php
348 } elseif ( $post->post_status == 'publish' ) {
349 echo '<span class="spam" id="feedback-spam-' . $post_id;
350 echo '"><a title="';
351 echo __( 'Mark this message as spam', 'jetpack' );
352 echo '" href="' . wp_nonce_url( admin_url( 'admin-ajax.php?post_id=' . $post_id . '&amp;action=spam' ), 'spam-feedback_' . $post_id );
353 echo '">Spam</a></span>';
354 echo ' | ';
355
356 echo '<span class="delete" id="feedback-trash-' . $post_id;
357 echo '">';
358 echo '<a class="submitdelete" title="' . esc_attr__( 'Trash', 'jetpack' );
359 echo '" href="' . get_delete_post_link( $post_id );
360 echo '">' . __( 'Trash', 'jetpack' ) . '</a></span>';
361
362 ?>
363
364 <script>
365 jQuery(document).ready( function($) {
366 $('#feedback-spam-<?php echo $post_id; ?>').click( function(e) {
367 e.preventDefault();
368 $.post( ajaxurl, {
369 action: 'grunion_ajax_spam',
370 post_id: '<?php echo $post_id; ?>',
371 make_it: 'spam',
372 sub_menu: jQuery('.subsubsub .current').attr('href'),
373 _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
374 },
375 function( r ) {
376 $('#post-<?php echo $post_id; ?>')
377 .css( {backgroundColor:'#FF7979'} )
378 .fadeOut(350, function() {
379 $(this).remove();
380 $('.subsubsub').html(r);
381 });
382 });
383 });
384
385 $('#feedback-trash-<?php echo $post_id; ?>').click(function(e) {
386 e.preventDefault();
387 $.post(ajaxurl, {
388 action: 'grunion_ajax_spam',
389 post_id: '<?php echo $post_id; ?>',
390 make_it: 'trash',
391 sub_menu: jQuery('.subsubsub .current').attr('href'),
392 _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
393 },
394 function(r) {
395 $('#post-<?php echo $post_id; ?>')
396 .css({backgroundColor: '#FF7979'})
397 .fadeOut(350, function() {
398 $(this).remove();
399 $('.subsubsub').html(r);
400 });
401 }
402 );
403 });
404 });
405 </script>
406
407 <?php
408 } elseif ( $post->post_status == 'spam' ) {
409 echo '<span class="unspam unapprove" id="feedback-ham-' . $post_id;
410 echo '"><a title="';
411 echo __( 'Mark this message as NOT spam', 'jetpack' );
412 echo '" href="">Not Spam</a></span>';
413 echo ' | ';
414
415 echo "<span class='delete' id='feedback-trash-" . $post_id;
416 echo "'> <a class='submitdelete' title='";
417 echo esc_attr( __( 'Delete this item permanently', 'jetpack' ) );
418 echo "' href='" . get_delete_post_link( $post->ID, '', true );
419 echo "'>" . __( 'Delete Permanently', 'jetpack' ) . '</a></span>';
420 ?>
421
422 <script>
423 jQuery(document).ready( function($) {
424 $('#feedback-ham-<?php echo $post_id; ?>').click( function(e) {
425 e.preventDefault();
426 $.post( ajaxurl, {
427 action: 'grunion_ajax_spam',
428 post_id: '<?php echo $post_id; ?>',
429 make_it: 'ham',
430 sub_menu: jQuery('.subsubsub .current').attr('href'),
431 _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
432 },
433 function( r ) {
434 $('#post-<?php echo $post_id; ?>')
435 .css( {backgroundColor:'#59C859'} )
436 .fadeOut(350, function() {
437 $(this).remove();
438 $('.subsubsub').html(r);
439 });
440 });
441 });
442 });
443 </script>
444
445 <?php
446 }
447 break;
448
449 case 'feedback_date':
450 $date_time_format = _x( '%1$s \a\t %2$s', '{$date_format} \a\t {$time_format}', 'jetpack' );
451 $date_time_format = sprintf( $date_time_format, get_option( 'date_format' ), get_option( 'time_format' ) );
452 $time = date_i18n( $date_time_format, get_the_time( 'U' ) );
453
454 echo $time;
455 break;
456 }
457 }
458
459 function grunion_esc_attr( $attr ) {
460 $out = esc_attr( $attr );
461 // we also have to entity-encode square brackets so they don't interfere with the shortcode parser
462 // FIXME: do this better - just stripping out square brackets for now since they mysteriously keep reappearing
463 $out = str_replace( '[', '', $out );
464 $out = str_replace( ']', '', $out );
465 return $out;
466 }
467
468 function grunion_sort_objects( $a, $b ) {
469 if ( isset( $a['order'] ) && isset( $b['order'] ) ) {
470 return $a['order'] - $b['order'];
471 }
472 return 0;
473 }
474
475 // take an array of field types from the form builder, and construct a shortcode form
476 // returns both the shortcode form, and HTML markup representing a preview of the form
477 function grunion_ajax_shortcode() {
478 check_ajax_referer( 'grunion_shortcode' );
479
480 if ( ! current_user_can( 'edit_posts' ) ) {
481 die( '-1' );
482 }
483
484 $attributes = array();
485
486 foreach ( array( 'subject', 'to' ) as $attribute ) {
487 if ( isset( $_POST[ $attribute ] ) && strlen( $_POST[ $attribute ] ) ) {
488 $attributes[ $attribute ] = stripslashes( $_POST[ $attribute ] );
489 }
490 }
491
492 if ( is_array( $_POST['fields'] ) ) {
493 $fields = stripslashes_deep( $_POST['fields'] );
494 usort( $fields, 'grunion_sort_objects' );
495
496 $field_shortcodes = array();
497
498 foreach ( $fields as $field ) {
499 $field_attributes = array();
500
501 if ( isset( $field['required'] ) && 'true' === $field['required'] ) {
502 $field_attributes['required'] = 'true';
503 }
504
505 foreach ( array( 'options', 'label', 'type' ) as $attribute ) {
506 if ( isset( $field[ $attribute ] ) ) {
507 $field_attributes[ $attribute ] = $field[ $attribute ];
508 }
509 }
510
511 $field_shortcodes[] = new Grunion_Contact_Form_Field( $field_attributes );
512 }
513 }
514
515 $grunion = new Grunion_Contact_Form( $attributes, $field_shortcodes );
516
517 die( "\n$grunion\n" );
518 }
519
520 // takes a post_id, extracts the contact-form shortcode from that post (if there is one), parses it,
521 // and constructs a json object representing its contents and attributes
522 function grunion_ajax_shortcode_to_json() {
523 global $post, $grunion_form;
524
525 check_ajax_referer( 'grunion_shortcode_to_json' );
526
527 if ( ! empty( $_POST['post_id'] ) && ! current_user_can( 'edit_post', $_POST['post_id'] ) ) {
528 die( '-1' );
529 } elseif ( ! current_user_can( 'edit_posts' ) ) {
530 die( '-1' );
531 }
532
533 if ( ! isset( $_POST['content'] ) || ! is_numeric( $_POST['post_id'] ) ) {
534 die( '-1' );
535 }
536
537 $content = stripslashes( $_POST['content'] );
538
539 // doesn't look like a post with a [contact-form] already.
540 if ( false === has_shortcode( $content, 'contact-form' ) ) {
541 die( '' );
542 }
543
544 $post = get_post( $_POST['post_id'] );
545
546 do_shortcode( $content );
547
548 $grunion = Grunion_Contact_Form::$last;
549
550 $out = array(
551 'to' => '',
552 'subject' => '',
553 'fields' => array(),
554 );
555
556 foreach ( $grunion->fields as $field ) {
557 $out['fields'][ $field->get_attribute( 'id' ) ] = $field->attributes;
558 }
559
560 $to = $grunion->get_attribute( 'to' );
561 $subject = $grunion->get_attribute( 'subject' );
562 foreach ( array( 'to', 'subject' ) as $attribute ) {
563 $value = $grunion->get_attribute( $attribute );
564 if ( isset( $grunion->defaults[ $attribute ] ) && $value == $grunion->defaults[ $attribute ] ) {
565 $value = '';
566 }
567 $out[ $attribute ] = $value;
568 }
569
570 die( json_encode( $out ) );
571 }
572
573
574 add_action( 'wp_ajax_grunion_shortcode', 'grunion_ajax_shortcode' );
575 add_action( 'wp_ajax_grunion_shortcode_to_json', 'grunion_ajax_shortcode_to_json' );
576
577
578 // process row-action spam/not spam clicks
579 add_action( 'wp_ajax_grunion_ajax_spam', 'grunion_ajax_spam' );
580 function grunion_ajax_spam() {
581 global $wpdb;
582
583 if ( empty( $_POST['make_it'] ) ) {
584 return;
585 }
586
587 $post_id = (int) $_POST['post_id'];
588 check_ajax_referer( 'grunion-post-status-' . $post_id );
589 if ( ! current_user_can( 'edit_page', $post_id ) ) {
590 wp_die( __( 'You are not allowed to manage this item.', 'jetpack' ) );
591 }
592
593 require_once dirname( __FILE__ ) . '/grunion-contact-form.php';
594
595 $current_menu = '';
596 if ( isset( $_POST['sub_menu'] ) && preg_match( '|post_type=feedback|', $_POST['sub_menu'] ) ) {
597 if ( preg_match( '|post_status=spam|', $_POST['sub_menu'] ) ) {
598 $current_menu = 'spam';
599 } elseif ( preg_match( '|post_status=trash|', $_POST['sub_menu'] ) ) {
600 $current_menu = 'trash';
601 } else {
602 $current_menu = 'messages';
603 }
604 }
605
606 $post = get_post( $post_id );
607 $post_type_object = get_post_type_object( $post->post_type );
608 $akismet_values = get_post_meta( $post_id, '_feedback_akismet_values', true );
609 if ( $_POST['make_it'] == 'spam' ) {
610 $post->post_status = 'spam';
611 $status = wp_insert_post( $post );
612
613 /** This action is already documented in modules/contact-form/admin.php */
614 do_action( 'contact_form_akismet', 'spam', $akismet_values );
615 } elseif ( $_POST['make_it'] == 'ham' ) {
616 $post->post_status = 'publish';
617 $status = wp_insert_post( $post );
618
619 /** This action is already documented in modules/contact-form/admin.php */
620 do_action( 'contact_form_akismet', 'ham', $akismet_values );
621
622 $comment_author_email = $reply_to_addr = $message = $to = $headers = false;
623 $blog_url = wp_parse_url( site_url() );
624
625 // resend the original email
626 $email = get_post_meta( $post_id, '_feedback_email', true );
627 $content_fields = Grunion_Contact_Form_Plugin::parse_fields_from_content( $post_id );
628
629 if ( ! empty( $email ) && ! empty( $content_fields ) ) {
630 if ( isset( $content_fields['_feedback_author_email'] ) ) {
631 $comment_author_email = $content_fields['_feedback_author_email'];
632 }
633
634 if ( isset( $email['to'] ) ) {
635 $to = $email['to'];
636 }
637
638 if ( isset( $email['message'] ) ) {
639 $message = $email['message'];
640 }
641
642 if ( isset( $email['headers'] ) ) {
643 $headers = $email['headers'];
644 } else {
645 $headers = 'From: "' . $content_fields['_feedback_author'] . '" <wordpress@' . $blog_url['host'] . ">\r\n";
646
647 if ( ! empty( $comment_author_email ) ) {
648 $reply_to_addr = $comment_author_email;
649 } elseif ( is_array( $to ) ) {
650 $reply_to_addr = $to[0];
651 }
652
653 if ( $reply_to_addr ) {
654 $headers .= 'Reply-To: "' . $content_fields['_feedback_author'] . '" <' . $reply_to_addr . ">\r\n";
655 }
656
657 $headers .= 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . '"';
658 }
659
660 /**
661 * Filters the subject of the email sent after a contact form submission.
662 *
663 * @module contact-form
664 *
665 * @since 3.0.0
666 *
667 * @param string $content_fields['_feedback_subject'] Feedback's subject line.
668 * @param array $content_fields['_feedback_all_fields'] Feedback's data from old fields.
669 */
670 $subject = apply_filters( 'contact_form_subject', $content_fields['_feedback_subject'], $content_fields['_feedback_all_fields'] );
671
672 Grunion_Contact_Form::wp_mail( $to, $subject, $message, $headers );
673 }
674 } elseif ( $_POST['make_it'] == 'publish' ) {
675 if ( ! current_user_can( $post_type_object->cap->delete_post, $post_id ) ) {
676 wp_die( __( 'You are not allowed to move this item out of the Trash.', 'jetpack' ) );
677 }
678
679 if ( ! wp_untrash_post( $post_id ) ) {
680 wp_die( __( 'Error in restoring from Trash.', 'jetpack' ) );
681 }
682 } elseif ( $_POST['make_it'] == 'trash' ) {
683 if ( ! current_user_can( $post_type_object->cap->delete_post, $post_id ) ) {
684 wp_die( __( 'You are not allowed to move this item to the Trash.', 'jetpack' ) );
685 }
686
687 if ( ! wp_trash_post( $post_id ) ) {
688 wp_die( __( 'Error in moving to Trash.', 'jetpack' ) );
689 }
690 }
691
692 $sql = "
693 SELECT post_status,
694 COUNT( * ) AS post_count
695 FROM `{$wpdb->posts}`
696 WHERE post_type = 'feedback'
697 GROUP BY post_status
698 ";
699 $status_count = (array) $wpdb->get_results( $sql, ARRAY_A );
700
701 $status = array();
702 $status_html = '';
703 foreach ( $status_count as $i => $row ) {
704 $status[ $row['post_status'] ] = $row['post_count'];
705 }
706
707 if ( isset( $status['publish'] ) ) {
708 $status_html .= '<li><a href="edit.php?post_type=feedback"';
709 if ( $current_menu == 'messages' ) {
710 $status_html .= ' class="current"';
711 }
712
713 $status_html .= '>' . __( 'Messages', 'jetpack' ) . ' <span class="count">';
714 $status_html .= '(' . number_format( $status['publish'] ) . ')';
715 $status_html .= '</span></a> |</li>';
716 }
717
718 if ( isset( $status['trash'] ) ) {
719 $status_html .= '<li><a href="edit.php?post_status=trash&amp;post_type=feedback"';
720 if ( $current_menu == 'trash' ) {
721 $status_html .= ' class="current"';
722 }
723
724 $status_html .= '>' . __( 'Trash', 'jetpack' ) . ' <span class="count">';
725 $status_html .= '(' . number_format( $status['trash'] ) . ')';
726 $status_html .= '</span></a>';
727 if ( isset( $status['spam'] ) ) {
728 $status_html .= ' |';
729 }
730 $status_html .= '</li>';
731 }
732
733 if ( isset( $status['spam'] ) ) {
734 $status_html .= '<li><a href="edit.php?post_status=spam&amp;post_type=feedback"';
735 if ( $current_menu == 'spam' ) {
736 $status_html .= ' class="current"';
737 }
738
739 $status_html .= '>' . __( 'Spam', 'jetpack' ) . ' <span class="count">';
740 $status_html .= '(' . number_format( $status['spam'] ) . ')';
741 $status_html .= '</span></a></li>';
742 }
743
744 echo $status_html;
745 exit;
746 }
747
748 /**
749 * Add the scripts that will add the "Check for Spam" button to the Feedbacks dashboard page.
750 */
751 function grunion_enable_spam_recheck() {
752 if ( ! defined( 'AKISMET_VERSION' ) ) {
753 return;
754 }
755
756 $screen = get_current_screen();
757
758 // Only add to feedback, only to non-spam view
759 if ( 'edit-feedback' != $screen->id || ( ! empty( $_GET['post_status'] ) && 'spam' == $_GET['post_status'] ) ) {
760 return;
761 }
762
763 // Add the actual "Check for Spam" button.
764 add_action( 'admin_head', 'grunion_check_for_spam_button' );
765 }
766
767 add_action( 'admin_enqueue_scripts', 'grunion_enable_spam_recheck' );
768
769 /**
770 * Add the JS and CSS necessary for the Feedback admin page to function.
771 */
772 function grunion_add_admin_scripts() {
773 $screen = get_current_screen();
774
775 if ( 'edit-feedback' !== $screen->id ) {
776 return;
777 }
778
779 // Add the scripts that handle the spam check event.
780 wp_register_script(
781 'grunion-admin',
782 Assets::get_file_url_for_environment(
783 '_inc/build/contact-form/js/grunion-admin.min.js',
784 'modules/contact-form/js/grunion-admin.js'
785 ),
786 array( 'jquery' ),
787 JETPACK__VERSION,
788 true
789 );
790
791 wp_enqueue_script( 'grunion-admin' );
792
793 wp_enqueue_style( 'grunion.css' );
794
795 // Only add to feedback, only to spam view.
796 if ( empty( $_GET['post_status'] ) || 'spam' !== $_GET['post_status'] ) {
797 return;
798 }
799
800 $feedbacks_count = wp_count_posts( 'feedback' );
801 $nonce = wp_create_nonce( 'jetpack_delete_spam_feedbacks' );
802 $success_url = remove_query_arg( array( 'jetpack_empty_feedback_spam_error', 'post_status' ) ); // Go to the "All Feedback" page.
803 $failure_url = add_query_arg( 'jetpack_empty_feedback_spam_error', '1' ); // Refresh the current page and show an error.
804 $spam_count = $feedbacks_count->spam;
805
806 $button_parameters = array(
807 /* translators: The placeholder is for showing how much of the process has completed, as a percent. e.g., "Emptying Spam (40%)" */
808 'progress_label' => __( 'Emptying Spam (%1$s%)', 'jetpack' ),
809 'success_url' => $success_url,
810 'failure_url' => $failure_url,
811 'spam_count' => $spam_count,
812 'nonce' => $nonce,
813 'label' => __( 'Empty Spam', 'jetpack' ),
814 );
815
816 wp_localize_script( 'grunion-admin', 'jetpack_empty_spam_button_parameters', $button_parameters );
817 }
818
819 add_action( 'admin_enqueue_scripts', 'grunion_add_admin_scripts' );
820
821 /**
822 * Add the "Check for Spam" button to the Feedbacks dashboard page.
823 */
824 function grunion_check_for_spam_button() {
825 // Nonce name.
826 $nonce_name = 'jetpack_check_feedback_spam_' . (string) get_current_blog_id();
827 // Get HTML for the button.
828 $button_html = get_submit_button(
829 __( 'Check for Spam', 'jetpack' ),
830 'secondary',
831 'jetpack-check-feedback-spam',
832 false,
833 array(
834 'data-failure-url' => add_query_arg( 'jetpack_check_feedback_spam_error', '1' ), // Refresh the current page and show an error.
835 'data-nonce-name' => $nonce_name,
836 )
837 );
838 $button_html .= '<span class="jetpack-check-feedback-spam-spinner"></span>';
839 $button_html .= wp_nonce_field( 'grunion_recheck_queue', $nonce_name, false, false );
840
841 // Add the button next to the filter button via js.
842 ?>
843 <script type="text/javascript">
844 jQuery( function( $ ) {
845 $( '#posts-filter #post-query-submit' ).after( '<?php echo $button_html; ?>' );
846 } );
847 </script>
848 <?php
849 }
850
851 /**
852 * Recheck all approved feedbacks for spam.
853 */
854 function grunion_recheck_queue() {
855 $blog_id = get_current_blog_id();
856
857 if (
858 empty( $_POST[ 'jetpack_check_feedback_spam_' . (string) $blog_id ] )
859 || ! wp_verify_nonce( sanitize_key( $_POST[ 'jetpack_check_feedback_spam_' . (string) $blog_id ] ), 'grunion_recheck_queue' )
860 ) {
861 wp_send_json_error(
862 __( 'You aren’t authorized to do that.', 'jetpack' ),
863 403
864 );
865
866 return;
867 }
868
869 if ( ! current_user_can( 'delete_others_posts' ) ) {
870 wp_send_json_error(
871 __( 'You don’t have permission to do that.', 'jetpack' ),
872 403
873 );
874
875 return;
876 }
877
878 $query = 'post_type=feedback&post_status=publish';
879
880 if ( isset( $_POST['limit'], $_POST['offset'] ) ) {
881 $query .= '&posts_per_page=' . (int) $_POST['limit'] . '&offset=' . (int) $_POST['offset'];
882 }
883
884 $approved_feedbacks = get_posts( $query );
885
886 foreach ( $approved_feedbacks as $feedback ) {
887 $meta = get_post_meta( $feedback->ID, '_feedback_akismet_values', true );
888
889 if ( ! $meta ) {
890 // _feedback_akismet_values is eventually deleted when it's no longer
891 // within a reasonable time period to check the feedback for spam, so
892 // if it's gone, don't attempt a spam recheck.
893 continue;
894 }
895
896 $meta['recheck_reason'] = 'recheck_queue';
897
898 /**
899 * Filter whether the submitted feedback is considered as spam.
900 *
901 * @module contact-form
902 *
903 * @since 3.4.0
904 *
905 * @param bool false Is the submitted feedback spam? Default to false.
906 * @param array $meta Feedack values returned by the Akismet plugin.
907 */
908 $is_spam = apply_filters( 'jetpack_contact_form_is_spam', false, $meta );
909
910 if ( $is_spam ) {
911 wp_update_post(
912 array(
913 'ID' => $feedback->ID,
914 'post_status' => 'spam',
915 )
916 );
917 /** This action is already documented in modules/contact-form/admin.php */
918 do_action( 'contact_form_akismet', 'spam', $meta );
919 }
920 }
921
922 wp_send_json(
923 array(
924 'processed' => count( $approved_feedbacks ),
925 )
926 );
927 }
928
929 add_action( 'wp_ajax_grunion_recheck_queue', 'grunion_recheck_queue' );
930
931 /**
932 * Delete a number of spam feedbacks via an AJAX request.
933 */
934 function grunion_delete_spam_feedbacks() {
935 if ( ! wp_verify_nonce( $_POST['nonce'], 'jetpack_delete_spam_feedbacks' ) ) {
936 wp_send_json_error(
937 __( 'You aren’t authorized to do that.', 'jetpack' ),
938 403
939 );
940
941 return;
942 }
943
944 if ( ! current_user_can( 'delete_others_posts' ) ) {
945 wp_send_json_error(
946 __( 'You don’t have permission to do that.', 'jetpack' ),
947 403
948 );
949
950 return;
951 }
952
953 $deleted_feedbacks = 0;
954
955 $delete_limit = 25;
956 /**
957 * Filter the amount of Spam feedback one can delete at once.
958 *
959 * @module contact-form
960 *
961 * @since 8.7.0
962 *
963 * @param int $delete_limit Number of spam to process at once. Default to 25.
964 */
965 $delete_limit = apply_filters( 'jetpack_delete_spam_feedbacks_limit', $delete_limit );
966 $delete_limit = (int) $delete_limit;
967 $delete_limit = max( 1, min( 100, $delete_limit ) ); // Allow a range of 1-100 for the delete limit.
968
969 $query_args = array(
970 'post_type' => 'feedback',
971 'post_status' => 'spam',
972 'posts_per_page' => $delete_limit,
973 );
974
975 $query = new WP_Query( $query_args );
976 $spam_feedbacks = $query->get_posts();
977
978 foreach ( $spam_feedbacks as $feedback ) {
979 wp_delete_post( $feedback->ID, true );
980
981 $deleted_feedbacks++;
982 }
983
984 wp_send_json(
985 array(
986 'success' => true,
987 'data' => array(
988 'counts' => array(
989 'deleted' => $deleted_feedbacks,
990 'limit' => $delete_limit,
991 ),
992 ),
993 )
994 );
995 }
996 add_action( 'wp_ajax_jetpack_delete_spam_feedbacks', 'grunion_delete_spam_feedbacks' );
997
998 /**
999 * Show an admin notice if the "Empty Spam" or "Check Spam" process was unable to complete, probably due to a permissions error.
1000 */
1001 function grunion_feedback_admin_notice() {
1002 if ( isset( $_GET['jetpack_empty_feedback_spam_error'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1003 echo '<div class="notice notice-error"><p>' . esc_html( __( 'An error occurred while trying to empty the Feedback spam folder.', 'jetpack' ) ) . '</p></div>';
1004 } elseif ( isset( $_GET['jetpack_check_feedback_spam_error'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1005 echo '<div class="notice notice-error"><p>' . esc_html( __( 'An error occurred while trying to check for spam among the feedback you received.', 'jetpack' ) ) . '</p></div>';
1006 }
1007 }
1008 add_action( 'admin_notices', 'grunion_feedback_admin_notice' );
1009