>
*/
public static function fields() {
return array(
'headline' => array(
'label' => __( 'Headline / Review Title', 'advanced-testimonial' ),
'type' => 'text',
),
'video' => array(
'label' => __( 'Video URL (YouTube, Vimeo or MP4)', 'advanced-testimonial' ),
'type' => 'url',
),
'company' => array(
'label' => __( 'Company', 'advanced-testimonial' ),
'type' => 'text',
),
'designation' => array(
'label' => __( 'Designation / Job Title', 'advanced-testimonial' ),
'type' => 'text',
),
'location' => array(
'label' => __( 'Location', 'advanced-testimonial' ),
'type' => 'text',
),
'website' => array(
'label' => __( 'Website', 'advanced-testimonial' ),
'type' => 'url',
),
'email' => array(
'label' => __( 'Email (optional)', 'advanced-testimonial' ),
'type' => 'email',
),
'rating' => array(
'label' => __( 'Rating', 'advanced-testimonial' ),
'type' => 'rating',
),
'review_date' => array(
'label' => __( 'Review Date', 'advanced-testimonial' ),
'type' => 'date',
),
'verified' => array(
'label' => __( 'Verified Customer', 'advanced-testimonial' ),
'type' => 'checkbox',
),
'company_logo' => array(
'label' => __( 'Company Logo', 'advanced-testimonial' ),
'type' => 'media',
),
'social_twitter' => array(
'label' => __( 'X / Twitter URL', 'advanced-testimonial' ),
'type' => 'url',
),
'social_linkedin' => array(
'label' => __( 'LinkedIn URL', 'advanced-testimonial' ),
'type' => 'url',
),
'social_facebook' => array(
'label' => __( 'Facebook URL', 'advanced-testimonial' ),
'type' => 'url',
),
);
}
/**
* Register the meta box.
*
* @return void
*/
public function add_meta_box() {
add_meta_box(
'advanced-testimonial-details',
__( 'Testimonial Details', 'advanced-testimonial' ),
array( $this, 'render' ),
CPT::POST_TYPE,
'normal',
'high'
);
}
/**
* Render the meta box.
*
* @param \WP_Post $post Current post.
* @return void
*/
public function render( $post ) {
wp_nonce_field( self::NONCE_ACTION, self::NONCE_NAME );
echo '
' . esc_html__( 'The post title is the client name, the editor is the review text, and the featured image is the client photo.', 'advanced-testimonial' ) . '
';
echo '';
}
/**
* Render a single field control.
*
* @param string $name Field name.
* @param array $field Field definition.
* @param mixed $value Stored value.
* @param string $id Element id.
* @return void
*/
private function render_control( $name, $field, $value, $id ) {
$key = Helpers::meta_key( $name );
switch ( $field['type'] ) {
case 'rating':
$rating_val = Helpers::clamp_rating( $value );
echo '';
echo '' . esc_html__( 'No rating', 'advanced-testimonial' ) . ' ';
foreach ( array( 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5 ) as $step ) {
$disp = rtrim( rtrim( number_format( (float) $step, 1 ), '0' ), '.' );
/* translators: %s: star rating value, e.g. 4.5. */
$label = sprintf( __( '%s stars', 'advanced-testimonial' ), $disp );
echo '' . esc_html( $label ) . ' ';
}
echo ' ';
break;
case 'checkbox':
echo ' ' . esc_html__( 'Mark this customer as verified', 'advanced-testimonial' ) . ' ';
break;
case 'media':
$attachment_id = (int) $value;
$preview = $attachment_id ? wp_get_attachment_image( $attachment_id, 'thumbnail', false, array( 'class' => 'at-media-preview-img' ) ) : '';
echo '';
break;
case 'url':
echo ' ';
break;
case 'email':
echo ' ';
break;
case 'date':
echo ' ';
break;
default:
echo ' ';
break;
}
}
/**
* Persist the submitted values.
*
* @param int $post_id Post ID.
* @param \WP_Post $post Post object.
* @return void
*/
public function save( $post_id, $post ) {
unset( $post ); // Required by the save_post hook signature; only the ID is used.
if ( ! isset( $_POST[ self::NONCE_NAME ] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST[ self::NONCE_NAME ] ) ), self::NONCE_ACTION ) ) {
return;
}
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
foreach ( self::fields() as $name => $field ) {
$key = Helpers::meta_key( $name );
$raw = isset( $_POST[ $key ] ) ? wp_unslash( $_POST[ $key ] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized per field type in sanitize_value().
$sanitized = self::sanitize_value( $field['type'], $raw );
if ( '' === $sanitized || ( 'media' === $field['type'] && 0 === $sanitized ) || ( 'checkbox' === $field['type'] && '' === $sanitized ) ) {
delete_post_meta( $post_id, $key );
} else {
update_post_meta( $post_id, $key, $sanitized );
}
}
}
/**
* Sanitize a value according to its field type.
*
* Public and static so register_post_meta() can reuse the exact same
* rules for REST writes — one sanitizer, two entry points.
*
* @param string $type Field type.
* @param mixed $raw Raw submitted value.
* @return mixed
*/
public static function sanitize_value( $type, $raw ) {
switch ( $type ) {
case 'rating':
return Helpers::clamp_rating( $raw );
case 'media':
return absint( $raw );
case 'checkbox':
return ( '1' === (string) $raw ) ? '1' : '';
case 'url':
return esc_url_raw( trim( (string) $raw ) );
case 'email':
return sanitize_email( (string) $raw );
case 'date':
$date = sanitize_text_field( (string) $raw );
return ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ) ? $date : '';
default:
return sanitize_text_field( (string) $raw );
}
}
}