_c(). if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wpaf_shortcode_form' ) ) { $this->setError( __( 'You are not authorized to submit forms, refresh page and try again', 'affcoupon' ) ); $this->getResponseJSON( true ); die(); } // Capability check — only users with the configured allowed role(s) may submit coupons. $allowed_roles = (array) get_option( 'wpaf_form-shortcode-allowed-roles', array( 'administrator' ) ); $user = wp_get_current_user(); $user_roles = $user ? (array) $user->roles : array(); if ( empty( array_intersect( $allowed_roles, $user_roles ) ) && ! current_user_can( 'manage_options' ) ) { $this->setError( __( 'You do not have permission to submit coupons.', 'affcoupon' ) ); $this->getResponseJSON( true ); die(); } if ( ! $this->checkAllowedTemplates( isset( $_POST['coupon-template'] ) ? sanitize_text_field( wp_unslash( $_POST['coupon-template'] ) ) : '' ) ) { $this->setError( __( 'You are not authorized to use this template', 'affcoupon' ) ); } else { if ( isset( $_POST['new_term'] ) ) { $this->create_term(); } elseif ( isset( $_POST['ID'] ) ) { $this->update_coupon(); } else { $this->insert_coupon(); } } $this->getResponseJSON( true ); die(); } /** * batch add/update post taxonomies * * @param int $post_id post id * @param string $post_field $_POST field name to check for terms */ private function insertTerms( $post_id, $post_field ) { check_ajax_referer( 'wpaf_shortcode_form', 'nonce' ); // Every value is sanitized inline: map_deep( ..., 'sanitize_text_field' ) // cleans the raw input, then each term is cast to int and each taxonomy // name is run through sanitize_key(). $tax_input = isset( $_POST[ $post_field ] ) ? map_deep( wp_unslash( $_POST[ $post_field ] ), 'sanitize_text_field' ) : []; foreach ( (array) $tax_input as $tax_name => $term ) { $term = array_map( 'intval', (array) $term ); wp_set_object_terms( $post_id, $term, sanitize_key( $tax_name ) ); } } /** * check setting gated default template against sent post field * * @param string $postedTemplate template name to be checked agains * * @return bool true for allowed */ private function checkAllowedTemplates( $postedTemplate ) { $allowed_template = get_option( 'wpaf_form-shortcode-default-template', 'all' ); if ( $allowed_template === 'all' ) { return true; } else { return $postedTemplate === $allowed_template; } } /** * update a coupon */ private function update_coupon() { check_ajax_referer( 'wpaf_shortcode_form', 'nonce' ); $ID = isset( $_POST['ID'] ) ? absint( wp_unslash( $_POST['ID'] ) ) : 0; $coupon_author = $this->_c()->get_post( $ID )->post_author; $current_author = $this->_c()->wp_get_current_user()->ID; if ( $coupon_author != $current_author ) { $this->setError( __( 'You are not authorized to submit forms, refresh page and try again', 'affcoupon' ) ); } else { // Nonce verified by caller in logic(). map_deep() applies the recognized // sanitizer sanitize_text_field() to every value; the rich-text description // is then re-sanitized with wp_kses_post() so its allowed HTML is kept. // WPAF_Sanitizer::sanitize() additionally enforces the per-field rules below. // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified by caller in logic(). $data = map_deep( wp_unslash( $_POST ), 'sanitize_text_field' ); if ( isset( $_POST['wpaf_description'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified by caller in logic(). $data['wpaf_description'] = wp_kses_post( wp_unslash( $_POST['wpaf_description'] ) ); } $original_fields = WPAF_Meta_Boxes_Fields_Pro::getFields(); // adding 'coupon-title' since it is not included in original fields $original_fields[] = [ 'id' => 'coupon-title' ]; $rules = []; foreach ( $original_fields as $field ) { $key = $field['id']; $rule = 'sanitize_text_field'; if ( strpos( $key, 'link' ) !== false ) { $rule = 'esc_url'; } if ( $key === 'wpaf_description' ) { $rule = 'wp_kses_post'; } $rules[ $key ] = $rule; } $sanitized_fields = WPAF_Sanitizer::sanitize( $data, $rules ); $meta_input = $this->batchMetaInput( 'coupon_details_', $sanitized_fields ); // updating post based on submitted form fields $operation_result = $this->_c()->wp_update_post( [ 'ID' => $ID, 'post_title' => $sanitized_fields['coupon-title'], 'meta_input' => $meta_input, ] ); if ( $this->_c()->is_wp_error( $operation_result ) ) { $this->setError( $operation_result->get_error_message() ); } else { $this->setData( 'id', $operation_result ); $this->setData( 'message', __( 'coupon updated', 'affcoupon' ) ); // taxonomy input $this->insertTerms( $operation_result, 'terms' ); $this->setData( 'terms', WPAF_Form_Shortcode_Pro::getCouponTerms() ); // image attachment process $this->image_attachment_process( $operation_result ); // featured image process $this->featured_image_process( $operation_result ); } } } /** * create and insert a new coupon */ private function insert_coupon() { check_ajax_referer( 'wpaf_shortcode_form', 'nonce' ); // map_deep() applies the recognized sanitizer sanitize_text_field() to every // value; the rich-text description is then re-sanitized with wp_kses_post() so // its allowed HTML is kept. WPAF_Sanitizer::sanitize() additionally enforces // the per-field rules below. $data = map_deep( wp_unslash( $_POST ), 'sanitize_text_field' ); if ( isset( $_POST['wpaf_description'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified by caller in logic(). $data['wpaf_description'] = wp_kses_post( wp_unslash( $_POST['wpaf_description'] ) ); } $original_fields = WPAF_Meta_Boxes_Fields_Pro::getFields(); // adding 'coupon-title' since it is not included in original fields $original_fields[] = [ 'id' => 'coupon-title' ]; $rules = []; foreach ( $original_fields as $field ) { $key = $field['id']; $rule = 'sanitize_text_field'; if ( strpos( $key, 'link' ) !== false ) { $rule = 'esc_url'; } if ( $key === 'wpaf_description' ) { $rule = 'wp_kses_post'; } $rules[ $key ] = $rule; } $sanitized_fields = WPAF_Sanitizer::sanitize( $data, $rules ); $meta_input = $this->batchMetaInput( 'coupon_details_', $sanitized_fields ); $post_status = get_option( 'wpaf_form-shortcode-coupon-status', 'publish' ); // inserting new post based on submitted form fields $operation_result = $this->_c()->wp_insert_post( [ 'post_title' => $sanitized_fields['coupon-title'], 'post_type' => WPAF_Plugin::CUSTOM_POST_TYPE, 'post_status' => $post_status, 'meta_input' => $meta_input, ] ); if ( $this->_c()->is_wp_error( $operation_result ) ) { $this->setError( $operation_result->get_error_message() ); } else { //taxonomy input $this->insertTerms( $operation_result, 'terms' ); $this->setData( 'terms', WPAF_Form_Shortcode_Pro::getCouponTerms() ); $this->setData( 'id', $operation_result ); $this->setData( 'message', __( 'coupon created', 'affcoupon' ) ); // image attachment process $this->image_attachment_process( $operation_result ); // featured image process $this->featured_image_process( $operation_result ); } } /** * transform input array to be compatible for meta values of post * * @param string $suffix suffix * @param array $meta_array array of meta values * * @return array handled array */ private function batchMetaInput( $suffix, $meta_array ) { $temp_array = []; foreach ( $meta_array as $key => $value ) { $final_key = "$suffix$key"; if ( $key === 'wpaf_description' ) { $final_key = $suffix . "description"; } $temp_array[ $final_key ] = $value; } return $temp_array; } /** * create new terms */ private function create_term() { check_ajax_referer( 'wpaf_shortcode_form', 'nonce' ); if ( isset( $_POST['new_term'] ) && get_option( 'wpaf_form-shortcode-enable-new-terms', '' ) === 'on' ) { // JSON payload — wp_unslash() reverses WP's auto-slash and sanitize_text_field() // strips any tags/control characters while leaving the JSON structure intact // (term names never contain angle brackets). json_decode then parses it, and // each extracted value is sanitized again before use (sanitize_key for the // taxonomy name, sanitize_text_field for the term name, absint for the parent). // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified by caller in logic(). $new_term_json = sanitize_text_field( wp_unslash( $_POST['new_term'] ) ); $new_term = json_decode( $new_term_json, true ); if ( ! is_array( $new_term ) ) { return; } $allowed_taxonomies = array( WPAF_Plugin::CUSTOM_TAXONOMY, WPAF_Plugin::VENDOR_TAXONOMY, ); foreach ( $new_term as $tax_name => $tax ) { $tax_name = sanitize_key( $tax_name ); if ( ! in_array( $tax_name, $allowed_taxonomies, true ) || ! is_array( $tax ) ) { continue; } $term_name = isset( $tax['name'] ) ? sanitize_text_field( $tax['name'] ) : ''; if ( '' === $term_name ) { continue; } $parent = isset( $tax['parent'] ) ? absint( $tax['parent'] ) : 0; wp_insert_term( $term_name, $tax_name, [ 'parent' => $parent ] ); } $this->setData( 'terms', WPAF_Form_Shortcode_Pro::getCouponTerms() ); } } /** * featured image process * * @param int $id post id */ private function featured_image_process( $id ) { check_ajax_referer( 'wpaf_shortcode_form', 'nonce' ); if ( isset( $_POST['featured_id'] ) ) { $featured_id = sanitize_text_field( wp_unslash( $_POST['featured_id'] ) ); if ( $featured_id === 'remove' ) { delete_post_thumbnail( $id ); } else { $this->_c()->set_post_thumbnail( $id, $featured_id ); } } } /** * image attachment process * * @param int $id post id */ private function image_attachment_process( $id ) { check_ajax_referer( 'wpaf_shortcode_form', 'nonce' ); if ( isset( $_POST['coupon-attachment-id'] ) ) { $this->_c()->update_post_meta( $id, 'coupon_details_coupon-image-input', sanitize_text_field( wp_unslash( $_POST['coupon-attachment-id'] ) ) ); } } }