$conf, $runtime; function_exists('chdir') AND chdir(APP_PATH); $r = 'mysql' == $conf['cache']['type'] ? website_set('runtime', $runtime) : cache_set('runtime', $runtime); } function runtime_truncate() { global $conf; 'mysql' == $conf['cache']['type'] ? website_set('runtime', '') : cache_delete('runtime'); } register_shutdown_function('runtime_save'); ?>plugin development - 403 Error when text is pasted in Custom Metabox Textarea|Programmer puzzle solving
最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

plugin development - 403 Error when text is pasted in Custom Metabox Textarea

matteradmin10PV0评论

I hope everyone is having a great day!

I am creating this question as I have been stuck on this issue all day and I need some friendly advice.

I have created a repeater custom metabox item for a custom post type called Proposal. The repeater works fine and is saving the content and displaying it how I would like. However, when I paste content into the textarea it throws a 403 error page when you update or publish the post and have MORE than 1 repeated option.

The weird part is when you just add text to the textarea and don't paste the content everything works fine. Obviously, this is an issue I need to fix before I release the plugin to the world. Here is the code I have. If you notice anything wrong, please let me know so I can improve it as I am fairly new to backend development.

<div id="wpp_meta_inner">
<?php
//get the saved meta as an array
$service = get_post_meta($post->ID,'service',false);
$service_title = get_post_meta($post->ID, 'wpp_service_title', true);
?>
    <div>

          <div class="wpp-input-container">
              <label><?php _e('Title') ?></label>
              <input type="text" name="wpp_service_title" id="wpp_service_title" value="<?php echo $service_title; ?>" />
              <p><?php _e('Will display default "Scope of Services" if empty.') ?></p>
          </div>

          <div class="wpp-input-container">
              <label class="wpp-label-repeater"><?php _e('Service Items') ?></label>
              <?php
              wp_nonce_field ( 'c_nonce_field', 'c_wpnonce');
                  $c = 0;
                  if ( count( $service ) > 0 ) {
                      if(!empty($service)) {
                          foreach( $service as $service_item_val ) {
                              foreach( $service_item_val as $service_item ) {
                                  if ( isset( $service_item['title'] ) || isset( $service_item['service_item'] ) ) {
                                      printf( '<div class="wpp-repeater-wrapper service">Title: <input class="wpp-repeater-input service" type="text" name="service[%1$s][title]" value="%2$s" />Description: <textarea class="wpp-repeater-input service" name="service[%1$s][service_item]" data-gramm_editor="false" value="">%3$s</textarea><span class="wpp-item-remove service">%4$s</span></div>', $c, $service_item['title'], $service_item['service_item'], __( 'Remove' ) );
                                      $c = $c +1;
                                  }
                              }
                          }
                      }
                  }
              ?>
              <span id="services_here"></span>
              <div class="wpp-item-add services" style="visibility: hidden; margin-bottom: -20px;"><?php _e('Add Item'); ?></div>
              <div class="wpp-item-add services add-button"><?php _e('Add Service'); ?></div>
              </div>

    </div>

    <script>
    var $ =jQuery.noConflict();
    $(document).ready(function() {
        var count = <?php echo $c; ?>;
        $(".wpp-item-add.services").click(function() {
            count = count + 1;
            $('#services_here').append('<div class="wpp-repeater-wrapper service">Title: <input class="wpp-repeater-input service" type="text" name="service['+count+'][title]" value="" placeholder="" />Description: <textarea class="wpp-repeater-input service" name="service['+count+'][service_item]" data-gramm_editor="false" value="" placeholder=""></textarea><span class="wpp-item-remove service">Remove</span></div>' );
            return false;
        });
        $(".wpp-item-remove.service").live('click', function() {
            $(this).parent().remove();
        });
    });
    </script>

</div>

<?php }
/* When the post is saved, saves our data */
function wpp_save_services_data( $post_id ) {
if(defined("DOING_AJAX") AND DOING_AJAX)
  return;
  if(!current_user_can('edit_post', $post_id ))
  return;
  if(!isset($_POST['c_wpnonce']) || !wp_verify_nonce( $_POST['c_wpnonce'], 'c_nonce_field'))
  return;
  $allowed_html = array(
          'a' => array(
              'href' => array()
          ));
  $service = $_POST['service'];
  update_post_meta ($post_id, 'wpp_service_title', wp_kses( $_POST['wpp_service_title'], $allowed_html));
  update_post_meta ($post_id,'service',$service);
 }

I hope everyone is having a great day!

I am creating this question as I have been stuck on this issue all day and I need some friendly advice.

I have created a repeater custom metabox item for a custom post type called Proposal. The repeater works fine and is saving the content and displaying it how I would like. However, when I paste content into the textarea it throws a 403 error page when you update or publish the post and have MORE than 1 repeated option.

