[
'label' => __( 'Credentials / Job Title', 'adworksbid-seo' ),
'type' => 'text',
'sanitizer' => 'sanitize_text_field',
'hint' => __( 'e.g. "Board-Certified Dermatologist, MD" or "خبيرة تسويق رقمي" - output as jobTitle in this author\'s schema.org markup.', 'adworksbid-seo' ),
],
'name_ar' => [
'label' => __( 'Arabic Display Name', 'adworksbid-seo' ),
'type' => 'text',
'sanitizer' => 'sanitize_text_field',
'hint' => __( 'Optional. Used as this author\'s primary schema name on Arabic posts (with the name above kept as an alternate), and the reverse on non-Arabic posts.', 'adworksbid-seo' ),
],
'knows_about' => [
'label' => __( 'Areas of Expertise', 'adworksbid-seo' ),
'type' => 'text',
'sanitizer' => 'sanitize_text_field',
'hint' => __( 'Comma-separated, e.g. "Dermatology, Skin Cancer Prevention". Output as knowsAbout in schema.', 'adworksbid-seo' ),
],
'sameas' => [
'label' => __( 'Professional Profile Links', 'adworksbid-seo' ),
'type' => 'textarea',
'sanitizer' => [ __CLASS__, 'sanitize_sameas' ],
'hint' => __( 'One URL per line - LinkedIn, a hospital/firm bio page, a professional directory listing, etc. Output as sameAs in schema, a core E-E-A-T trust signal.', 'adworksbid-seo' ),
],
];
}
public static function sanitize_sameas( $raw ) {
$lines = preg_split( '/[\r\n]+/', (string) $raw );
$urls = array_filter( array_map( 'esc_url_raw', array_map( 'trim', $lines ) ) );
return implode( "\n", $urls );
}
public static function render_fields( $user ) {
if ( ! current_user_can( 'edit_user', $user->ID ) ) {
return;
}
?>
$field ) {
$raw_key = 'awbseo_author_' . $key;
if ( ! isset( $_POST[ $raw_key ] ) ) {
continue;
}
$value = call_user_func( $field['sanitizer'], wp_unslash( $_POST[ $raw_key ] ) );
update_user_meta( $user_id, self::META_PREFIX . $key, $value );
}
}
/**
* Builds a schema.org Person block for this author, enriched with
* whatever E-E-A-T fields above are filled in - falls back gracefully to
* just the WordPress display name and author archive URL when none are
* set, same as the plugin's previous bare-minimum author block.
*
* $is_arabic_content decides which of the WordPress display name / the
* Arabic name field is primary vs alternateName, so an Arabic post
* surfaces this author's Arabic name first - matching what a reader or
* an AI engine parsing that specific page actually sees - while a
* non-Arabic post keeps the WordPress display name primary.
*/
public static function get_author_schema( $user_id, $is_arabic_content = false ) {
$user = get_userdata( $user_id );
if ( ! $user ) {
return [ '@type' => 'Person', 'name' => '' ];
}
$credentials = get_user_meta( $user_id, self::META_PREFIX . 'credentials', true );
$name_ar = get_user_meta( $user_id, self::META_PREFIX . 'name_ar', true );
$knows_raw = get_user_meta( $user_id, self::META_PREFIX . 'knows_about', true );
$sameas_raw = get_user_meta( $user_id, self::META_PREFIX . 'sameas', true );
$primary_name = $user->display_name;
$alternate_name = '';
if ( $name_ar ) {
if ( $is_arabic_content ) {
$primary_name = $name_ar;
$alternate_name = $user->display_name;
} else {
$alternate_name = $name_ar;
}
}
$person = [
'@type' => 'Person',
'name' => $primary_name,
'url' => get_author_posts_url( $user_id ),
];
if ( $alternate_name ) {
$person['alternateName'] = $alternate_name;
}
if ( $credentials ) {
$person['jobTitle'] = $credentials;
}
if ( $user->description ) {
$person['description'] = wp_strip_all_tags( $user->description );
}
$avatar = get_avatar_url( $user_id );
if ( $avatar ) {
$person['image'] = $avatar;
}
if ( $knows_raw ) {
$topics = array_values( array_filter( array_map( 'trim', explode( ',', $knows_raw ) ) ) );
if ( $topics ) {
$person['knowsAbout'] = $topics;
}
}
if ( $sameas_raw ) {
$links = array_values( array_filter( array_map( 'trim', preg_split( '/[\r\n]+/', $sameas_raw ) ) ) );
if ( $links ) {
$person['sameAs'] = $links;
}
}
return $person;
}
}