' . sxseo_json( array( '@context' => 'https://schema.org', '@graph' => $graph ) ) . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput } } private static function id( $frag ) { return home_url( '/' ) . '#' . $frag; } public static function organisation() { $s = SXSEO_Settings::all(); $type = in_array( $s['org_type'], array( 'Organization', 'LocalBusiness', 'Person', 'Corporation', 'ProfessionalService', 'Store', 'Restaurant', 'EducationalOrganization', 'NGO' ), true ) ? $s['org_type'] : 'Organization'; $local = ! in_array( $type, array( 'Organization', 'Person', 'Corporation', 'NGO', 'EducationalOrganization' ), true ); $org = array( // Business types are also LocalBusiness so maps and local results can use them. '@type' => ( $local && 'LocalBusiness' !== $type ) ? array( $type, 'LocalBusiness' ) : $type, '@id' => self::id( 'organization' ), 'name' => $s['org_name'] ?: get_bloginfo( 'name' ), 'url' => home_url( '/' ), ); if ( $s['org_legal_name'] ) { $org['legalName'] = $s['org_legal_name']; } if ( $s['org_description'] ) { $org['description'] = $s['org_description']; } $topics = array_values( array_filter( array_map( 'trim', explode( ',', (string) $s['org_knows_about'] ) ) ) ); if ( $topics ) { $org['knowsAbout'] = $topics; } if ( $s['org_logo'] ) { $org['logo'] = array( '@type' => 'ImageObject', '@id' => self::id( 'logo' ), 'url' => $s['org_logo'], 'contentUrl' => $s['org_logo'] ); $org['image'] = array( '@id' => self::id( 'logo' ) ); } if ( $s['org_phone'] ) { $org['telephone'] = $s['org_phone']; } if ( $s['org_email'] ) { $org['email'] = $s['org_email']; } $profiles = array_values( array_filter( array_map( 'esc_url_raw', preg_split( '/\r\n|\r|\n/', (string) $s['org_profiles'] ) ) ) ); if ( $profiles ) { $org['sameAs'] = $profiles; } if ( $s['org_street'] || $s['org_city'] || $s['org_postcode'] ) { $org['address'] = array_filter( array( '@type' => 'PostalAddress', 'streetAddress' => $s['org_street'], 'addressLocality' => $s['org_city'], 'addressRegion' => $s['org_region'], 'postalCode' => $s['org_postcode'], 'addressCountry' => $s['org_country'], ) ); } if ( $local ) { if ( $s['org_lat'] && $s['org_lng'] ) { $org['geo'] = array( '@type' => 'GeoCoordinates', 'latitude' => (float) $s['org_lat'], 'longitude' => (float) $s['org_lng'] ); } if ( $s['org_price_range'] ) { $org['priceRange'] = $s['org_price_range']; } $hours = array_values( array_filter( array_map( 'trim', preg_split( '/\r\n|\r|\n/', (string) $s['org_hours'] ) ) ) ); if ( $hours ) { $org['openingHours'] = $hours; } $areas = array_values( array_filter( array_map( 'trim', explode( ',', (string) $s['org_area'] ) ) ) ); if ( $areas ) { $org['areaServed'] = array_map( function ( $a ) { return array( '@type' => 'Place', 'name' => $a ); }, $areas ); } } return $org; } public static function graph( array $c ) { $g = array(); $g[] = self::organisation(); $site = array( '@type' => 'WebSite', '@id' => self::id( 'website' ), 'url' => home_url( '/' ), 'name' => get_bloginfo( 'name' ), 'publisher' => array( '@id' => self::id( 'organization' ) ), 'inLanguage'=> str_replace( '_', '-', get_locale() ), ); if ( get_bloginfo( 'description' ) ) { $site['description'] = get_bloginfo( 'description' ); } $site['potentialAction'] = array( '@type' => 'SearchAction', 'target' => array( '@type' => 'EntryPoint', 'urlTemplate' => home_url( '/' ) . '?s={search_term_string}' ), 'query-input' => 'required name=search_term_string', ); $g[] = $site; // Services offered, on the home page only (the site-wide organisation node already identifies the business). $services = array_values( array_filter( array_map( 'trim', preg_split( '/\r\n|\r|\n/', (string) SXSEO_Settings::get( 'org_services' ) ) ) ) ); if ( $services && in_array( $c['type'], array( 'home', 'front' ), true ) ) { $areas = array_values( array_filter( array_map( 'trim', explode( ',', (string) SXSEO_Settings::get( 'org_area' ) ) ) ) ); $items = array(); foreach ( $services as $i => $name ) { $svc = array( '@type' => 'Service', 'position' => $i + 1, 'name' => $name, 'provider' => array( '@id' => self::id( 'organization' ) ) ); if ( $areas ) { $svc['areaServed'] = array_map( function ( $a ) { return array( '@type' => 'AdministrativeArea', 'name' => $a ); }, $areas ); } $items[] = $svc; } $g[] = array( '@type' => 'ItemList', '@id' => self::id( 'services' ), 'name' => ( SXSEO_Settings::get( 'org_name' ) ?: get_bloginfo( 'name' ) ) . ' services', 'itemListElement' => $items ); } if ( '' === $c['url'] ) { return $g; } $page_id = $c['url'] . '#webpage'; $page = array( '@type' => 'WebPage', '@id' => $page_id, 'url' => $c['url'], 'name' => $c['title'], 'isPartOf' => array( '@id' => self::id( 'website' ) ), 'inLanguage' => str_replace( '_', '-', get_locale() ), ); if ( $c['desc'] ) { $page['description'] = $c['desc']; } if ( in_array( $c['type'], array( 'term', 'archive', 'author', 'date' ), true ) ) { $page['@type'] = 'CollectionPage'; } elseif ( 'home' === $c['type'] || 'front' === $c['type'] ) { $page['@type'] = 'WebPage'; } elseif ( 'page' === $c['type'] && $c['post'] instanceof WP_Post ) { $slug = $c['post']->post_name; if ( preg_match( '/(^|-)(contact)(-|$)/', $slug ) ) { $page['@type'] = 'ContactPage'; } elseif ( preg_match( '/(^|-)(about)(-|$)/', $slug ) ) { $page['@type'] = 'AboutPage'; } } $img_id = null; if ( $c['image'] ) { $img_id = $c['url'] . '#primaryimage'; $g[] = array( '@type' => 'ImageObject', '@id' => $img_id, 'url' => $c['image'], 'contentUrl' => $c['image'] ); $page['primaryImageOfPage'] = array( '@id' => $img_id ); $page['image'] = array( '@id' => $img_id ); } $crumbs = self::breadcrumbs( $c ); if ( count( $crumbs ) > 1 ) { $items = array(); foreach ( $crumbs as $i => $cr ) { $items[] = array( '@type' => 'ListItem', 'position' => $i + 1, 'name' => $cr['name'], 'item' => $cr['url'] ); } $g[] = array( '@type' => 'BreadcrumbList', '@id' => $c['url'] . '#breadcrumb', 'itemListElement' => $items ); $page['breadcrumb'] = array( '@id' => $c['url'] . '#breadcrumb' ); } $post = $c['post']; if ( $post instanceof WP_Post && in_array( $c['type'], array( 'post', 'page', 'front' ), true ) ) { $page['datePublished'] = get_post_time( 'c', true, $post ); $page['dateModified'] = get_post_modified_time( 'c', true, $post ); $schema_type = $c['ctx']['custom']['schema_type'] ?? ''; $is_article = 'post' === $post->post_type || in_array( $schema_type, array( 'Article', 'BlogPosting', 'NewsArticle' ), true ); if ( $is_article && 'page' !== $post->post_type ) { $author_id = self::id( 'author-' . $post->post_author ); $g[] = array( '@type' => 'Person', '@id' => $author_id, 'name' => get_the_author_meta( 'display_name', $post->post_author ), 'url' => get_author_posts_url( $post->post_author ), ); $art = array( '@type' => in_array( $schema_type, array( 'Article', 'BlogPosting', 'NewsArticle' ), true ) ? $schema_type : sxseo_opt( 'article_type', 'BlogPosting' ), '@id' => $c['url'] . '#article', 'isPartOf' => array( '@id' => $page_id ), 'mainEntityOfPage' => array( '@id' => $page_id ), 'headline' => sxseo_trim( sxseo_title_text( $post ), 110 ), 'datePublished' => get_post_time( 'c', true, $post ), 'dateModified' => get_post_modified_time( 'c', true, $post ), 'author' => array( '@id' => $author_id ), 'publisher' => array( '@id' => self::id( 'organization' ) ), 'inLanguage' => str_replace( '_', '-', get_locale() ), 'wordCount' => str_word_count( sxseo_plain( $post->post_content ) ), ); if ( $c['desc'] ) { $art['description'] = $c['desc']; } if ( $img_id ) { $art['image'] = array( '@id' => $img_id ); } if ( ! empty( $c['ctx']['terms']['primary'] ) ) { $art['articleSection'] = $c['ctx']['terms']['primary']; } if ( $c['keywords'] ) { $art['keywords'] = implode( ', ', $c['keywords'] ); } $g[] = $art; $page['@type'] = 'WebPage'; } elseif ( in_array( $schema_type, array( 'Service', 'Product' ), true ) ) { $node = array( '@type' => $schema_type, '@id' => $c['url'] . '#main', 'name' => sxseo_title_text( $post ), 'description' => $c['desc'], 'url' => $c['url'], ); $node['Service' === $schema_type ? 'provider' : 'brand'] = array( '@id' => self::id( 'organization' ) ); if ( $img_id ) { $node['image'] = array( '@id' => $img_id ); } $g[] = $node; } $faq = $c['ctx']['custom']['faq'] ?? array(); if ( $faq ) { $qa = array(); foreach ( $faq as $row ) { $qa[] = array( '@type' => 'Question', 'name' => $row['q'], 'acceptedAnswer' => array( '@type' => 'Answer', 'text' => $row['a'] ) ); } $g[] = array( '@type' => 'FAQPage', '@id' => $c['url'] . '#faq', 'isPartOf' => array( '@id' => $page_id ), 'mainEntity' => $qa ); } } $g[] = $page; return $g; } /** Trail of {name,url} from home to the current page. Shared with the [sxseo_breadcrumbs] shortcode. */ public static function breadcrumbs( array $c ) { $trail = array( array( 'name' => __( 'Home', 'sentrixa-seo' ), 'url' => home_url( '/' ) ) ); $post = $c['post']; if ( $post instanceof WP_Post && ! in_array( $c['type'], array( 'front' ), true ) ) { if ( 'post' === $post->post_type ) { $cats = get_the_category( $post->ID ); if ( $cats ) { $primary = $cats[0]; foreach ( array_reverse( get_ancestors( $primary->term_id, 'category' ) ) as $anc ) { $t = get_term( $anc, 'category' ); if ( $t && ! is_wp_error( $t ) ) { $trail[] = array( 'name' => $t->name, 'url' => get_term_link( $t ) ); } } if ( 'uncategorized' !== $primary->slug ) { $trail[] = array( 'name' => $primary->name, 'url' => get_term_link( $primary ) ); } } } elseif ( 'page' !== $post->post_type ) { $pto = get_post_type_object( $post->post_type ); if ( $pto && $pto->has_archive ) { $trail[] = array( 'name' => $pto->labels->name, 'url' => get_post_type_archive_link( $post->post_type ) ); } } foreach ( array_reverse( get_post_ancestors( $post ) ) as $anc ) { $trail[] = array( 'name' => sxseo_title_text( $anc ), 'url' => get_permalink( $anc ) ); } $trail[] = array( 'name' => sxseo_title_text( $post ), 'url' => get_permalink( $post ) ); } elseif ( 'term' === $c['type'] && $c['object'] instanceof WP_Term ) { foreach ( array_reverse( get_ancestors( $c['object']->term_id, $c['object']->taxonomy ) ) as $anc ) { $t = get_term( $anc, $c['object']->taxonomy ); if ( $t && ! is_wp_error( $t ) ) { $trail[] = array( 'name' => $t->name, 'url' => get_term_link( $t ) ); } } $trail[] = array( 'name' => $c['object']->name, 'url' => get_term_link( $c['object'] ) ); } elseif ( in_array( $c['type'], array( 'archive', 'author', 'date' ), true ) ) { $trail[] = array( 'name' => trim( wp_strip_all_tags( get_the_archive_title() ) ), 'url' => $c['url'] ); } return $trail; } } function sxseo_breadcrumbs_html() { $c = SXSEO_Context::current(); $trail = SXSEO_Schema::breadcrumbs( $c ); if ( count( $trail ) < 2 ) { return ''; } $sep = ' ' . esc_html( sxseo_opt( 'breadcrumb_sep', '›' ) ) . ' '; $out = array(); $last = count( $trail ) - 1; foreach ( $trail as $i => $t ) { $out[] = $i === $last ? '' . esc_html( $t['name'] ) . '' : '' . esc_html( $t['name'] ) . ''; } return ''; } add_shortcode( 'sxseo_breadcrumbs', 'sxseo_breadcrumbs_html' );