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]403 Error when text is pasted in Custom Metabox Textarea" 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+']403 Error when text is pasted in Custom Metabox Textarea" 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' );