The weird part is when you just add text to the textarea and don't paste the content everything works fine. Obviously, this is an issue I need to fix before I release the plugin to the world. Here is the code I have. If you notice anything wrong, please let me know so I can improve it as I am fairly new to backend development.

<div id="wpp_meta_inner">
<?php
//get the saved meta as an array
$service = get_post_meta($post->ID,'service',false);
$service_title = get_post_meta($post->ID, 'wpp_service_title', true);
?>
    <div>

          <div class="wpp-input-container">
              <label><?php _e('Title') ?></label>
              <input type="text" name="wpp_service_title" id="wpp_service_title" value="<?php echo $service_title; ?>" />
              <p><?php _e('Will display default "Scope of Services" if empty.') ?></p>
          </div>

          <div class="wpp-input-container">
              <label class="wpp-label-repeater"><?php _e('Service Items') ?></label>
              <?php
              wp_nonce_field ( 'c_nonce_field', 'c_wpnonce');
                  $c = 0;
                  if ( count( $service ) > 0 ) {
                      if(!empty($service)) {
                          foreach( $service as $service_item_val ) {
                              foreach( $service_item_val as $service_item ) {
                                  if ( isset( $service_item['title'] ) || isset( $service_item['service_item'] ) ) {
                                      printf( '<div class="wpp-repeater-wrapper service">Title: <input class="wpp-repeater-input service" type="text" name="service[%1$s][title]" value="%2$s" />Description: <textarea class="wpp-repeater-input service" name="service[%1$s][service_item]" data-gramm_editor="false" value="">%3$s</textarea><span class="wpp-item-remove service">%4$s</span></div>', $c, $service_item['title'], $service_item['service_item'], __( 'Remove' ) );
                                      $c = $c +1;
                                  }
                              }
                          }
                      }
                  }
              ?>
              <span id="services_here"></span>
              <div class="wpp-item-add services" style="visibility: hidden; margin-bottom: -20px;"><?php _e('Add Item'); ?></div>
              <div class="wpp-item-add services add-button"><?php _e('Add Service'); ?></div>
              </div>

    </div>

    <script>
    var $ =jQuery.noConflict();
    $(document).ready(function() {
        var count = <?php echo $c; ?>;
        $(".wpp-item-add.services").click(function() {
            count = count + 1;
            $('#services_here').append('<div class="wpp-repeater-wrapper service">Title: <input class="wpp-repeater-input service" type="text" name="service['+count+'][title]" value="" placeholder="" />Description: <textarea class="wpp-repeater-input service" name="service['+count+'][service_item]" data-gramm_editor="false" value="" placeholder=""></textarea><span class="wpp-item-remove service">Remove</span></div>' );
            return false;
        });
        $(".wpp-item-remove.service").live('click', function() {
            $(this).parent().remove();
        });
    });
    </script>

</div>

<?php }
/* When the post is saved, saves our data */
function wpp_save_services_data( $post_id ) {
if(defined("DOING_AJAX") AND DOING_AJAX)
  return;
  if(!current_user_can('edit_post', $post_id ))
  return;
  if(!isset($_POST['c_wpnonce']) || !wp_verify_nonce( $_POST['c_wpnonce'], 'c_nonce_field'))
  return;
  $allowed_html = array(
          'a' => array(
              'href' => array()
          ));
  $service = $_POST['service'];
  update_post_meta ($post_id, 'wpp_service_title', wp_kses( $_POST['wpp_service_title'], $allowed_html));
  update_post_meta ($post_id,'service',$service);
 }
Share Improve this question asked Dec 12, 2018 at 19:29 Kevin W.Kevin W. 439 bronze badges 3
  • I tested your code and could not duplicate the issue. Maybe there is some other code somewhere causing the issue. – Dave Romsey Commented Dec 13, 2018 at 6:10
  • Hey Dave, thanks for commenting back. I have finally debugged the issue and determined it was a mod_security rule that was getting triggered from the hosting company. The reason why it would trigger only when pasting content is because the content I was pasting containged the text (CMS). For some reason anytime the textarea contained () it would trigger mod_security. Do you happen to have a solution that would trip those values from the textarea before it gets saved or on blur? – Kevin W. Commented Dec 13, 2018 at 15:07
  • You got it! I posted an answer with some updated code. Not 100% sure it will fix the issue (let me know), but I was easily able to break the input fields with certain data before, and with the updated code, things are working as expected. – Dave Romsey Commented Dec 13, 2018 at 19:24
Add a comment  | 

1 Answer 1

Reset to default 1

I gave your code a look over, and updated it to include proper escaping and sanitation. I suspect that the problem could have stemmed from the lack of escaping. I also applied some formatting and WP best practices, but otherwise the code is the same.

/**
 * Meta box display callback.
 *
 * @param WP_Post $post Current post object.
 */
