meta_resolver = $meta_resolver; $this->post_meta = $post_meta; } public function get_id(): string { return 'ForcedType'; } /** * The stored value the graph should honor for a post — sanitized again on * read (defense in depth: a row written before the whitelist existed, or * by other tooling, must not inject an arbitrary @type). */ public static function override_for( PostMetaRepositoryInterface $post_meta, \WP_Post $post ): string { return MetaFields::sanitize_schema_type_override( (string) $post_meta->get( $post->ID, MetaFields::SCHEMA_TYPE_OVERRIDE ) ); } public function applies_to( \WP_Post $post ): bool { $override = self::override_for( $this->post_meta, $post ); return '' !== $override && 'none' !== $override; } /** * @return array */ public function build( \WP_Post $post ): array { $override = self::override_for( $this->post_meta, $post ); $title = $this->meta_resolver->title( $post ); $title = '' !== $title ? $title : get_the_title( $post ); if ( in_array( $override, self::ARTICLE_LIKE, true ) ) { // Mirror of ArticleSchemaType::build() with the @type swapped — // same @id fragment, so publisher referencing stays intact. $data = array( '@type' => $override, '@id' => SchemaIds::article( $post ), 'headline' => wp_strip_all_tags( $title ), 'description' => $this->meta_resolver->description( $post ), 'mainEntityOfPage' => array( '@id' => (string) get_permalink( $post ) ), 'datePublished' => get_the_date( DATE_W3C, $post ), 'dateModified' => get_the_modified_date( DATE_W3C, $post ), 'publisher' => array( '@id' => SchemaIds::organization() ), ); $author = $this->author_property( $post ); if ( array() !== $author ) { $data['author'] = $author; } $image = $this->meta_resolver->og_image_url( $post ); if ( '' !== $image ) { $data['image'] = $image; } } else { // Page-like shape (WebPage and friends, plus Blog/CollectionPage — // all the properties used here are valid CreativeWork properties, // so the shape is correct for Blog too). The @id fragment follows // the forced type instead of the fixed '#webpage', so switching a // page between e.g. Blog and WebPage never leaves a stale @id. $data = array( '@type' => $override, '@id' => (string) get_permalink( $post ) . '#' . strtolower( $override ), 'url' => (string) get_permalink( $post ), 'name' => wp_strip_all_tags( $title ), 'description' => $this->meta_resolver->description( $post ), 'isPartOf' => array( '@id' => SchemaIds::website() ), 'datePublished' => get_the_date( DATE_W3C, $post ), 'dateModified' => get_the_modified_date( DATE_W3C, $post ), ); $image = $this->meta_resolver->og_image_url( $post ); if ( '' !== $image ) { $data['primaryImageOfPage'] = $image; } } /** * Filters the user-forced main schema node (M66). * * Extension point for per-post property overrides, which acez-seo-pro * hooks (M69 Etap 3). Until then the base plugin applied them itself, * behind a licence check — exactly the pattern WordPress.org's * Guideline 5 forbids. The filter already fired here, immediately * after that step, so moving the work onto it keeps the order intact. * * @param array $data * @param \WP_Post $post * @param string $override The forced type name. */ return (array) apply_filters( 'acez_seo_forced_type', $data, $post, $override ); } /** * For real posts the separate Person node applies (PersonSchemaType is * gated to post_type 'post'), so an @id reference works exactly like * ArticleSchemaType's. For any OTHER post type forced into an * article-like shape (the very scenario M66 exists for — a builder-made * page) that node is never emitted, and an @id reference would dangle — * so the author is embedded inline instead, honoring the same per-user * profile overrides PersonSchemaType reads. * * @return array Empty array when the post has no author. */ private function author_property( \WP_Post $post ): array { $author_id = (int) $post->post_author; if ( $author_id <= 0 ) { return array(); } if ( 'post' === $post->post_type ) { return array( '@id' => SchemaIds::person( $author_id ) ); } $name_override = trim( (string) get_user_meta( $author_id, \AcezSeo\Data\UserMetaFields::PERSON_NAME, true ) ); $url_override = trim( (string) get_user_meta( $author_id, \AcezSeo\Data\UserMetaFields::PERSON_URL, true ) ); return array( '@type' => 'Person', 'name' => '' !== $name_override ? $name_override : get_the_author_meta( 'display_name', $author_id ), 'url' => '' !== $url_override ? $url_override : get_author_posts_url( $author_id ), ); } }