webhooks = $webhooks; add_filter( 'manage_dsgo_form_submission_posts_columns', array( $this, 'add_column' ), 20 ); add_action( 'manage_dsgo_form_submission_posts_custom_column', array( $this, 'render_column' ), 10, 2 ); add_filter( 'post_row_actions', array( $this, 'row_actions' ), 10, 2 ); add_action( 'add_meta_boxes_dsgo_form_submission', array( $this, 'maybe_add_meta_box' ) ); add_action( 'admin_post_' . Form_Webhooks::RESEND_ACTION, array( $this, 'handle_resend' ) ); add_action( 'admin_notices', array( $this, 'resend_notice' ) ); add_filter( 'removable_query_args', array( $this, 'removable_query_args' ) ); } /** * Insert the Webhook column before Date. * * @param array $columns Columns. * @return array Columns. */ public function add_column( $columns ) { $out = array(); foreach ( $columns as $key => $label ) { if ( 'date' === $key ) { $out['airowp_webhook'] = __( 'Webhook', 'airo-wp' ); } $out[ $key ] = $label; } if ( ! isset( $out['airowp_webhook'] ) ) { $out['airowp_webhook'] = __( 'Webhook', 'airo-wp' ); } return $out; } /** * Render the Webhook column. * * @param string $column Column key. * @param int $post_id Submission ID. */ public function render_column( $column, $post_id ) { if ( 'airowp_webhook' !== $column ) { return; } $label = Form_Webhook_Status::label( (int) $post_id ); if ( '' === $label ) { echo '' . esc_html__( 'No webhook', 'airo-wp' ) . ''; return; } // All at least 4.5:1 on the white and striped list-table rows. $colors = array( 'delivered' => '#007017', 'failed' => '#d63638', 'pending' => '#996800', ); $status = Form_Webhook_Status::get_status( (int) $post_id ); $color = isset( $colors[ $status ] ) ? $colors[ $status ] : 'inherit'; echo '' . esc_html( $label ) . ''; } /** * Add a Resend webhook row action. * * @param array $actions Row actions. * @param \WP_Post $post Post. * @return array Row actions. */ public function row_actions( $actions, $post ) { if ( ! $post instanceof \WP_Post || 'dsgo_form_submission' !== $post->post_type || ! $this->has_webhook( $post->ID ) ) { return $actions; } $actions['airowp_resend_webhook'] = sprintf( '%3$s', esc_url( Form_Webhook_Status::resend_url( $post->ID ) ), /* translators: %d: submission ID */ esc_attr( sprintf( __( 'Resend webhook for submission %d', 'airo-wp' ), $post->ID ) ), esc_html__( 'Resend webhook', 'airo-wp' ) ); return $actions; } /** * Register the delivery meta box for submissions that have a webhook. * * @param \WP_Post $post Submission. */ public function maybe_add_meta_box( $post ) { if ( ! $this->has_webhook( $post->ID ) ) { return; } add_meta_box( 'airowp_webhook_delivery', __( 'Webhook delivery', 'airo-wp' ), array( $this, 'render_meta_box' ), 'dsgo_form_submission', 'side', 'default' ); } /** * Render delivery details. Shows only the receiver's host: some webhook URLs are credentials. * * @param \WP_Post $post Submission. */ public function render_meta_box( $post ) { $id = (int) $post->ID; $host = (string) wp_parse_url( (string) get_post_meta( $id, '_dsg_webhook_url', true ), PHP_URL_HOST ); $code = (int) get_post_meta( $id, '_dsg_webhook_last_code', true ); $error = (string) get_post_meta( $id, '_dsg_webhook_last_error', true ); $date = (string) get_post_meta( $id, '_dsg_webhook_delivered_date', true ); $rows = array( __( 'Status', 'airo-wp' ) => Form_Webhook_Status::label( $id ), __( 'Receiver', 'airo-wp' ) => $host, __( 'Attempts', 'airo-wp' ) => (string) (int) get_post_meta( $id, '_dsg_webhook_attempts', true ), ); $signed = (string) get_post_meta( $id, '_dsg_webhook_signed', true ); if ( '' !== $signed ) { $rows[ __( 'Signature', 'airo-wp' ) ] = 'yes' === $signed ? __( 'Signed', 'airo-wp' ) : __( 'Unsigned — no webhook secret configured', 'airo-wp' ); } if ( $code ) { /* translators: %d: HTTP status code */ $rows[ __( 'Last response', 'airo-wp' ) ] = sprintf( __( 'HTTP %d', 'airo-wp' ), $code ); } if ( '' !== $error ) { $rows[ __( 'Last error', 'airo-wp' ) ] = $error; } if ( '' !== $date ) { $rows[ __( 'Delivered', 'airo-wp' ) ] = mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $date ); } echo '
| ' . esc_html( $label ) . ' | ' . esc_html( $value ) . ' |
|---|
' . esc_html__( 'Resend webhook', 'airo-wp' ) . '
'; } /** * Handle the admin-post Resend request. */ public function handle_resend() { $submission_id = isset( $_GET['submission'] ) ? absint( wp_unslash( $_GET['submission'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verified on the next line. check_admin_referer( Form_Webhooks::RESEND_ACTION . '_' . $submission_id ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to resend webhooks.', 'airo-wp' ), '', array( 'response' => 403 ) ); } $result = $this->webhooks->resend( $submission_id ); $fallback = admin_url( 'edit.php?post_type=dsgo_form_submission' ); $referer = wp_get_referer(); wp_safe_redirect( add_query_arg( 'airowp_webhook_resent', $result, $referer ? $referer : $fallback ) ); exit; } /** * Let WordPress strip the resend flag from the URL so the notice does not persist. * * @param string[] $args Removable query args. * @return string[] Args. */ public function removable_query_args( $args ) { $args = (array) $args; $args[] = 'airowp_webhook_resent'; return $args; } /** * Notice after a resend. */ public function resend_notice() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display-only flag set by our own redirect. $result = isset( $_GET['airowp_webhook_resent'] ) ? sanitize_key( wp_unslash( $_GET['airowp_webhook_resent'] ) ) : ''; $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( '' === $result || ! $screen || 'dsgo_form_submission' !== $screen->post_type ) { return; } // Type, ARIA role (a full page load is otherwise not announced), message. $messages = array( 'delivered' => array( 'success', 'status', __( 'Webhook delivered.', 'airo-wp' ) ), 'pending' => array( 'warning', 'status', __( 'Webhook delivery failed; it will be retried automatically.', 'airo-wp' ) ), 'failed' => array( 'error', 'alert', __( 'Webhook delivery failed. See the submission for details.', 'airo-wp' ) ), 'invalid' => array( 'error', 'alert', __( 'This submission has no webhook to resend.', 'airo-wp' ) ), ); if ( ! isset( $messages[ $result ] ) ) { return; } list( $type, $role, $message ) = $messages[ $result ]; printf( '%3$s