function wpse_metabox_callback( $post ) {
    $service_title = get_post_meta( $post->ID, 'wpp_service_title', true );
    $service       = get_post_meta( $post->ID, 'service', false ); // get as an array ?>

    <div id="wpp_meta_inner">
    <div>
            <div class="wpp-input-container">
                <label><?php esc_html_e( 'Title', 'textdomain' ); ?></label>
                <input type="text" name="wpp_service_title" id="wpp_service_title" value="<?php echo esc_attr( $service_title ); ?>" />
                <p><?php esc_html_e( 'Will display default "Scope of Services" if empty.', 'textdomain' ); ?></p>
            </div>

            <div class="wpp-input-container">
                <label class="wpp-label-repeater"><?php _e( 'Service Items', 'textdomain' ); ?></label>
                <?php
                    wp_nonce_field( 'c_nonce_field', 'c_wpnonce' );
                    $c = 0;
                    if ( count( $service ) > 0 ) {
                        if ( ! empty( $service ) ) {
                            foreach( $service as $service_item_val ) {
                                foreach( $service_item_val as $service_item ) {
                                    if ( isset( $service_item['title'] ) || isset( $service_item['service_item'] ) ) {
                                        printf(
                                            '<div class="wpp-repeater-wrapper service">' .
                                                'Title: <input class="wpp-repeater-input service" type="text" name="service[%1$s][title]" value="%2$s" />' .
                                                'Description: <textarea class="wpp-repeater-input service" name="service[%1$s][service_item]" data-gramm_editor="false" value="">%3$s</textarea><span class="wpp-item-remove service">%4$s</span>' . 
                                            '</div>',
                                            esc_attr( $c ),
                                            esc_html( $service_item['title'] ),
                                            esc_textarea( $service_item['service_item'] ),
                                            esc_html( __( 'Remove', 'textdomain' ) )
                                        );
                                        $c += 1;
                                    }
                                }
                            }
                        }
                    }
                ?>
                <span id="services_here"></span>
                <div class="wpp-item-add services" style="visibility: hidden; margin-bottom: -20px;"><?php esc_html_e( 'Add Item', 'textdomain' ); ?></div>
                <div class="wpp-item-add services add-button"><?php esc_html_e( 'Add Service', 'textdomain' ); ?></div>
            </div>
        </div>

        <script>
            var $ =jQuery.noConflict();
            $(document).ready(function() {
                    var count = <?php echo esc_js( $c ); ?>;
                    $(".wpp-item-add.services").click(function() {
                            count = count + 1;
                            $('#services_here').append('<div class="wpp-repeater-wrapper service">Title: <input class="wpp-repeater-input service" type="text" name="service['+count+'][title]" value="" placeholder="" />Description: <textarea class="wpp-repeater-input service" name="service['+count+'][service_item]" data-gramm_editor="false" value="" placeholder=""></textarea><span class="wpp-item-remove service">Remove</span></div>' );
                            return false;
                    });
                    $(".wpp-item-remove.service").live('click', function() {
                            $(this).parent().remove();
                    });
            });
        </script>
    </div><?php
}

When saving the metabox data, wp_kses_post() will be applied to each element of the $services array:

/**
 * Sanitize and save metabox data.
 */
add_action( 'save_post', 'wpse_save_meta_box' );
function wpse_save_meta_box( $post_id ) {
    if ( defined( 'DOING_AJAX' ) AND DOING_AJAX ) {
        return;
    }

    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    if ( ! isset( $_POST['c_wpnonce'] ) || ! wp_verify_nonce( $_POST['c_wpnonce'], 'c_nonce_field' ) ) {
        return;
    }

    $allowed_html = array (
        'a' => array(
            'href' => array()
    ) );

    $service = isset( $_POST['service'] ) && ! empty( $_POST['service'] ) ? array_map( 'wp_kses_post', $_POST['service'] ) : array();

    update_post_meta( $post_id, 'wpp_service_title', wp_kses( $_POST['wpp_service_title'], $allowed_html ) );
    update_post_meta( $post_id, 'service', $service );
}

For the sake of completeness, here is the code that registers the proposal post type:

/**
 * Register proposal post type.
 */
add_action( 'init', 'wpse_register_post_type_proposal' );
function wpse_register_post_type_proposal() {
    $args = [
            'label'             => __( 'Proposals', 'textdomain' ),
            'public'             => true,
            'publicly_queryable' => true,
            'show_ui'            => true,
            'show_in_menu'       => true,
            'query_var'          => true,
            'rewrite'            => [ 'slug' => 'proposal' ],
            'capability_type'    => 'post',
            'has_archive'        => true,
            'hierarchical'       => false,
            'menu_position'      => null,
            'supports'           => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ],
    ];
    register_post_type( 'proposal', $args );
}

and the meta box:

/**
 * Register meta box
 */
function wpse_register_meta_boxes() {
    add_meta_box( 'meta-box-id', __( 'My Meta Box', 'textdomain' ), 'wpse_metabox_callback', 'proposal' );
}
add_action( 'add_meta_boxes', 'wpse_register_meta_boxes' );
Post a comment

comment list (0)

  1. No comments so far