[ '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 ) : $value = get_user_meta( $user->ID, self::META_PREFIX . $key, true ); ?>

$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; } }