Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor meta fields #21576

Open
wants to merge 19 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 13 additions & 23 deletions admin/metabox/class-metabox.php
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,9 @@ protected function get_additional_tabs() {
/**
* Adds a line in the meta box.
*
* @deprecated 23.5
* @codeCoverageIgnore
*
* @todo [JRF] Check if $class is added appropriately everywhere.
*
* @param string[] $meta_field_def Contains the vars based on which output is generated.
Expand All @@ -509,6 +512,8 @@ protected function get_additional_tabs() {
* @return string
*/
public function do_meta_box( $meta_field_def, $key = '' ) {
_deprecated_function( __METHOD__, 'Yoast SEO 23.5' );

$content = '';
$esc_form_key = esc_attr( WPSEO_Meta::$form_prefix . $key );
$meta_value = WPSEO_Meta::get_value( $key, $this->get_metabox_post()->ID );
Expand Down Expand Up @@ -740,34 +745,19 @@ public function save_postdata( $post_id ) {
continue;
}

$data = null;
$field_name = WPSEO_Meta::$form_prefix . $key;

if ( $meta_box['type'] === 'checkbox' ) {
$data = isset( $_POST[ $field_name ] ) ? 'on' : 'off';
}
else {
if ( isset( $_POST[ $field_name ] ) ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We're preparing to do just that.
$data = wp_unslash( $_POST[ $field_name ] );
if ( isset( $_POST[ $field_name ] ) && is_string( $_POST[ $field_name ] ) ) {
// We are sanitizing the text field here even though it will be sanitized by the sanitize_post_meta function.
// That is because there are filters that can add fields but not neccessarily register them with the sanitize_post_meta function.
// See: 'wpseo_metabox_entries_' . $tab.

// For multi-select.
if ( is_array( $data ) ) {
$data = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], $data );
}

if ( is_string( $data ) ) {
$data = ( $key !== 'canonical' ) ? WPSEO_Utils::sanitize_text_field( $data ) : WPSEO_Utils::sanitize_url( $data );
}
}
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We're preparing to do just that.
$data = wp_unslash( $_POST[ $field_name ] );

// Reset options when no entry is present with multiselect - only applies to `meta-robots-adv` currently.
if ( ! isset( $_POST[ $field_name ] ) && ( $meta_box['type'] === 'multiselect' ) ) {
$data = [];
if ( ! in_array( $key, [ 'canonical', 'redirect' ], true ) ) {
$data = WPSEO_Utils::sanitize_text_field( $data );
}
}

if ( $data !== null ) {
WPSEO_Meta::set_value( $key, $data, $post_id );
}
}
Expand Down
181 changes: 47 additions & 134 deletions inc/class-wpseo-meta.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,147 +69,89 @@ class WPSEO_Meta {
* Meta box field definitions for the meta box form.
*
* {@internal
* - Titles, help texts, description text and option labels are added via a translate_meta_boxes() method
* in the relevant child classes (WPSEO_Metabox and WPSEO_Social_admin) as they are only needed there.
* - Beware: even though the meta keys are divided into subsets, they still have to be uniquely named!}}
* - The properties for the field translations and configuration (all the properties except 'default_value', 'options')
* are not used anymore because the fields rendered hidden by the Meta_Fields_Presenter and otherwise the fields are configured on the client side.
*
* @var array
* Array format:
* (required) 'type' => (string) field type. i.e. text / textarea / checkbox /
* radio / select / multiselect / upload etc.
* (required) 'title' => (string) table row title.
* (recommended) 'default_value' => (string|array) default value for the field.
* IMPORTANT:
* - if the field has options, the default has to be the
* key of one of the options.
* - if the field is a text field, the default **has** to be
* an empty string as otherwise the user can't save
* an empty value/delete the meta value.
* - if the field is a checkbox, the only valid values
* are 'on' or 'off'.
* - default will be an empty string.
* (semi-required) 'options' => (array) options for used with (multi-)select and radio
* fields, required if that's the field type.
* key = (string) value which will be saved to db.
* value = (string) text label for the option.
* (optional) 'autocomplete' => (bool) whether autocomplete is on for text fields,
* defaults to true.
* (optional) 'class' => (string) classname(s) to add to the actual <input> tag.
* (optional) 'description' => (string) description to show underneath the field.
* (optional) 'expl' => (string) label for a checkbox.
* (optional) 'help' => (string) help text to show on mouse over ? image.
* (optional) 'rows' => (int) number of rows for a textarea, defaults to 3.
* (optional) 'placeholder' => (string) Currently only used by add-on plugins.
* (optional) 'serialized' => (bool) whether the value is expected to be serialized,
* i.e. an array or object, defaults to false.
* Currently only used by add-on plugins.
*/
public static $meta_fields = [
'general' => [
'focuskw' => [
'type' => 'hidden',
'title' => '',
'focuskw' => [
'default_value' => '',
],
'title' => [
'type' => 'hidden',
'title' => '', // Translation added later.
'title' => [
'default_value' => '',
'description' => '', // Translation added later.
'help' => '', // Translation added later.
],
'metadesc' => [
'type' => 'hidden',
'title' => '', // Translation added later.
'metadesc' => [
'default_value' => '',
'class' => 'metadesc',
'rows' => 2,
'description' => '', // Translation added later.
'help' => '', // Translation added later.
],
'linkdex' => [
'type' => 'hidden',
'title' => 'linkdex',
'linkdex' => [
'default_value' => '0',
'description' => '',
],
'content_score' => [
'type' => 'hidden',
'title' => 'content_score',
'content_score' => [
'default_value' => '0',
'description' => '',
],
'inclusive_language_score' => [
'type' => 'hidden',
'title' => 'inclusive_language_score',
'default_value' => '0',
'description' => '',
],
'is_cornerstone' => [
'type' => 'hidden',
'title' => 'is_cornerstone',
'default_value' => 'false',
'description' => '',
'is_cornerstone' => [
'default_value' => '0',
],
],
'advanced' => [
'meta-robots-noindex' => [
'type' => 'hidden',
'title' => '', // Translation added later.
'default_value' => '0', // = post-type default.
'options' => [
'0' => '', // Post type default - translation added later.
'2' => '', // Index - translation added later.
'1' => '', // No-index - translation added later.
'0' => '',
'2' => '',
'1' => '',
],
],
'meta-robots-nofollow' => [
'type' => 'hidden',
'title' => '', // Translation added later.
'default_value' => '0', // = follow.
'options' => [
'0' => '', // Follow - translation added later.
'1' => '', // No-follow - translation added later.
'0' => '', // Follow.
'1' => '', // No-follow .
],
],
'meta-robots-adv' => [
'type' => 'hidden',
'title' => '', // Translation added later.
'default_value' => '',
'description' => '', // Translation added later.
'options' => [
'noimageindex' => '', // Translation added later.
'noarchive' => '', // Translation added later.
'nosnippet' => '', // Translation added later.
'noimageindex' => '',
'noarchive' => '',
'nosnippet' => '',
],
],
'bctitle' => [
'type' => 'hidden',
'title' => '', // Translation added later.
'default_value' => '',
'description' => '', // Translation added later.
],
'canonical' => [
'type' => 'hidden',
'title' => '', // Translation added later.
'default_value' => '',
'description' => '', // Translation added later.
],
'redirect' => [
'type' => 'url',
'title' => '', // Translation added later.
'default_value' => '',
'description' => '', // Translation added later.
],
],
'social' => [],
'schema' => [
'schema_page_type' => [
'type' => 'hidden',
'title' => '',
'options' => Schema_Types::PAGE_TYPES,
'options' => Schema_Types::PAGE_TYPES,
],
'schema_article_type' => [
'type' => 'hidden',
'title' => '',
'hide_on_pages' => true,
'options' => Schema_Types::ARTICLE_TYPES,
],
Expand Down Expand Up @@ -258,10 +200,10 @@ class WPSEO_Meta {
* @var array
*/
private static $social_fields = [
'title' => 'hidden',
'description' => 'hidden',
'image' => 'hidden',
'image-id' => 'hidden',
'title',
'description',
'image',
'image-id',
];

/**
Expand All @@ -272,17 +214,14 @@ class WPSEO_Meta {
public static function init() {
foreach ( self::$social_networks as $option => $network ) {
if ( WPSEO_Options::get( $option, false ) === true ) {
foreach ( self::$social_fields as $box => $type ) {
self::$meta_fields['social'][ $network . '-' . $box ] = [
'type' => $type,
'title' => '', // Translation added later.
foreach ( self::$social_fields as $key ) {
self::$meta_fields['social'][ $network . '-' . $key ] = [
'default_value' => '',
'description' => '', // Translation added later.
];
}
}
}
unset( $option, $network, $box, $type );
unset( $option, $network, $key );

/**
* Allow add-on plugins to register their meta fields for management by this class.
Expand Down Expand Up @@ -367,13 +306,6 @@ public static function get_meta_field_defs( $tab, $post_type = 'post' ) {
return [];
}

/* Adjust the no-index text strings based on the post type. */
$post_type_object = get_post_type_object( $post_type );

$field_defs['meta-robots-noindex']['title'] = sprintf( $field_defs['meta-robots-noindex']['title'], $post_type_object->labels->singular_name );
$field_defs['meta-robots-noindex']['options']['0'] = sprintf( $field_defs['meta-robots-noindex']['options']['0'], ( ( WPSEO_Options::get( 'noindex-' . $post_type, false ) === true ) ? $field_defs['meta-robots-noindex']['options']['1'] : $field_defs['meta-robots-noindex']['options']['2'] ), $post_type_object->label );
$field_defs['meta-robots-nofollow']['title'] = sprintf( $field_defs['meta-robots-nofollow']['title'], $post_type_object->labels->singular_name );

/* Don't show the breadcrumb title field if breadcrumbs aren't enabled. */
if ( WPSEO_Options::get( 'breadcrumbs-enable', false ) !== true && ! current_theme_supports( 'yoast-seo-breadcrumbs' ) ) {
unset( $field_defs['bctitle'] );
Expand Down Expand Up @@ -434,86 +366,67 @@ public static function sanitize_post_meta( $meta_value, $meta_key ) {
$field_def = self::$meta_fields[ self::$fields_index[ $meta_key ]['subset'] ][ self::$fields_index[ $meta_key ]['key'] ];
$clean = self::$defaults[ $meta_key ];

switch ( true ) {
case ( $meta_key === self::$meta_prefix . 'linkdex' ):
$key = str_replace( self::$meta_prefix, '', $meta_key );

switch ( $key ) {
case 'linkdex':
case 'content_score':
case 'inclusive_language_score':
$int = WPSEO_Utils::validate_int( $meta_value );
if ( $int !== false && $int >= 0 ) {
$clean = strval( $int ); // Convert to string to make sure default check works.
}
break;

case ( $field_def['type'] === 'checkbox' ):
// Only allow value if it's one of the predefined options.
if ( in_array( $meta_value, [ 'on', 'off' ], true ) ) {
$clean = $meta_value;
}
break;

case ( $field_def['type'] === 'select' || $field_def['type'] === 'radio' ):
// Only allow value if it's one of the predefined options.
if ( isset( $field_def['options'][ $meta_value ] ) ) {
$clean = $meta_value;
}
break;

case ( $field_def['type'] === 'hidden' && $meta_key === self::$meta_prefix . 'meta-robots-adv' ):
case 'meta-robots-adv':
$clean = self::validate_meta_robots_adv( $meta_value );
break;

case ( $field_def['type'] === 'url' || $meta_key === self::$meta_prefix . 'canonical' ):
case 'redirect':
case 'canonical':
// Validate as url(-part).
$url = WPSEO_Utils::sanitize_url( $meta_value );
if ( $url !== '' ) {
$clean = $url;
}
break;

case ( $field_def['type'] === 'upload' && in_array( $meta_key, [ self::$meta_prefix . 'opengraph-image', self::$meta_prefix . 'twitter-image' ], true ) ):
case 'opengraph-image':
case 'twitter-image':
// Validate as url.
$url = WPSEO_Utils::sanitize_url( $meta_value, [ 'http', 'https', 'ftp', 'ftps' ] );
if ( $url !== '' ) {
$clean = $url;
}
break;

case ( $field_def['type'] === 'hidden' && $meta_key === self::$meta_prefix . 'is_cornerstone' ):
$clean = $meta_value;

case 'is_cornerstone':
/*
* This used to be a checkbox, then became a hidden input.
* To make sure the value remains consistent, we cast 'true' to '1'.
*/
if ( $meta_value === 'true' ) {
if ( in_array( $meta_value, [ '1', 'true' ], true ) ) {
$clean = '1';
}
if ( in_array( $meta_value, [ '0', 'false' ], true ) ) {
$clean = '0';
}
break;

case ( $field_def['type'] === 'hidden' && isset( $field_def['options'] ) ):
case 'meta-robots-noindex':
case 'meta-robots-nofollow':
case 'schema_page_type':
case 'schema_article_type':
// Only allow value if it's one of the predefined options.
if ( isset( $field_def['options'][ $meta_value ] ) ) {
$clean = $meta_value;
}
break;

case ( $field_def['type'] === 'textarea' ):
if ( is_string( $meta_value ) ) {
// Remove line breaks and tabs.
// @todo [JRF => Yoast] Verify that line breaks and the likes aren't allowed/recommended in meta header fields.
$meta_value = str_replace( [ "\n", "\r", "\t", ' ' ], ' ', $meta_value );
$clean = WPSEO_Utils::sanitize_text_field( trim( $meta_value ) );
}
break;

case ( $field_def['type'] === 'multiselect' ):
$clean = $meta_value;
break;

case ( $field_def['type'] === 'text' ):
default:
if ( is_string( $meta_value ) ) {
$clean = WPSEO_Utils::sanitize_text_field( trim( $meta_value ) );
}

break;
}

Expand Down
Loading
Loading