Файловый менеджер - Редактировать - /home/kunzqhe/photostocker/2/generators.tar
Назад
schema/third-party/events-calendar-schema.php 0000644 00000014775 15154134645 0015323 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema\Third_Party; use stdClass; use Tribe__Events__JSON_LD__Event; use Tribe__Events__Template__Month; use Yoast\WP\SEO\Config\Schema_IDs; use Yoast\WP\SEO\Context\Meta_Tags_Context; use Yoast\WP\SEO\Generators\Schema\Abstract_Schema_Piece; use Yoast\WP\SEO\Surfaces\Helpers_Surface; /** * A class to handle textdomains and other Yoast Event Schema related logic.. */ class Events_Calendar_Schema extends Abstract_Schema_Piece { /** * The meta tags context. * * @var Meta_Tags_Context */ public $context; /** * The helpers surface * * @var Helpers_Surface */ public $helpers; /** * Determines whether or not a piece should be added to the graph. * * @return bool */ public function is_needed() { if ( \is_single() && \get_post_type() === 'tribe_events' ) { // The single event view. return true; } elseif ( \tribe_is_month() ) { // The month event view. return true; } return false; } /** * Adds our Event piece of the graph. * Partially lifted from the 'Tribe__JSON_LD__Abstract' class. * * @see https://docs.theeventscalendar.com/reference/classes/tribe__json_ld__abstract/ * @return array Event Schema markup */ public function generate() { $posts = []; if ( \is_singular( 'tribe_events' ) ) { global $post; $posts[] = $post; } elseif ( \tribe_is_month() ) { $posts = $this->get_month_events(); } $tribe_data = $this->get_tribe_schema( $posts ); $tribe_data = $this->transform_tribe_schema( $tribe_data ); $data = []; foreach ( $tribe_data as $t ) { // Cast the schema object as array, the Yoast Class can't handle objects. $data[] = (array) $t; } // If the resulting array only has one entry, print it directly. if ( \count( $data ) === 1 ) { $data = $data[0]; $data['mainEntityOfPage'] = [ '@id' => $this->context->canonical . Schema_IDs::WEBPAGE_HASH ]; } elseif ( \count( $data ) === 0 ) { $data = false; } return $data; } /** * Get and return the schema markup for a collection of posts. * If the posts array is empty, only the current post is returned. * * @param array $posts The collection of posts we want schema markup for. * * @return array The tribe schema for these posts. */ private function get_tribe_schema( array $posts = [] ) { $args = [ // We do not want the @context to be shown. 'context' => false, ]; $tribe_data = Tribe__Events__JSON_LD__Event::instance()->get_data( $posts, $args ); $type = \strtolower( \esc_attr( Tribe__Events__JSON_LD__Event::instance()->type ) ); foreach ( $tribe_data as $post_id => $_data ) { Tribe__Events__JSON_LD__Event::instance()->set_type( $post_id, $type ); // Register this post as done already. Tribe__Events__JSON_LD__Event::instance()->register( $post_id ); } /** * Allows the event data to be modifed by themes and other plugins. * * @example yoast_tec_json_ld_thing_data * @example yoast_tec_json_ld_event_data * * @param array $data objects representing the Google Markup for each event. * @param array $args the arguments used to get data */ $tribe_data = \apply_filters( "yoast_tec_json_ld_{$type}_data", $tribe_data, $args ); return $tribe_data; } /** * Transform the tribe schema markup and adapt it to the Yoast SEO standard. * * @param array $data The data retrieved from the TEC plugin. * * @return array The transformed event data. */ private function transform_tribe_schema( array $data = [] ) { $new_data = []; foreach ( $data as $post_id => $d ) { $permalink = \get_permalink( $post_id ); // EVENT. // Generate an @id for the event. $d->{'@id'} = $permalink . '#' . \strtolower( \esc_attr( $d->{'@type'} ) ); // Transform the post_thumbnail from the url to the @id of #primaryimage. if ( \has_post_thumbnail( $post_id ) ) { if ( \is_singular( 'tribe_events' ) ) { // On a single view we can assume that Yoast SEO already printed the // image schema for the post thumbnail. $d->image = (object) [ '@id' => $permalink . '#primaryimage', ]; } else { $image_id = \get_post_thumbnail_id( $post_id ); $schema_id = $permalink . '#primaryimage'; $d->image = $this->helpers->schema->image->generate_from_attachment_id( $schema_id, $image_id ); } } if ( isset( $d->description ) && ! empty( $d->description ) ) { // By the time the description arrives in this plugin it is heavily // escaped. That's why we basically pull new text from the database. $d->description = \get_the_excerpt( $post_id ); } // ORGANIZER. if ( \tribe_has_organizer( $post_id ) ) { if ( ! $d->organizer ) { $d->organizer = new stdClass(); } $organizer_id = \tribe_get_organizer_id( $post_id ); $d->organizer->description = \get_the_excerpt( $organizer_id ); // Fix empty organizer/url and wrong organizer/sameAs. if ( isset( $d->organizer->sameAs ) && $d->organizer->url === false ) { $d->organizer->url = $d->organizer->sameAs; } unset( $d->organizer->sameAs ); } // VENUE / LOCATION. if ( \tribe_has_venue( $post_id ) ) { if ( ! $d->location ) { $d->location = new stdClass(); } $venue_id = \tribe_get_venue_id( $post_id ); $d->location->description = \get_the_excerpt( $venue_id ); } /* * PERFORMER * Unset the performer, as it is currently unused. * @see: https://github.com/moderntribe/the-events-calendar/blob/5e737eb820c59bb9639d9ee9f4b88931a51c8554/src/Tribe/JSON_LD/Event.php#L151 */ unset( $d->performer ); // OFFERS. if ( isset( $d->offers ) && \is_array( $d->offers ) ) { foreach ( $d->offers as $key => $offer ) { unset( $d->offers[ $key ]->category ); } } $new_data[ $post_id ] = $d; } return $new_data; } /** * Get an array of events for the requested month. * * @return array An array of posts of the custom post type event. */ private function get_month_events() { $wp_query = \tribe_get_global_query_object(); $event_date = $wp_query->get( 'eventDate' ); $month = $event_date; if ( empty( $month ) ) { $month = \tribe_get_month_view_date(); } $args = [ 'eventDisplay' => 'custom', 'start_date' => Tribe__Events__Template__Month::calculate_first_cell_date( $month ), 'end_date' => Tribe__Events__Template__Month::calculate_final_cell_date( $month ), 'posts_per_page' => -1, 'hide_upcoming' => true, ]; return \tribe_get_events( $args ); } } schema/main-image.php 0000644 00000003317 15154134645 0010535 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns ImageObject schema data. */ class Main_Image extends Abstract_Schema_Piece { /** * Determines whether or not a piece should be added to the graph. * * @return bool */ public function is_needed() { return $this->context->indexable->object_type === 'post'; } /** * Adds a main image for the current URL to the schema if there is one. * * This can be either a social image (Open Graph or Twitter), the featured image, * or fall back to the first image in the content of the page. * * @return false|array Image Schema. */ public function generate() { $image_id = $this->context->canonical . Schema_IDs::PRIMARY_IMAGE_HASH; // The Open Graph image. if ( isset( $this->context->indexable->open_graph_image_id ) && $this->context->indexable->open_graph_image_source === 'set-by-user' ) { return $this->helpers->schema->image->generate_from_attachment_id( $image_id, $this->context->indexable->open_graph_image_id ); } // The Twitter image. if ( isset( $this->context->indexable->twitter_image_id ) && $this->context->indexable->twitter_image_source === 'set-by-user' ) { return $this->helpers->schema->image->generate_from_attachment_id( $image_id, $this->context->indexable->twitter_image_id ); } // The featured image. if ( $this->context->main_image_id ) { return $this->helpers->schema->image->generate_from_attachment_id( $image_id, $this->context->main_image_id ); } // The first image in the content. if ( $this->context->main_image_url ) { return $this->helpers->schema->image->generate_from_url( $image_id, $this->context->main_image_url ); } return false; } } schema/author.php 0000644 00000005445 15154134645 0010037 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns schema Author data. */ class Author extends Person { /** * Determine whether we should return Person schema. * * @return bool */ public function is_needed() { if ( $this->context->indexable->object_type === 'user' ) { return true; } if ( $this->context->indexable->object_type === 'post' && $this->helpers->schema->article->is_author_supported( $this->context->indexable->object_sub_type ) && $this->context->schema_article_type !== 'None' ) { return true; } return false; } /** * Returns Person Schema data. * * @return bool|array Person data on success, false on failure. */ public function generate() { $user_id = $this->determine_user_id(); if ( ! $user_id ) { return false; } $data = $this->build_person_data( $user_id ); if ( $this->site_represents_current_author() === false ) { $data['@type'] = [ 'Person' ]; unset( $data['logo'] ); } // If this is an author page, the Person object is the main object, so we set it as such here. if ( $this->context->indexable->object_type === 'user' ) { $data['mainEntityOfPage'] = [ '@id' => $this->context->canonical . Schema_IDs::WEBPAGE_HASH, ]; } // If this is a post and the author archives are enabled, set the author archive url as the author url. if ( $this->context->indexable->object_type === 'post' ) { if ( $this->helpers->options->get( 'disable-author' ) !== true ) { $data['url'] = $this->helpers->user->get_the_author_posts_url( $user_id ); } } return $data; } /** * Determines a User ID for the Person data. * * @return bool|int User ID or false upon return. */ protected function determine_user_id() { $user_id = 0; if ( $this->context->indexable->object_type === 'post' ) { $user_id = (int) $this->context->post->post_author; } if ( $this->context->indexable->object_type === 'user' ) { $user_id = $this->context->indexable->object_id; } /** * Filter: 'wpseo_schema_person_user_id' - Allows filtering of user ID used for person output. * * @api int|bool $user_id The user ID currently determined. */ $user_id = \apply_filters( 'wpseo_schema_person_user_id', $user_id ); if ( \is_int( $user_id ) && $user_id > 0 ) { return $user_id; } return false; } /** * An author should not have an image from options, this only applies to persons. * * @param array $data The Person schema. * @param string $schema_id The string used in the `@id` for the schema. * * @return array The Person schema. */ protected function set_image_from_options( $data, $schema_id ) { if ( $this->site_represents_current_author() ) { return parent::set_image_from_options( $data, $schema_id ); } return $data; } } schema/faq.php 0000644 00000005623 15154134645 0007302 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; /** * Returns schema FAQ data. */ class FAQ extends Abstract_Schema_Piece { /** * Determines whether or not a piece should be added to the graph. * * @return bool */ public function is_needed() { if ( empty( $this->context->blocks['yoast/faq-block'] ) ) { return false; } if ( ! \is_array( $this->context->schema_page_type ) ) { $this->context->schema_page_type = [ $this->context->schema_page_type ]; } $this->context->schema_page_type[] = 'FAQPage'; $this->context->main_entity_of_page = $this->generate_ids(); return true; } /** * Generate the IDs so we can link to them in the main entity. * * @return array */ private function generate_ids() { $ids = []; foreach ( $this->context->blocks['yoast/faq-block'] as $block ) { foreach ( $block['attrs']['questions'] as $index => $question ) { if ( ! isset( $question['jsonAnswer'] ) || empty( $question['jsonAnswer'] ) ) { continue; } $ids[] = [ '@id' => $this->context->canonical . '#' . \esc_attr( $question['id'] ) ]; } } return $ids; } /** * Render a list of questions, referencing them by ID. * * @return array Our Schema graph. */ public function generate() { $graph = []; $questions = []; foreach ( $this->context->blocks['yoast/faq-block'] as $index => $block ) { $questions = \array_merge( $questions, $block['attrs']['questions'] ); } foreach ( $questions as $index => $question ) { if ( ! isset( $question['jsonAnswer'] ) || empty( $question['jsonAnswer'] ) ) { continue; } $graph[] = $this->generate_question_block( $question, ( $index + 1 ) ); } return $graph; } /** * Generate a Question piece. * * @param array $question The question to generate schema for. * @param int $position The position of the question. * * @return array Schema.org Question piece. */ protected function generate_question_block( $question, $position ) { $url = $this->context->canonical . '#' . \esc_attr( $question['id'] ); $data = [ '@type' => 'Question', '@id' => $url, 'position' => $position, 'url' => $url, 'name' => $this->helpers->schema->html->smart_strip_tags( $question['jsonQuestion'] ), 'answerCount' => 1, 'acceptedAnswer' => $this->add_accepted_answer_property( $question ), ]; $data = $this->helpers->schema->language->add_piece_language( $data ); return $data; } /** * Adds the Questions `acceptedAnswer` property. * * @param array $question The question to add the acceptedAnswer to. * * @return array Schema.org Question piece. */ protected function add_accepted_answer_property( $question ) { $data = [ '@type' => 'Answer', 'text' => $this->helpers->schema->html->sanitize( $question['jsonAnswer'] ), ]; $data = $this->helpers->schema->language->add_piece_language( $data ); return $data; } } schema/person.php 0000644 00000020241 15154134645 0010032 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use WP_User; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns schema Person data. */ class Person extends Abstract_Schema_Piece { /** * Array of the social profiles we display for a Person. * * @var string[] */ private $social_profiles = [ 'facebook', 'instagram', 'linkedin', 'pinterest', 'twitter', 'myspace', 'youtube', 'soundcloud', 'tumblr', 'wikipedia', ]; /** * The Schema type we use for this class. * * @var string[] */ protected $type = [ 'Person', 'Organization' ]; /** * Determine whether we should return Person schema. * * @return bool */ public function is_needed() { // Using an author piece instead. if ( $this->site_represents_current_author() ) { return false; } return $this->context->site_represents === 'person' || $this->context->indexable->object_type === 'user'; } /** * Returns Person Schema data. * * @return bool|array Person data on success, false on failure. */ public function generate() { $user_id = $this->determine_user_id(); if ( ! $user_id ) { return false; } return $this->build_person_data( $user_id ); } /** * Determines a User ID for the Person data. * * @return bool|int User ID or false upon return. */ protected function determine_user_id() { /** * Filter: 'wpseo_schema_person_user_id' - Allows filtering of user ID used for person output. * * @api int|bool $user_id The user ID currently determined. */ $user_id = \apply_filters( 'wpseo_schema_person_user_id', $this->context->site_user_id ); // It should to be an integer higher than 0. if ( \is_int( $user_id ) && $user_id > 0 ) { return $user_id; } return false; } /** * Retrieve a list of social profile URLs for Person. * * @param array $same_as_urls Array of SameAs URLs. * @param int $user_id User ID. * * @return string[] A list of SameAs URLs. */ protected function get_social_profiles( $same_as_urls, $user_id ) { /** * Filter: 'wpseo_schema_person_social_profiles' - Allows filtering of social profiles per user. * * @param int $user_id The current user we're grabbing social profiles for. * * @api string[] $social_profiles The array of social profiles to retrieve. Each should be a user meta field * key. As they are retrieved using the WordPress function `get_the_author_meta`. */ $social_profiles = \apply_filters( 'wpseo_schema_person_social_profiles', $this->social_profiles, $user_id ); // We can only handle an array. if ( ! \is_array( $social_profiles ) ) { return $same_as_urls; } foreach ( $social_profiles as $profile ) { // Skip non-string values. if ( ! \is_string( $profile ) ) { continue; } $social_url = $this->url_social_site( $profile, $user_id ); if ( $social_url ) { $same_as_urls[] = $social_url; } } return $same_as_urls; } /** * Builds our array of Schema Person data for a given user ID. * * @param int $user_id The user ID to use. * * @return array An array of Schema Person data. */ protected function build_person_data( $user_id ) { $user_data = \get_userdata( $user_id ); $data = [ '@type' => $this->type, '@id' => $this->helpers->schema->id->get_user_schema_id( $user_id, $this->context ), ]; // Safety check for the `get_userdata` WP function, which could return false. if ( $user_data === false ) { return $data; } $data['name'] = $this->helpers->schema->html->smart_strip_tags( $user_data->display_name ); $data = $this->add_image( $data, $user_data ); if ( ! empty( $user_data->description ) ) { $data['description'] = $this->helpers->schema->html->smart_strip_tags( $user_data->description ); } $data = $this->add_same_as_urls( $data, $user_data, $user_id ); /** * Filter: 'wpseo_schema_person_data' - Allows filtering of schema data per user. * * @param array $data The schema data we have for this person. * @param int $user_id The current user we're collecting schema data for. */ $data = \apply_filters( 'wpseo_schema_person_data', $data, $user_id ); return $data; } /** * Returns an ImageObject for the persons avatar. * * @param array $data The Person schema. * @param WP_User $user_data User data. * * @return array The Person schema. */ protected function add_image( $data, $user_data ) { $schema_id = $this->context->site_url . Schema_IDs::PERSON_LOGO_HASH; $data = $this->set_image_from_options( $data, $schema_id ); if ( ! isset( $data['image'] ) ) { $data = $this->set_image_from_avatar( $data, $user_data, $schema_id ); } if ( \is_array( $this->type ) && \in_array( 'Organization', $this->type, true ) ) { $data['logo'] = [ '@id' => $schema_id ]; } return $data; } /** * Generate the person image from our settings. * * @param array $data The Person schema. * @param string $schema_id The string used in the `@id` for the schema. * * @return array The Person schema. */ protected function set_image_from_options( $data, $schema_id ) { if ( $this->context->site_represents !== 'person' ) { return $data; } if ( \is_array( $this->context->person_logo_meta ) ) { $data['image'] = $this->helpers->schema->image->generate_from_attachment_meta( $schema_id, $this->context->person_logo_meta, $data['name'] ); } return $data; } /** * Generate the person logo from gravatar. * * @param array $data The Person schema. * @param WP_User $user_data User data. * @param string $schema_id The string used in the `@id` for the schema. * * @return array The Person schema. */ protected function set_image_from_avatar( $data, $user_data, $schema_id ) { // If we don't have an image in our settings, fall back to an avatar, if we're allowed to. $show_avatars = \get_option( 'show_avatars' ); if ( ! $show_avatars ) { return $data; } $url = \get_avatar_url( $user_data->user_email ); if ( empty( $url ) ) { return $data; } $data['image'] = $this->helpers->schema->image->simple_image_object( $schema_id, $url, $user_data->display_name ); return $data; } /** * Returns an author's social site URL. * * @param string $social_site The social site to retrieve the URL for. * @param mixed $user_id The user ID to use function outside of the loop. * * @return string */ protected function url_social_site( $social_site, $user_id = false ) { $url = \get_the_author_meta( $social_site, $user_id ); if ( ! empty( $url ) && $social_site === 'twitter' ) { $url = 'https://twitter.com/' . $url; } return $url; } /** * Checks the site is represented by the same person as this indexable. * * @return bool True when the site is represented by the same person as this indexable. */ protected function site_represents_current_author() { // Can only be the case when the site represents a user. if ( $this->context->site_represents !== 'person' ) { return false; } // Article post from the same user as the site represents. if ( $this->context->indexable->object_type === 'post' && $this->helpers->schema->article->is_author_supported( $this->context->indexable->object_sub_type ) && $this->context->schema_article_type !== 'None' ) { return $this->context->site_user_id === $this->context->indexable->author_id; } // Author archive from the same user as the site represents. return $this->context->indexable->object_type === 'user' && $this->context->site_user_id === $this->context->indexable->object_id; } /** * Builds our SameAs array. * * @param array $data The Person schema data. * @param WP_User $user_data The user data object. * @param int $user_id The user ID to use. * * @return array The Person schema data. */ protected function add_same_as_urls( $data, $user_data, $user_id ) { $same_as_urls = []; // Add the "Website" field from WordPress' contact info. if ( ! empty( $user_data->user_url ) ) { $same_as_urls[] = $user_data->user_url; } // Add the social profiles. $same_as_urls = $this->get_social_profiles( $same_as_urls, $user_id ); if ( ! empty( $same_as_urls ) ) { $data['sameAs'] = $same_as_urls; } return $data; } } schema/breadcrumb.php 0000644 00000011117 15154134645 0010634 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns schema Breadcrumb data. */ class Breadcrumb extends Abstract_Schema_Piece { /** * Determine if we should add a breadcrumb attribute. * * @return bool */ public function is_needed() { if ( $this->context->indexable->object_type === 'system-page' && $this->context->indexable->object_sub_type === '404' ) { return false; } return true; } /** * Returns Schema breadcrumb data to allow recognition of page's position in the site hierarchy. * * @link https://developers.google.com/search/docs/data-types/breadcrumb * * @return bool|array Array on success, false on failure. */ public function generate() { $breadcrumbs = $this->context->presentation->breadcrumbs; $list_elements = []; // In case of pagination, replace the last breadcrumb, because it only contains "Page [number]" and has no URL. if ( ( $this->helpers->current_page->is_paged() || $this->context->indexable->number_of_pages > 1 ) && ( // Do not replace the last breadcrumb on static post pages. ! $this->helpers->current_page->is_static_posts_page() // Do not remove the last breadcrumb if only one exists (bugfix for custom paginated frontpages). && \count( $breadcrumbs ) > 1 ) ) { \array_pop( $breadcrumbs ); } // Only output breadcrumbs that are not hidden. $breadcrumbs = \array_filter( $breadcrumbs, [ $this, 'not_hidden' ] ); \reset( $breadcrumbs ); /* * Check whether at least one of the breadcrumbs is broken. * If so, do not output anything. */ foreach ( $breadcrumbs as $breadcrumb ) { if ( $this->is_broken( $breadcrumb ) ) { return false; } } // Create the last breadcrumb. $last_breadcrumb = \array_pop( $breadcrumbs ); $breadcrumbs[] = $this->format_last_breadcrumb( $last_breadcrumb ); // If this is a static front page, prevent nested pages from creating a trail. if ( $this->helpers->current_page->is_home_static_page() ) { // Check if we're dealing with a nested page. if ( \count( $breadcrumbs ) > 1 ) { // Store the breadcrumbs home variable before dropping the parent page from the Schema. $breadcrumbs_home = $breadcrumbs[0]['text']; $breadcrumbs = [ \array_pop( $breadcrumbs ) ]; // Make the child page show the breadcrumbs home variable rather than its own title. $breadcrumbs[0]['text'] = $breadcrumbs_home; } } // Create intermediate breadcrumbs. foreach ( $breadcrumbs as $index => $breadcrumb ) { $list_elements[] = $this->create_breadcrumb( $index, $breadcrumb ); } return [ '@type' => 'BreadcrumbList', '@id' => $this->context->canonical . Schema_IDs::BREADCRUMB_HASH, 'itemListElement' => $list_elements, ]; } /** * Returns a breadcrumb array. * * @param int $index The position in the list. * @param array $breadcrumb The position in the list. * * @return array A breadcrumb listItem. */ private function create_breadcrumb( $index, $breadcrumb ) { $crumb = [ '@type' => 'ListItem', 'position' => ( $index + 1 ), 'name' => $this->helpers->schema->html->smart_strip_tags( $breadcrumb['text'] ), ]; if ( ! empty( $breadcrumb['url'] ) ) { $crumb['item'] = $breadcrumb['url']; } return $crumb; } /** * Creates the last breadcrumb in the breadcrumb list, omitting the URL per Google's spec. * * @link https://developers.google.com/search/docs/data-types/breadcrumb * * @param array $breadcrumb The position in the list. * * @return array The last of the breadcrumbs. */ private function format_last_breadcrumb( $breadcrumb ) { unset( $breadcrumb['url'] ); return $breadcrumb; } /** * Tests if the breadcrumb is broken. * A breadcrumb is considered broken: * - when it is not an array. * - when it has no URL or text. * * @param array $breadcrumb The breadcrumb to test. * * @return bool `true` if the breadcrumb is broken. */ private function is_broken( $breadcrumb ) { // A breadcrumb is broken if it is not an array. if ( ! \is_array( $breadcrumb ) ) { return true; } // A breadcrumb is broken if it does not contain a URL or text. if ( ! \array_key_exists( 'url', $breadcrumb ) || ! \array_key_exists( 'text', $breadcrumb ) ) { return true; } return false; } /** * Checks whether the breadcrumb is not set to be hidden. * * @param array $breadcrumb The breadcrumb array. * * @return bool If the breadcrumb should not be hidden. */ private function not_hidden( $breadcrumb ) { return empty( $breadcrumb['hide_in_schema'] ); } } schema/article.php 0000644 00000014277 15154134645 0010163 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns schema Article data. */ class Article extends Abstract_Schema_Piece { /** * Determines whether or not a piece should be added to the graph. * * @return bool */ public function is_needed() { if ( $this->context->indexable->object_type !== 'post' ) { return false; } // If we cannot output a publisher, we shouldn't output an Article. if ( $this->context->site_represents === false ) { return false; } // If we cannot output an author, we shouldn't output an Article. if ( ! $this->helpers->schema->article->is_author_supported( $this->context->indexable->object_sub_type ) ) { return false; } if ( $this->context->schema_article_type !== 'None' ) { $this->context->main_schema_id = $this->context->canonical . Schema_IDs::ARTICLE_HASH; return true; } return false; } /** * Returns Article data. * * @return array Article data. */ public function generate() { $data = [ '@type' => $this->context->schema_article_type, '@id' => $this->context->canonical . Schema_IDs::ARTICLE_HASH, 'isPartOf' => [ '@id' => $this->context->canonical . Schema_IDs::WEBPAGE_HASH ], 'author' => [ '@id' => $this->helpers->schema->id->get_user_schema_id( $this->context->post->post_author, $this->context ) ], 'headline' => $this->helpers->schema->html->smart_strip_tags( $this->helpers->post->get_post_title_with_fallback( $this->context->id ) ), 'datePublished' => $this->helpers->date->format( $this->context->post->post_date_gmt ), 'dateModified' => $this->helpers->date->format( $this->context->post->post_modified_gmt ), 'mainEntityOfPage' => [ '@id' => $this->context->canonical . Schema_IDs::WEBPAGE_HASH ], 'wordCount' => $this->word_count( $this->context->post->post_content, $this->context->post->post_title ), ]; if ( $this->context->post->comment_status === 'open' ) { $data['commentCount'] = \intval( $this->context->post->comment_count, 10 ); } if ( $this->context->site_represents_reference ) { $data['publisher'] = $this->context->site_represents_reference; } $data = $this->add_image( $data ); $data = $this->add_keywords( $data ); $data = $this->add_sections( $data ); $data = $this->helpers->schema->language->add_piece_language( $data ); if ( \post_type_supports( $this->context->post->post_type, 'comments' ) && $this->context->post->comment_status === 'open' ) { $data = $this->add_potential_action( $data ); } return $data; } /** * Adds tags as keywords, if tags are assigned. * * @param array $data Article data. * * @return array Article data. */ private function add_keywords( $data ) { /** * Filter: 'wpseo_schema_article_keywords_taxonomy' - Allow changing the taxonomy used to assign keywords to a post type Article data. * * @api string $taxonomy The chosen taxonomy. */ $taxonomy = \apply_filters( 'wpseo_schema_article_keywords_taxonomy', 'post_tag' ); return $this->add_terms( $data, 'keywords', $taxonomy ); } /** * Adds categories as sections, if categories are assigned. * * @param array $data Article data. * * @return array Article data. */ private function add_sections( $data ) { /** * Filter: 'wpseo_schema_article_sections_taxonomy' - Allow changing the taxonomy used to assign keywords to a post type Article data. * * @api string $taxonomy The chosen taxonomy. */ $taxonomy = \apply_filters( 'wpseo_schema_article_sections_taxonomy', 'category' ); return $this->add_terms( $data, 'articleSection', $taxonomy ); } /** * Adds a term or multiple terms, comma separated, to a field. * * @param array $data Article data. * @param string $key The key in data to save the terms in. * @param string $taxonomy The taxonomy to retrieve the terms from. * * @return mixed Article data. */ protected function add_terms( $data, $key, $taxonomy ) { $terms = \get_the_terms( $this->context->id, $taxonomy ); if ( ! \is_array( $terms ) ) { return $data; } $callback = static function( $term ) { // We are using the WordPress internal translation. return $term->name !== \__( 'Uncategorized', 'default' ); }; $terms = \array_filter( $terms, $callback ); if ( empty( $terms ) ) { return $data; } $data[ $key ] = \wp_list_pluck( $terms, 'name' ); return $data; } /** * Adds an image node if the post has a featured image. * * @param array $data The Article data. * * @return array The Article data. */ private function add_image( $data ) { if ( $this->context->main_image_url !== null ) { $data['image'] = [ '@id' => $this->context->canonical . Schema_IDs::PRIMARY_IMAGE_HASH, ]; $data['thumbnailUrl'] = $this->context->main_image_url; } return $data; } /** * Adds the potential action property to the Article Schema piece. * * @param array $data The Article data. * * @return array The Article data with the potential action added. */ private function add_potential_action( $data ) { /** * Filter: 'wpseo_schema_article_potential_action_target' - Allows filtering of the schema Article potentialAction target. * * @api array $targets The URLs for the Article potentialAction target. */ $targets = \apply_filters( 'wpseo_schema_article_potential_action_target', [ $this->context->canonical . '#respond' ] ); $data['potentialAction'][] = [ '@type' => 'CommentAction', 'name' => 'Comment', 'target' => $targets, ]; return $data; } /** * Does a simple word count but tries to be relatively smart about it. * * @param string $post_content The post content. * @param string $post_title The post title. * * @return int The number of words in the content. */ private function word_count( $post_content, $post_title = '' ) { // Add the title to our word count. $post_content = $post_title . ' ' . $post_content; // Strip pre/code blocks and their content. $post_content = \preg_replace( '@<(pre|code)[^>]*?>.*?</\\1>@si', '', $post_content ); // Strips all other tags. $post_content = \wp_strip_all_tags( $post_content ); return \str_word_count( $post_content, 0 ); } } schema/webpage.php 0000644 00000010346 15154134645 0010143 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use WP_Post; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns schema WebPage data. */ class WebPage extends Abstract_Schema_Piece { /** * Determines whether or not a piece should be added to the graph. * * @return bool */ public function is_needed() { return ! ( $this->context->indexable->object_type === 'system-page' && $this->context->indexable->object_sub_type === '404' ); } /** * Returns WebPage schema data. * * @return array WebPage schema data. */ public function generate() { $data = [ '@type' => $this->context->schema_page_type, '@id' => $this->context->canonical . Schema_IDs::WEBPAGE_HASH, 'url' => $this->context->canonical, 'name' => $this->helpers->schema->html->smart_strip_tags( $this->context->title ), 'isPartOf' => [ '@id' => $this->context->site_url . Schema_IDs::WEBSITE_HASH, ], ]; if ( empty( $this->context->canonical ) && \is_search() ) { $data['url'] = $this->build_search_url(); } if ( $this->helpers->current_page->is_front_page() ) { if ( $this->context->site_represents_reference ) { $data['about'] = $this->context->site_represents_reference; } } if ( $this->context->indexable->object_type === 'post' ) { $this->add_image( $data ); $data['datePublished'] = $this->helpers->date->format( $this->context->post->post_date_gmt ); $data['dateModified'] = $this->helpers->date->format( $this->context->post->post_modified_gmt ); if ( $this->context->indexable->object_sub_type === 'post' ) { $data = $this->add_author( $data, $this->context->post ); } } if ( ! empty( $this->context->description ) ) { $data['description'] = $this->helpers->schema->html->smart_strip_tags( $this->context->description ); } if ( $this->add_breadcrumbs() ) { $data['breadcrumb'] = [ '@id' => $this->context->canonical . Schema_IDs::BREADCRUMB_HASH, ]; } if ( ! empty( $this->context->main_entity_of_page ) ) { $data['mainEntity'] = $this->context->main_entity_of_page; } $data = $this->helpers->schema->language->add_piece_language( $data ); $data = $this->add_potential_action( $data ); return $data; } /** * Adds an author property to the $data if the WebPage is not represented. * * @param array $data The WebPage schema. * @param WP_Post $post The post the context is representing. * * @return array The WebPage schema. */ public function add_author( $data, $post ) { if ( $this->context->site_represents === false ) { $data['author'] = [ '@id' => $this->helpers->schema->id->get_user_schema_id( $post->post_author, $this->context ) ]; } return $data; } /** * If we have an image, make it the primary image of the page. * * @param array $data WebPage schema data. */ public function add_image( &$data ) { if ( $this->context->has_image ) { $data['primaryImageOfPage'] = [ '@id' => $this->context->canonical . Schema_IDs::PRIMARY_IMAGE_HASH ]; } } /** * Determine if we should add a breadcrumb attribute. * * @return bool */ private function add_breadcrumbs() { if ( $this->context->indexable->object_type === 'system-page' && $this->context->indexable->object_sub_type === '404' ) { return false; } return true; } /** * Adds the potential action property to the WebPage Schema piece. * * @param array $data The WebPage data. * * @return array The WebPage data with the potential action added. */ private function add_potential_action( $data ) { $url = $this->context->canonical; if ( empty( $url ) && \is_search() ) { $url = $this->build_search_url(); } /** * Filter: 'wpseo_schema_webpage_potential_action_target' - Allows filtering of the schema WebPage potentialAction target. * * @api array $targets The URLs for the WebPage potentialAction target. */ $targets = \apply_filters( 'wpseo_schema_webpage_potential_action_target', [ $url ] ); $data['potentialAction'][] = [ '@type' => 'ReadAction', 'target' => $targets, ]; return $data; } /** * Creates the search URL for use when if there is no canonical. * * @return string Search URL. */ private function build_search_url() { return $this->context->site_url . '?s=' . \get_search_query(); } } schema/howto.php 0000644 00000011354 15154134645 0007671 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; /** * Returns schema HowTo data. */ class HowTo extends Abstract_Schema_Piece { /** * Determines whether or not a piece should be added to the graph. * * @return bool */ public function is_needed() { return ! empty( $this->context->blocks['yoast/how-to-block'] ); } /** * Renders a list of questions, referencing them by ID. * * @return array Our Schema graph. */ public function generate() { $graph = []; foreach ( $this->context->blocks['yoast/how-to-block'] as $index => $block ) { $this->add_how_to( $graph, $block, $index ); } return $graph; } /** * Adds the duration of the task to the Schema. * * @param array $data Our How-To schema data. * @param array $attributes The block data attributes. */ private function add_duration( &$data, $attributes ) { if ( empty( $attributes['hasDuration'] ) ) { return; } $days = empty( $attributes['days'] ) ? 0 : $attributes['days']; $hours = empty( $attributes['hours'] ) ? 0 : $attributes['hours']; $minutes = empty( $attributes['minutes'] ) ? 0 : $attributes['minutes']; if ( ( $days + $hours + $minutes ) > 0 ) { $data['totalTime'] = \esc_attr( 'P' . $days . 'DT' . $hours . 'H' . $minutes . 'M' ); } } /** * Adds the steps to our How-To output. * * @param array $data Our How-To schema data. * @param array $steps Our How-To block's steps. */ private function add_steps( &$data, $steps ) { foreach ( $steps as $step ) { $schema_id = $this->context->canonical . '#' . \esc_attr( $step['id'] ); $schema_step = [ '@type' => 'HowToStep', 'url' => $schema_id, ]; if ( isset( $step['jsonText'] ) ) { $json_text = $this->helpers->schema->html->sanitize( $step['jsonText'] ); } if ( isset( $step['jsonName'] ) ) { $json_name = $this->helpers->schema->html->smart_strip_tags( $step['jsonName'] ); } if ( empty( $json_name ) ) { if ( empty( $step['text'] ) ) { continue; } $schema_step['text'] = ''; $this->add_step_image( $schema_step, $step ); // If there is no text and no image, don't output the step. if ( empty( $json_text ) && empty( $schema_step['image'] ) ) { continue; } if ( ! empty( $json_text ) ) { $schema_step['text'] = $json_text; } } elseif ( empty( $json_text ) ) { $schema_step['text'] = $json_name; } else { $schema_step['name'] = $json_name; $this->add_step_description( $schema_step, $json_text ); $this->add_step_image( $schema_step, $step ); } $data['step'][] = $schema_step; } } /** * Checks if we have a step description, if we do, add it. * * @param array $schema_step Our Schema output for the Step. * @param string $json_text The step text. */ private function add_step_description( &$schema_step, $json_text ) { $schema_step['itemListElement'] = [ [ '@type' => 'HowToDirection', 'text' => $json_text, ], ]; } /** * Checks if we have a step image, if we do, add it. * * @param array $schema_step Our Schema output for the Step. * @param array $step The step block data. */ private function add_step_image( &$schema_step, $step ) { foreach ( $step['text'] as $line ) { if ( \is_array( $line ) && isset( $line['type'] ) && $line['type'] === 'img' ) { $schema_step['image'] = $this->get_image_schema( \esc_url( $line['props']['src'] ) ); } } } /** * Generates the HowTo schema for a block. * * @param array $graph Our Schema data. * @param array $block The How-To block content. * @param int $index The index of the current block. */ protected function add_how_to( &$graph, $block, $index ) { $data = [ '@type' => 'HowTo', '@id' => $this->context->canonical . '#howto-' . ( $index + 1 ), 'name' => $this->helpers->schema->html->smart_strip_tags( $this->helpers->post->get_post_title_with_fallback( $this->context->id ) ), 'mainEntityOfPage' => [ '@id' => $this->context->main_schema_id ], 'description' => '', ]; if ( isset( $block['attrs']['jsonDescription'] ) ) { $data['description'] = $this->helpers->schema->html->sanitize( $block['attrs']['jsonDescription'] ); } $this->add_duration( $data, $block['attrs'] ); $this->add_steps( $data, $block['attrs']['steps'] ); $data = $this->helpers->schema->language->add_piece_language( $data ); $graph[] = $data; } /** * Generates the image schema from the attachment $url. * * @param string $url Attachment url. * * @return array Image schema. */ protected function get_image_schema( $url ) { $schema_id = $this->context->canonical . '#schema-image-' . \md5( $url ); return $this->helpers->schema->image->generate_from_url( $schema_id, $url ); } } schema/organization.php 0000644 00000004451 15154134645 0011235 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns schema Organization data. */ class Organization extends Abstract_Schema_Piece { /** * Determines whether an Organization graph piece should be added. * * @return bool */ public function is_needed() { return $this->context->site_represents === 'company'; } /** * Returns the Organization Schema data. * * @return array The Organization schema. */ public function generate() { $logo_schema_id = $this->context->site_url . Schema_IDs::ORGANIZATION_LOGO_HASH; if ( $this->context->company_logo_meta ) { $logo = $this->helpers->schema->image->generate_from_attachment_meta( $logo_schema_id, $this->context->company_logo_meta, $this->context->company_name ); } else { $logo = $this->helpers->schema->image->generate_from_attachment_id( $logo_schema_id, $this->context->company_logo_id, $this->context->company_name ); } return [ '@type' => 'Organization', '@id' => $this->context->site_url . Schema_IDs::ORGANIZATION_HASH, 'name' => $this->helpers->schema->html->smart_strip_tags( $this->context->company_name ), 'url' => $this->context->site_url, 'sameAs' => $this->fetch_social_profiles(), 'logo' => $logo, 'image' => [ '@id' => $logo_schema_id ], ]; } /** * Retrieve the social profiles to display in the organization schema. * * @return array An array of social profiles. */ private function fetch_social_profiles() { $profiles = []; $social_profiles = [ 'facebook_site', 'instagram_url', 'linkedin_url', 'myspace_url', 'youtube_url', 'pinterest_url', 'wikipedia_url', ]; foreach ( $social_profiles as $profile ) { $social_profile = $this->helpers->options->get( $profile, '' ); if ( $social_profile !== '' ) { $profiles[] = \urldecode( $social_profile ); } } $twitter = $this->helpers->options->get( 'twitter_site', '' ); if ( $twitter !== '' ) { $profiles[] = 'https://twitter.com/' . $twitter; } /** * Filter: 'wpseo_schema_organization_social_profiles' - Allows filtering social profiles for the * represented organization. * * @api string[] $profiles */ $profiles = \apply_filters( 'wpseo_schema_organization_social_profiles', $profiles ); return $profiles; } } schema/website.php 0000644 00000005223 15154134645 0010171 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use Yoast\WP\SEO\Config\Schema_IDs; /** * Returns schema Website data. */ class Website extends Abstract_Schema_Piece { /** * Determines whether or not a piece should be added to the graph. * * @return bool */ public function is_needed() { return true; } /** * Outputs code to allow recognition of the internal search engine. * * @return array Website data blob. */ public function generate() { $data = [ '@type' => 'WebSite', '@id' => $this->context->site_url . Schema_IDs::WEBSITE_HASH, 'url' => $this->context->site_url, 'name' => $this->helpers->schema->html->smart_strip_tags( $this->context->site_name ), 'description' => \get_bloginfo( 'description' ), ]; if ( $this->context->site_represents_reference ) { $data['publisher'] = $this->context->site_represents_reference; } $data = $this->add_alternate_name( $data ); $data = $this->internal_search_section( $data ); $data = $this->helpers->schema->language->add_piece_language( $data ); return $data; } /** * Returns an alternate name if one was specified in the Yoast SEO settings. * * @param array $data The website data array. * * @return array */ private function add_alternate_name( $data ) { $alternate_name = $this->helpers->options->get( 'alternate_website_name', '' ); if ( $alternate_name !== '' ) { $data['alternateName'] = $this->helpers->schema->html->smart_strip_tags( $alternate_name ); } return $data; } /** * Adds the internal search JSON LD code to the homepage if it's not disabled. * * @link https://developers.google.com/search/docs/data-types/sitelinks-searchbox * * @param array $data The website data array. * * @return array */ private function internal_search_section( $data ) { /** * Filter: 'disable_wpseo_json_ld_search' - Allow disabling of the json+ld output. * * @api bool $display_search Whether or not to display json+ld search on the frontend. */ if ( \apply_filters( 'disable_wpseo_json_ld_search', false ) ) { return $data; } /** * Filter: 'wpseo_json_ld_search_url' - Allows filtering of the search URL for Yoast SEO. * * @api string $search_url The search URL for this site with a `{search_term_string}` variable. */ $search_url = \apply_filters( 'wpseo_json_ld_search_url', $this->context->site_url . '?s={search_term_string}' ); $data['potentialAction'][] = [ '@type' => 'SearchAction', 'target' => [ '@type' => 'EntryPoint', 'urlTemplate' => $search_url, ], 'query-input' => 'required name=search_term_string', ]; return $data; } } schema/abstract-schema-piece.php 0000644 00000001145 15154134645 0012652 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators\Schema; use Yoast\WP\SEO\Context\Meta_Tags_Context; use Yoast\WP\SEO\Surfaces\Helpers_Surface; /** * Class Abstract_Schema_Piece. */ abstract class Abstract_Schema_Piece { /** * The meta tags context. * * @var Meta_Tags_Context */ public $context; /** * The helpers surface * * @var Helpers_Surface */ public $helpers; /** * Generates the schema piece. * * @return mixed */ abstract public function generate(); /** * Determines whether the schema piece is needed. * * @return bool */ abstract public function is_needed(); } open-graph-locale-generator.php 0000644 00000012755 15154134645 0012560 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators; use Yoast\WP\SEO\Context\Meta_Tags_Context; /** * Class Open_Graph_Locale_Generator. */ class Open_Graph_Locale_Generator implements Generator_Interface { /** * Generates the OG Locale. * * @param Meta_Tags_Context $context The context. * * @return string The OG locale. */ public function generate( Meta_Tags_Context $context ) { /** * Filter: 'wpseo_locale' - Allow changing the locale output. * * Note that this filter is different from `wpseo_og_locale`, which is run _after_ the OG specific filtering. * * @api string Locale string. */ $locale = \apply_filters( 'wpseo_locale', \get_locale() ); // Catch some weird locales served out by WP that are not easily doubled up. $fix_locales = [ 'ca' => 'ca_ES', 'en' => 'en_US', 'el' => 'el_GR', 'et' => 'et_EE', 'ja' => 'ja_JP', 'sq' => 'sq_AL', 'uk' => 'uk_UA', 'vi' => 'vi_VN', 'zh' => 'zh_CN', ]; if ( isset( $fix_locales[ $locale ] ) ) { return $fix_locales[ $locale ]; } // Convert locales like "es" to "es_ES", in case that works for the given locale (sometimes it does). if ( \strlen( $locale ) === 2 ) { $locale = \strtolower( $locale ) . '_' . \strtoupper( $locale ); } // These are the locales FB supports. $fb_valid_fb_locales = [ 'af_ZA', // Afrikaans. 'ak_GH', // Akan. 'am_ET', // Amharic. 'ar_AR', // Arabic. 'as_IN', // Assamese. 'ay_BO', // Aymara. 'az_AZ', // Azerbaijani. 'be_BY', // Belarusian. 'bg_BG', // Bulgarian. 'bp_IN', // Bhojpuri. 'bn_IN', // Bengali. 'br_FR', // Breton. 'bs_BA', // Bosnian. 'ca_ES', // Catalan. 'cb_IQ', // Sorani Kurdish. 'ck_US', // Cherokee. 'co_FR', // Corsican. 'cs_CZ', // Czech. 'cx_PH', // Cebuano. 'cy_GB', // Welsh. 'da_DK', // Danish. 'de_DE', // German. 'el_GR', // Greek. 'en_GB', // English (UK). 'en_PI', // English (Pirate). 'en_UD', // English (Upside Down). 'en_US', // English (US). 'em_ZM', 'eo_EO', // Esperanto. 'es_ES', // Spanish (Spain). 'es_LA', // Spanish. 'es_MX', // Spanish (Mexico). 'et_EE', // Estonian. 'eu_ES', // Basque. 'fa_IR', // Persian. 'fb_LT', // Leet Speak. 'ff_NG', // Fulah. 'fi_FI', // Finnish. 'fo_FO', // Faroese. 'fr_CA', // French (Canada). 'fr_FR', // French (France). 'fy_NL', // Frisian. 'ga_IE', // Irish. 'gl_ES', // Galician. 'gn_PY', // Guarani. 'gu_IN', // Gujarati. 'gx_GR', // Classical Greek. 'ha_NG', // Hausa. 'he_IL', // Hebrew. 'hi_IN', // Hindi. 'hr_HR', // Croatian. 'hu_HU', // Hungarian. 'ht_HT', // Haitian Creole. 'hy_AM', // Armenian. 'id_ID', // Indonesian. 'ig_NG', // Igbo. 'is_IS', // Icelandic. 'it_IT', // Italian. 'ik_US', 'iu_CA', 'ja_JP', // Japanese. 'ja_KS', // Japanese (Kansai). 'jv_ID', // Javanese. 'ka_GE', // Georgian. 'kk_KZ', // Kazakh. 'km_KH', // Khmer. 'kn_IN', // Kannada. 'ko_KR', // Korean. 'ks_IN', // Kashmiri. 'ku_TR', // Kurdish (Kurmanji). 'ky_KG', // Kyrgyz. 'la_VA', // Latin. 'lg_UG', // Ganda. 'li_NL', // Limburgish. 'ln_CD', // Lingala. 'lo_LA', // Lao. 'lt_LT', // Lithuanian. 'lv_LV', // Latvian. 'mg_MG', // Malagasy. 'mi_NZ', // Maori. 'mk_MK', // Macedonian. 'ml_IN', // Malayalam. 'mn_MN', // Mongolian. 'mr_IN', // Marathi. 'ms_MY', // Malay. 'mt_MT', // Maltese. 'my_MM', // Burmese. 'nb_NO', // Norwegian (bokmal). 'nd_ZW', // Ndebele. 'ne_NP', // Nepali. 'nl_BE', // Dutch (Belgie). 'nl_NL', // Dutch. 'nn_NO', // Norwegian (nynorsk). 'nr_ZA', // Southern Ndebele. 'ns_ZA', // Northern Sotho. 'ny_MW', // Chewa. 'om_ET', // Oromo. 'or_IN', // Oriya. 'pa_IN', // Punjabi. 'pl_PL', // Polish. 'ps_AF', // Pashto. 'pt_BR', // Portuguese (Brazil). 'pt_PT', // Portuguese (Portugal). 'qc_GT', // Quiché. 'qu_PE', // Quechua. 'qr_GR', 'qz_MM', // Burmese (Zawgyi). 'rm_CH', // Romansh. 'ro_RO', // Romanian. 'ru_RU', // Russian. 'rw_RW', // Kinyarwanda. 'sa_IN', // Sanskrit. 'sc_IT', // Sardinian. 'se_NO', // Northern Sami. 'si_LK', // Sinhala. 'su_ID', // Sundanese. 'sk_SK', // Slovak. 'sl_SI', // Slovenian. 'sn_ZW', // Shona. 'so_SO', // Somali. 'sq_AL', // Albanian. 'sr_RS', // Serbian. 'ss_SZ', // Swazi. 'st_ZA', // Southern Sotho. 'sv_SE', // Swedish. 'sw_KE', // Swahili. 'sy_SY', // Syriac. 'sz_PL', // Silesian. 'ta_IN', // Tamil. 'te_IN', // Telugu. 'tg_TJ', // Tajik. 'th_TH', // Thai. 'tk_TM', // Turkmen. 'tl_PH', // Filipino. 'tl_ST', // Klingon. 'tn_BW', // Tswana. 'tr_TR', // Turkish. 'ts_ZA', // Tsonga. 'tt_RU', // Tatar. 'tz_MA', // Tamazight. 'uk_UA', // Ukrainian. 'ur_PK', // Urdu. 'uz_UZ', // Uzbek. 've_ZA', // Venda. 'vi_VN', // Vietnamese. 'wo_SN', // Wolof. 'xh_ZA', // Xhosa. 'yi_DE', // Yiddish. 'yo_NG', // Yoruba. 'zh_CN', // Simplified Chinese (China). 'zh_HK', // Traditional Chinese (Hong Kong). 'zh_TW', // Traditional Chinese (Taiwan). 'zu_ZA', // Zulu. 'zz_TR', // Zazaki. ]; // Check to see if the locale is a valid FB one, if not, use en_US as a fallback. if ( \in_array( $locale, $fb_valid_fb_locales, true ) ) { return $locale; } $locale = \strtolower( \substr( $locale, 0, 2 ) ) . '_' . \strtoupper( \substr( $locale, 0, 2 ) ); if ( ! \in_array( $locale, $fb_valid_fb_locales, true ) ) { return 'en_US'; } return $locale; } } breadcrumbs-generator.php 0000644 00000026270 15154134645 0011551 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators; use Yoast\WP\SEO\Context\Meta_Tags_Context; use Yoast\WP\SEO\Helpers\Current_Page_Helper; use Yoast\WP\SEO\Helpers\Options_Helper; use Yoast\WP\SEO\Helpers\Pagination_Helper; use Yoast\WP\SEO\Helpers\Post_Type_Helper; use Yoast\WP\SEO\Helpers\Url_Helper; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Repositories\Indexable_Repository; /** * Represents the generator class for the breadcrumbs. */ class Breadcrumbs_Generator implements Generator_Interface { /** * The indexable repository. * * @var Indexable_Repository */ private $repository; /** * The options helper. * * @var Options_Helper */ private $options; /** * The current page helper. * * @var Current_Page_Helper */ private $current_page_helper; /** * The post type helper. * * @var Post_Type_Helper */ private $post_type_helper; /** * The URL helper. * * @var Url_Helper */ private $url_helper; /** * The pagination helper. * * @var Pagination_Helper */ private $pagination_helper; /** * Breadcrumbs_Generator constructor. * * @param Indexable_Repository $repository The repository. * @param Options_Helper $options The options helper. * @param Current_Page_Helper $current_page_helper The current page helper. * @param Post_Type_Helper $post_type_helper The post type helper. * @param Url_Helper $url_helper The URL helper. * @param Pagination_Helper $pagination_helper The pagination helper. */ public function __construct( Indexable_Repository $repository, Options_Helper $options, Current_Page_Helper $current_page_helper, Post_Type_Helper $post_type_helper, Url_Helper $url_helper, Pagination_Helper $pagination_helper ) { $this->repository = $repository; $this->options = $options; $this->current_page_helper = $current_page_helper; $this->post_type_helper = $post_type_helper; $this->url_helper = $url_helper; $this->pagination_helper = $pagination_helper; } /** * Generates the breadcrumbs. * * @param Meta_Tags_Context $context The meta tags context. * * @return array An array of associative arrays that each have a 'text' and a 'url'. */ public function generate( Meta_Tags_Context $context ) { $static_ancestors = []; $breadcrumbs_home = $this->options->get( 'breadcrumbs-home' ); if ( $breadcrumbs_home !== '' && ! \in_array( $this->current_page_helper->get_page_type(), [ 'Home_Page', 'Static_Home_Page' ], true ) ) { $front_page_id = $this->current_page_helper->get_front_page_id(); if ( $front_page_id === 0 ) { $static_ancestors[] = $this->repository->find_for_home_page(); } else { $static_ancestor = $this->repository->find_by_id_and_type( $front_page_id, 'post' ); if ( $static_ancestor->post_status !== 'unindexed' ) { $static_ancestors[] = $static_ancestor; } } } $page_for_posts = \get_option( 'page_for_posts' ); if ( $this->should_have_blog_crumb( $page_for_posts, $context ) ) { $static_ancestor = $this->repository->find_by_id_and_type( $page_for_posts, 'post' ); if ( $static_ancestor->post_status !== 'unindexed' ) { $static_ancestors[] = $static_ancestor; } } if ( $context->indexable->object_type === 'post' && $context->indexable->object_sub_type !== 'post' && $context->indexable->object_sub_type !== 'page' && $this->post_type_helper->has_archive( $context->indexable->object_sub_type ) ) { $static_ancestors[] = $this->repository->find_for_post_type_archive( $context->indexable->object_sub_type ); } if ( $context->indexable->object_type === 'term' ) { $parent = $this->get_taxonomy_post_type_parent( $context->indexable->object_sub_type ); if ( $parent && $parent !== 'post' && $this->post_type_helper->has_archive( $parent ) ) { $static_ancestors[] = $this->repository->find_for_post_type_archive( $parent ); } } // Get all ancestors of the indexable and append itself to get all indexables in the full crumb. $indexables = $this->repository->get_ancestors( $context->indexable ); $indexables[] = $context->indexable; if ( ! empty( $static_ancestors ) ) { \array_unshift( $indexables, ...$static_ancestors ); } $indexables = \apply_filters( 'wpseo_breadcrumb_indexables', $indexables, $context ); $callback = function ( Indexable $ancestor ) { $crumb = [ 'url' => $ancestor->permalink, 'text' => $ancestor->breadcrumb_title, ]; switch ( $ancestor->object_type ) { case 'post': $crumb = $this->get_post_crumb( $crumb, $ancestor ); break; case 'post-type-archive': $crumb = $this->get_post_type_archive_crumb( $crumb, $ancestor ); break; case 'term': $crumb = $this->get_term_crumb( $crumb, $ancestor ); break; case 'system-page': $crumb = $this->get_system_page_crumb( $crumb, $ancestor ); break; case 'user': $crumb = $this->get_user_crumb( $crumb, $ancestor ); break; case 'date-archive': $crumb = $this->get_date_archive_crumb( $crumb ); break; } return $crumb; }; $crumbs = \array_map( $callback, $indexables ); if ( $breadcrumbs_home !== '' ) { $crumbs[0]['text'] = $breadcrumbs_home; } $crumbs = $this->add_paged_crumb( $crumbs, $context->indexable ); /** * Filter: 'wpseo_breadcrumb_links' - Allow the developer to filter the Yoast SEO breadcrumb links, add to them, change order, etc. * * @api array $crumbs The crumbs array. */ $crumbs = \apply_filters( 'wpseo_breadcrumb_links', $crumbs ); $filter_callback = static function( $link_info, $index ) use ( $crumbs ) { /** * Filter: 'wpseo_breadcrumb_single_link_info' - Allow developers to filter the Yoast SEO Breadcrumb link information. * * @api array $link_info The breadcrumb link information. * * @param int $index The index of the breadcrumb in the list. * @param array $crumbs The complete list of breadcrumbs. */ return \apply_filters( 'wpseo_breadcrumb_single_link_info', $link_info, $index, $crumbs ); }; return \array_map( $filter_callback, $crumbs, \array_keys( $crumbs ) ); } /** * Returns the modified post crumb. * * @param array $crumb The crumb. * @param Indexable $ancestor The indexable. * * @return array The crumb. */ private function get_post_crumb( $crumb, $ancestor ) { $crumb['id'] = $ancestor->object_id; return $crumb; } /** * Returns the modified post type crumb. * * @param array $crumb The crumb. * @param Indexable $ancestor The indexable. * * @return array The crumb. */ private function get_post_type_archive_crumb( $crumb, $ancestor ) { $crumb['ptarchive'] = $ancestor->object_sub_type; return $crumb; } /** * Returns the modified term crumb. * * @param array $crumb The crumb. * @param Indexable $ancestor The indexable. * * @return array The crumb. */ private function get_term_crumb( $crumb, $ancestor ) { $crumb['term_id'] = $ancestor->object_id; return $crumb; } /** * Returns the modified system page crumb. * * @param array $crumb The crumb. * @param Indexable $ancestor The indexable. * * @return array The crumb. */ private function get_system_page_crumb( $crumb, $ancestor ) { if ( $ancestor->object_sub_type === 'search-result' ) { $crumb['text'] = $this->options->get( 'breadcrumbs-searchprefix' ) . ' ' . \esc_html( \get_search_query() ); $crumb['url'] = \get_search_link(); } elseif ( $ancestor->object_sub_type === '404' ) { $crumb['text'] = $this->options->get( 'breadcrumbs-404crumb' ); } return $crumb; } /** * Returns the modified user crumb. * * @param array $crumb The crumb. * @param Indexable $ancestor The indexable. * * @return array The crumb. */ private function get_user_crumb( $crumb, $ancestor ) { $display_name = \get_the_author_meta( 'display_name', $ancestor->object_id ); $crumb['text'] = $this->options->get( 'breadcrumbs-archiveprefix' ) . ' ' . $display_name; return $crumb; } /** * Returns the modified date archive crumb. * * @param array $crumb The crumb. * * @return array The crumb. */ protected function get_date_archive_crumb( $crumb ) { $home_url = $this->url_helper->home(); $prefix = $this->options->get( 'breadcrumbs-archiveprefix' ); if ( \is_day() ) { $day = \esc_html( \get_the_date() ); $crumb['url'] = $home_url . \get_the_date( 'Y/m/d' ) . '/'; $crumb['text'] = $prefix . ' ' . $day; } elseif ( \is_month() ) { $month = \esc_html( \trim( \single_month_title( ' ', false ) ) ); $crumb['url'] = $home_url . \get_the_date( 'Y/m' ) . '/'; $crumb['text'] = $prefix . ' ' . $month; } elseif ( \is_year() ) { $year = \get_the_date( 'Y' ); $crumb['url'] = $home_url . $year . '/'; $crumb['text'] = $prefix . ' ' . $year; } return $crumb; } /** * Returns whether or not a blog crumb should be added. * * @param int $page_for_posts The page for posts ID. * @param Meta_Tags_Context $context The meta tags context. * * @return bool Whether or not a blog crumb should be added. */ protected function should_have_blog_crumb( $page_for_posts, $context ) { // When there is no page configured as blog page. if ( \get_option( 'show_on_front' ) !== 'page' || ! $page_for_posts ) { return false; } if ( $context->indexable->object_type === 'term' ) { $parent = $this->get_taxonomy_post_type_parent( $context->indexable->object_sub_type ); return $parent === 'post'; } if ( $this->options->get( 'breadcrumbs-display-blog-page' ) !== true ) { return false; } // When the current page is the home page, searchpage or isn't a singular post. if ( \is_home() || \is_search() || ! \is_singular( 'post' ) ) { return false; } return true; } /** * Returns the post type parent of a given taxonomy. * * @param string $taxonomy The taxonomy. * * @return string|false The parent if it exists, false otherwise. */ protected function get_taxonomy_post_type_parent( $taxonomy ) { $parent = $this->options->get( 'taxonomy-' . $taxonomy . '-ptparent' ); if ( empty( $parent ) || (string) $parent === '0' ) { return false; } return $parent; } /** * Adds a crumb for the current page, if we're on an archive page or paginated post. * * @param array $crumbs The array of breadcrumbs. * @param Indexable $current_indexable The current indexable. * * @return array The breadcrumbs. */ protected function add_paged_crumb( array $crumbs, $current_indexable ) { $is_simple_page = $this->current_page_helper->is_simple_page(); // If we're not on a paged page do nothing. if ( ! $is_simple_page && ! $this->current_page_helper->is_paged() ) { return $crumbs; } // If we're not on a paginated post do nothing. if ( $is_simple_page && $current_indexable->number_of_pages === null ) { return $crumbs; } $current_page_number = $this->pagination_helper->get_current_page_number(); if ( $current_page_number <= 1 ) { return $crumbs; } $crumbs[] = [ 'text' => \sprintf( /* translators: %s expands to the current page number */ \__( 'Page %s', 'wordpress-seo' ), $current_page_number ), ]; return $crumbs; } } open-graph-image-generator.php 0000644 00000013747 15154134645 0012405 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators; use Error; use Yoast\WP\SEO\Context\Meta_Tags_Context; use Yoast\WP\SEO\Helpers\Image_Helper; use Yoast\WP\SEO\Helpers\Open_Graph\Image_Helper as Open_Graph_Image_Helper; use Yoast\WP\SEO\Helpers\Options_Helper; use Yoast\WP\SEO\Helpers\Url_Helper; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Values\Open_Graph\Images; /** * Represents the generator class for the Open Graph images. */ class Open_Graph_Image_Generator implements Generator_Interface { /** * The Open Graph image helper. * * @var Open_Graph_Image_Helper */ protected $open_graph_image; /** * The image helper. * * @var Image_Helper */ protected $image; /** * The URL helper. * * @var Url_Helper */ protected $url; /** * The options helper. * * @var Options_Helper */ private $options; /** * Images constructor. * * @codeCoverageIgnore * * @param Open_Graph_Image_Helper $open_graph_image Image helper for Open Graph. * @param Image_Helper $image The image helper. * @param Options_Helper $options The options helper. * @param Url_Helper $url The url helper. */ public function __construct( Open_Graph_Image_Helper $open_graph_image, Image_Helper $image, Options_Helper $options, Url_Helper $url ) { $this->open_graph_image = $open_graph_image; $this->image = $image; $this->options = $options; $this->url = $url; } /** * Retrieves the images for an indexable. * * For legacy reasons some plugins might expect we filter a WPSEO_Opengraph_Image object. That might cause * type errors. This is why we try/catch our filters. * * @param Meta_Tags_Context $context The context. * * @return array The images. */ public function generate( Meta_Tags_Context $context ) { $image_container = $this->get_image_container(); $backup_image_container = $this->get_image_container(); try { /** * Filter: wpseo_add_opengraph_images - Allow developers to add images to the Open Graph tags. * * @api Yoast\WP\SEO\Values\Open_Graph\Images The current object. */ \apply_filters( 'wpseo_add_opengraph_images', $image_container ); } catch ( Error $error ) { $image_container = $backup_image_container; } $this->add_from_indexable( $context->indexable, $image_container ); $backup_image_container = $image_container; try { /** * Filter: wpseo_add_opengraph_additional_images - Allows to add additional images to the Open Graph tags. * * @api Yoast\WP\SEO\Values\Open_Graph\Images The current object. */ \apply_filters( 'wpseo_add_opengraph_additional_images', $image_container ); } catch ( Error $error ) { $image_container = $backup_image_container; } $this->add_from_templates( $context, $image_container ); $this->add_from_default( $image_container ); return $image_container->get_images(); } /** * Retrieves the images for an author archive indexable. * * This is a custom method to address the case of Author Archives, since they always have an Open Graph image * set in the indexable (even if it is an empty default Gravatar). * * @param Meta_Tags_Context $context The context. * * @return array The images. */ public function generate_for_author_archive( Meta_Tags_Context $context ) { $image_container = $this->get_image_container(); $this->add_from_templates( $context, $image_container ); if ( $image_container->has_images() ) { return $image_container->get_images(); } return $this->generate( $context ); } /** * Adds an image based on the given indexable. * * @param Indexable $indexable The indexable. * @param Images $image_container The image container. */ protected function add_from_indexable( Indexable $indexable, Images $image_container ) { if ( $indexable->open_graph_image_meta ) { $image_container->add_image_by_meta( $indexable->open_graph_image_meta ); return; } if ( $indexable->open_graph_image_id ) { $image_container->add_image_by_id( $indexable->open_graph_image_id ); return; } if ( $indexable->open_graph_image ) { $meta_data = []; if ( $indexable->open_graph_image_meta && \is_string( $indexable->open_graph_image_meta ) ) { $meta_data = \json_decode( $indexable->open_graph_image_meta, true ); } $image_container->add_image( \array_merge( (array) $meta_data, [ 'url' => $indexable->open_graph_image, ] ) ); } } /** * Retrieves the default Open Graph image. * * @param Images $image_container The image container. */ protected function add_from_default( Images $image_container ) { if ( $image_container->has_images() ) { return; } $default_image_id = $this->options->get( 'og_default_image_id', '' ); if ( $default_image_id ) { $image_container->add_image_by_id( $default_image_id ); return; } $default_image_url = $this->options->get( 'og_default_image', '' ); if ( $default_image_url ) { $image_container->add_image_by_url( $default_image_url ); } } /** * Retrieves the default Open Graph image. * * @param Meta_Tags_Context $context The context. * @param Images $image_container The image container. */ protected function add_from_templates( Meta_Tags_Context $context, Images $image_container ) { if ( $image_container->has_images() ) { return; } if ( $context->presentation->open_graph_image_id ) { $image_container->add_image_by_id( $context->presentation->open_graph_image_id ); return; } if ( $context->presentation->open_graph_image ) { $image_container->add_image_by_url( $context->presentation->open_graph_image ); } } /** * Retrieves an instance of the image container. * * @codeCoverageIgnore * * @return Images The image container. */ protected function get_image_container() { $image_container = new Images( $this->image, $this->url ); $image_container->set_helpers( $this->open_graph_image ); return $image_container; } } schema-generator.php 0000644 00000026145 15154134645 0010521 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators; use WP_Block_Parser_Block; use Yoast\WP\SEO\Context\Meta_Tags_Context; use Yoast\WP\SEO\Generators\Schema\Abstract_Schema_Piece; use Yoast\WP\SEO\Helpers\Schema\Replace_Vars_Helper; use Yoast\WP\SEO\Surfaces\Helpers_Surface; /** * Class Schema_Generator. */ class Schema_Generator implements Generator_Interface { /** * The helpers surface. * * @var Helpers_Surface */ protected $helpers; /** * The Schema replace vars helper. * * @var Replace_Vars_Helper */ protected $schema_replace_vars_helper; /** * Generator constructor. * * @param Helpers_Surface $helpers The helpers surface. * @param Replace_Vars_Helper $schema_replace_vars_helper The replace vars helper. */ public function __construct( Helpers_Surface $helpers, Replace_Vars_Helper $schema_replace_vars_helper ) { $this->helpers = $helpers; $this->schema_replace_vars_helper = $schema_replace_vars_helper; } /** * Returns a Schema graph array. * * @param Meta_Tags_Context $context The meta tags context. * * @return array The graph. */ public function generate( Meta_Tags_Context $context ) { $pieces = $this->get_graph_pieces( $context ); $this->schema_replace_vars_helper->register_replace_vars( $context ); foreach ( \array_keys( $context->blocks ) as $block_type ) { /** * Filter: 'wpseo_pre_schema_block_type_<block-type>' - Allows hooking things to change graph output based on the blocks on the page. * * @param string $block_type The block type. * @param WP_Block_Parser_Block[] $blocks All the blocks of this block type. * @param Meta_Tags_Context $context A value object with context variables. */ \do_action( 'wpseo_pre_schema_block_type_' . $block_type, $context->blocks[ $block_type ], $context ); } // Do a loop before everything else to inject the context and helpers. foreach ( $pieces as $piece ) { if ( \is_a( $piece, Abstract_Schema_Piece::class ) ) { $piece->context = $context; $piece->helpers = $this->helpers; } } $pieces_to_generate = $this->filter_graph_pieces_to_generate( $pieces ); $graph = $this->generate_graph( $pieces_to_generate, $context ); $graph = $this->add_schema_blocks_graph_pieces( $graph, $context ); return [ '@context' => 'https://schema.org', '@graph' => $graph, ]; } /** * Filters out any graph pieces that should not be generated. * (Using the `wpseo_schema_needs_<graph_piece_identifier>` series of filters). * * @param array $graph_pieces The current list of graph pieces that we want to generate. * * @return array The graph pieces to generate. */ protected function filter_graph_pieces_to_generate( $graph_pieces ) { $pieces_to_generate = []; foreach ( $graph_pieces as $piece ) { $identifier = \strtolower( \str_replace( 'Yoast\WP\SEO\Generators\Schema\\', '', \get_class( $piece ) ) ); if ( \property_exists( $piece, 'identifier' ) ) { $identifier = $piece->identifier; } /** * Filter: 'wpseo_schema_needs_<identifier>' - Allows changing which graph pieces we output. * * @api bool $is_needed Whether or not to show a graph piece. */ $is_needed = \apply_filters( 'wpseo_schema_needs_' . $identifier, $piece->is_needed() ); if ( ! $is_needed ) { continue; } $pieces_to_generate[ $identifier ] = $piece; } return $pieces_to_generate; } /** * Generates the schema graph. * * @param array $graph_piece_generators The schema graph pieces to generate. * @param Meta_Tags_Context $context The meta tags context to use. * * @return array The generated schema graph. */ protected function generate_graph( $graph_piece_generators, $context ) { $graph = []; foreach ( $graph_piece_generators as $identifier => $graph_piece_generator ) { $graph_pieces = $graph_piece_generator->generate(); // If only a single graph piece was returned. if ( $graph_pieces !== false && \array_key_exists( '@type', $graph_pieces ) ) { $graph_pieces = [ $graph_pieces ]; } if ( ! \is_array( $graph_pieces ) ) { continue; } foreach ( $graph_pieces as $graph_piece ) { /** * Filter: 'wpseo_schema_<identifier>' - Allows changing graph piece output. * This filter can be called with either an identifier or a block type (see `add_schema_blocks_graph_pieces()`). * * @api array $graph_piece The graph piece to filter. * * @param Meta_Tags_Context $context A value object with context variables. * @param Abstract_Schema_Piece $graph_piece_generator A value object with context variables. * @param Abstract_Schema_Piece[] $graph_piece_generators A value object with context variables. */ $graph_piece = \apply_filters( 'wpseo_schema_' . $identifier, $graph_piece, $context, $graph_piece_generator, $graph_piece_generators ); $graph_piece = $this->type_filter( $graph_piece, $identifier, $context, $graph_piece_generator, $graph_piece_generators ); $graph_piece = $this->validate_type( $graph_piece ); if ( \is_array( $graph_piece ) ) { $graph[] = $graph_piece; } } } return $graph; } /** * Adds schema graph pieces from Gutenberg blocks on the current page to * the given schema graph. * * Think of blocks like the Yoast FAQ block or the How To block. * * @param array $graph The current schema graph. * @param Meta_Tags_Context $context The meta tags context. * * @return array The graph with the schema blocks graph pieces added. */ protected function add_schema_blocks_graph_pieces( $graph, $context ) { foreach ( $context->blocks as $block_type => $blocks ) { foreach ( $blocks as $block ) { $block_type = \strtolower( $block['blockName'] ); /** * Filter: 'wpseo_schema_block_<block-type>'. * This filter is documented in the `generate_graph()` function in this class. */ $graph = \apply_filters( 'wpseo_schema_block_' . $block_type, $graph, $block, $context ); if ( isset( $block['attrs']['yoast-schema'] ) ) { $graph[] = $this->schema_replace_vars_helper->replace( $block['attrs']['yoast-schema'], $context->presentation ); } } } return $graph; } /** * Adapts the WebPage graph piece for password-protected posts. * * It should only have certain whitelisted properties. * The type should always be WebPage. * * @param array $graph_piece The WebPage graph piece that should be adapted for password-protected posts. * * @return array The WebPage graph piece that has been adapted for password-protected posts. */ public function protected_webpage_schema( $graph_piece ) { $properties_to_show = \array_flip( [ '@type', '@id', 'url', 'name', 'isPartOf', 'inLanguage', 'datePublished', 'dateModified', 'breadcrumb', ] ); $graph_piece = \array_intersect_key( $graph_piece, $properties_to_show ); $graph_piece['@type'] = 'WebPage'; return $graph_piece; } /** * Gets all the graph pieces we need. * * @param Meta_Tags_Context $context The meta tags context. * * @return Abstract_Schema_Piece[] A filtered array of graph pieces. */ protected function get_graph_pieces( $context ) { if ( $context->indexable->object_type === 'post' && \post_password_required( $context->post ) ) { $schema_pieces = [ new Schema\Organization(), new Schema\Website(), new Schema\WebPage(), ]; \add_filter( 'wpseo_schema_webpage', [ $this, 'protected_webpage_schema' ], 1 ); } else { $schema_pieces = [ new Schema\Organization(), new Schema\Person(), new Schema\Website(), new Schema\Main_Image(), new Schema\WebPage(), new Schema\Breadcrumb(), new Schema\Article(), new Schema\Author(), new Schema\FAQ(), new Schema\HowTo(), ]; } /** * Filter: 'wpseo_schema_graph_pieces' - Allows adding pieces to the graph. * * @param Meta_Tags_Context $context An object with context variables. * * @api array $pieces The schema pieces. */ return \apply_filters( 'wpseo_schema_graph_pieces', $schema_pieces, $context ); } /** * Allows filtering the graph piece by its schema type. * * Note: We removed the Abstract_Schema_Piece type-hint from the $graph_piece_generator argument, because * it caused conflicts with old code, Yoast SEO Video specifically. * * @param array $graph_piece The graph piece we're filtering. * @param string $identifier The identifier of the graph piece that is being filtered. * @param Meta_Tags_Context $context The meta tags context. * @param Abstract_Schema_Piece $graph_piece_generator A value object with context variables. * @param Abstract_Schema_Piece[] $graph_piece_generators A value object with context variables. * * @return array The filtered graph piece. */ private function type_filter( $graph_piece, $identifier, Meta_Tags_Context $context, $graph_piece_generator, array $graph_piece_generators ) { $types = $this->get_type_from_piece( $graph_piece ); foreach ( $types as $type ) { $type = \strtolower( $type ); // Prevent running the same filter twice. This makes sure we run f/i. for 'author' and for 'person'. if ( $type && $type !== $identifier ) { /** * Filter: 'wpseo_schema_<type>' - Allows changing graph piece output by @type. * * @api array $graph_piece The graph piece to filter. * * @param Meta_Tags_Context $context A value object with context variables. * @param Abstract_Schema_Piece $graph_piece_generator A value object with context variables. * @param Abstract_Schema_Piece[] $graph_piece_generators A value object with context variables. */ $graph_piece = \apply_filters( 'wpseo_schema_' . $type, $graph_piece, $context, $graph_piece_generator, $graph_piece_generators ); } } return $graph_piece; } /** * Retrieves the type from a graph piece. * * @param array $piece The graph piece. * * @return array An array of the piece's types. */ private function get_type_from_piece( $piece ) { if ( isset( $piece['@type'] ) ) { if ( \is_array( $piece['@type'] ) ) { return $piece['@type']; } return [ $piece['@type'] ]; } return []; } /** * Validates a graph piece's type. * * When the type is an array: * - Ensure the values are unique. * - Only 1 value? Use that value without the array wrapping. * * @param array $piece The graph piece. * * @return array The graph piece. */ private function validate_type( $piece ) { if ( ! isset( $piece['@type'] ) ) { // No type to validate. return $piece; } // If it is not an array, we can return immediately. if ( ! \is_array( $piece['@type'] ) ) { return $piece; } /* * Ensure the types are unique. * Use array_values to reset the indices (e.g. no 0, 2 because 1 was a duplicate). */ $piece['@type'] = \array_values( \array_unique( $piece['@type'] ) ); // Use the first value if there is only 1 type. if ( \count( $piece['@type'] ) === 1 ) { $piece['@type'] = \reset( $piece['@type'] ); } return $piece; } } twitter-image-generator.php 0000644 00000004553 15154134645 0012042 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators; use Yoast\WP\SEO\Context\Meta_Tags_Context; use Yoast\WP\SEO\Helpers\Image_Helper; use Yoast\WP\SEO\Helpers\Twitter\Image_Helper as Twitter_Image_Helper; use Yoast\WP\SEO\Helpers\Url_Helper; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Values\Images; /** * Represents the generator class for the Twitter images. */ class Twitter_Image_Generator implements Generator_Interface { /** * The image helper. * * @var Image_Helper */ protected $image; /** * The URL helper. * * @var Url_Helper */ protected $url; /** * The Twitter image helper. * * @var Twitter_Image_Helper */ protected $twitter_image; /** * Twitter_Image_Generator constructor. * * @codeCoverageIgnore * * @param Image_Helper $image The image helper. * @param Url_Helper $url The url helper. * @param Twitter_Image_Helper $twitter_image The Twitter image helper. */ public function __construct( Image_Helper $image, Url_Helper $url, Twitter_Image_Helper $twitter_image ) { $this->image = $image; $this->url = $url; $this->twitter_image = $twitter_image; } /** * Retrieves the images for an indexable. * * @param Meta_Tags_Context $context The context. * * @return array The images. */ public function generate( Meta_Tags_Context $context ) { $image_container = $this->get_image_container(); $this->add_from_indexable( $context->indexable, $image_container ); return $image_container->get_images(); } /** * Adds an image based on the given indexable. * * @param Indexable $indexable The indexable. * @param Images $image_container The image container. */ protected function add_from_indexable( Indexable $indexable, Images $image_container ) { if ( $indexable->twitter_image_id ) { $image_container->add_image_by_id( $indexable->twitter_image_id ); return; } if ( $indexable->twitter_image ) { $image_container->add_image_by_url( $indexable->twitter_image ); } } /** * Retrieves an instance of the image container. * * @codeCoverageIgnore * * @return Images The image container. */ protected function get_image_container() { $image_container = new Images( $this->image, $this->url ); $image_container->image_size = $this->twitter_image->get_image_size(); return $image_container; } } generator-interface.php 0000644 00000000544 15154134645 0011214 0 ustar 00 <?php namespace Yoast\WP\SEO\Generators; use Yoast\WP\SEO\Context\Meta_Tags_Context; interface Generator_Interface { /** * Returns a string, or other Thing that the associated presenter can handle. * * @param Meta_Tags_Context $context The meta tags context. * * @return mixed */ public function generate( Meta_Tags_Context $context ); }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка