admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'affcf_rejection_ajax' ), 'strings' => array( 'processing' => __( 'Processing...', 'airy-frontend-forms' ), 'success' => __( 'Post rejected successfully! Redirecting...', 'airy-frontend-forms' ), 'error' => __( 'Error: ', 'airy-frontend-forms' ), 'confirm' => __( 'Are you sure you want to reject this post?', 'airy-frontend-forms' ), ), ) ); // Enqueue admin styles for the meta box. wp_enqueue_style( 'affcf-rejection-metabox', AFFCF_PLUGIN_URL . 'assets/css/admin/admin-global.css', array(), AFFCF_VERSION ); // Add inline styles for meta box. $inline_css = ' .affcf-rejection-metabox .button-primary:hover { background: #b32d2e !important; border-color: #b32d2e !important; }'; wp_add_inline_style( 'affcf-rejection-metabox', $inline_css ); } /** * Add meta box */ public function add_meta_box() { // Get all post types that might have forms. $post_types = get_post_types( array( 'public' => true ), 'names' ); foreach ( $post_types as $post_type ) { add_meta_box( 'affcf_rejection', __( 'Frontend Form Rejection', 'airy-frontend-forms' ), array( $this, 'render_meta_box' ), $post_type, 'side', 'high' ); } } /** * Render meta box * * @param WP_Post $post The post object. */ public function render_meta_box( $post ) { // Check if this post was submitted via our plugin. $form_id = get_post_meta( $post->ID, '_affcf_form_id', true ); if ( ! $form_id ) { echo '
' . esc_html__( 'This post was not submitted via Airy Forms.', 'airy-frontend-forms' ) . '
'; return; } // Get form details. $form_repo = new AFFCF_Form_Repository(); $form = $form_repo->get_by_id( $form_id ); if ( ! $form ) { /* translators: %d: form ID */ echo '' . esc_html( sprintf( __( 'Form not found (ID: %d)', 'airy-frontend-forms' ), absint( $form_id ) ) ) . '
'; return; } // Get rejection history. global $wpdb; $rejections_table = $wpdb->prefix . 'affcf_rejections'; // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, plugin-controlled $rejections = $wpdb->get_results( $wpdb->prepare( "SELECT r.*, u.display_name as rejected_by_name FROM {$rejections_table} r LEFT JOIN {$wpdb->users} u ON r.rejected_by = u.ID WHERE r.post_id = %d ORDER BY r.rejected_at DESC", $post->ID ) ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter // Nonce. wp_nonce_field( 'affcf_rejection_' . $post->ID, 'affcf_rejection_nonce' ); ?> get_by_id( $form_id ); if ( ! $form ) { wp_die( esc_html__( 'Form not found.', 'airy-frontend-forms' ) ); } // Get rejection reason. $reason = isset( $_POST['rejection_reason'] ) ? sanitize_textarea_field( wp_unslash( $_POST['rejection_reason'] ) ) : ''; $send_notification = isset( $_POST['send_notification'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['send_notification'] ) ); // Save rejection to database. global $wpdb; $rejections_table = $wpdb->prefix . 'affcf_rejections'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table insert, no WP API available $wpdb->insert( $rejections_table, array( 'post_id' => $post_id, 'rejected_by' => get_current_user_id(), 'rejection_reason' => $reason, 'rejected_at' => current_time( 'mysql' ), 'notified' => $send_notification ? 1 : 0, ), array( '%d', '%d', '%s', '%s', '%d' ) ); // Move post to draft status. wp_update_post( array( 'ID' => $post_id, 'post_status' => 'draft', ) ); // Send email notification. if ( $send_notification ) { try { $email_manager = new AFFCF_Email_Manager(); $email_manager->send_post_rejected( $post->post_author, $post_id, $reason, $form ); } catch ( Exception $e ) { // Email failure should not block rejection - silently continue. unset( $e ); } } // Redirect back to post editor with success message. wp_safe_redirect( add_query_arg( array( 'post' => $post_id, 'action' => 'edit', 'affcf_rejected' => '1', ), admin_url( 'post.php' ) ) ); exit; } /** * AJAX handler for rejection */ public function ajax_handle_rejection() { // Verify nonce. check_ajax_referer( 'affcf_rejection_ajax', 'nonce' ); $post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0; // Check permissions. if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to reject this post.', 'airy-frontend-forms' ), ) ); } // Get post. $post = get_post( $post_id ); if ( ! $post ) { wp_send_json_error( array( 'message' => __( 'Post not found.', 'airy-frontend-forms' ), ) ); } // Get form. $form_id = get_post_meta( $post_id, '_affcf_form_id', true ); if ( ! $form_id ) { wp_send_json_error( array( 'message' => __( 'This post was not submitted via Airy Forms.', 'airy-frontend-forms' ), ) ); } $form_repo = new AFFCF_Form_Repository(); $form = $form_repo->get_by_id( $form_id ); if ( ! $form ) { wp_send_json_error( array( 'message' => __( 'Form not found.', 'airy-frontend-forms' ), ) ); } // Get rejection reason. $reason = isset( $_POST['rejection_reason'] ) ? sanitize_textarea_field( wp_unslash( $_POST['rejection_reason'] ) ) : ''; $send_notification = isset( $_POST['send_notification'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['send_notification'] ) ); // Save rejection to database. global $wpdb; $rejections_table = $wpdb->prefix . 'affcf_rejections'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table insert, no WP API available $result = $wpdb->insert( $rejections_table, array( 'post_id' => $post_id, 'rejected_by' => get_current_user_id(), 'rejection_reason' => $reason, 'rejected_at' => current_time( 'mysql' ), 'notified' => $send_notification ? 1 : 0, ), array( '%d', '%d', '%s', '%s', '%d' ) ); if ( false === $result ) { wp_send_json_error( array( 'message' => __( 'Failed to save rejection record.', 'airy-frontend-forms' ), ) ); } // Move post to draft status. $update_result = wp_update_post( array( 'ID' => $post_id, 'post_status' => 'draft', ), true ); if ( is_wp_error( $update_result ) ) { wp_send_json_error( array( 'message' => $update_result->get_error_message(), ) ); } // Send email notification. $email_sent = false; if ( $send_notification ) { try { $email_manager = new AFFCF_Email_Manager(); $email_manager->send_post_rejected( $post->post_author, $post_id, $reason, $form ); $email_sent = true; } catch ( Exception $e ) { // Silently fail email - don't block rejection process. // Error is already handled by email manager. unset( $e ); // Suppress unused variable warning. } } // Success response. wp_send_json_success( array( 'message' => __( 'Post rejected successfully!', 'airy-frontend-forms' ), 'email_sent' => $email_sent, 'redirect_url' => add_query_arg( array( 'post' => $post_id, 'action' => 'edit', 'affcf_rejected' => '1', ), admin_url( 'post.php' ) ), ) ); } }