diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed7f132 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# WDS Headless SEO + +Please see for more information. diff --git a/activation.php b/activation.php new file mode 100644 index 0000000..a85d220 --- /dev/null +++ b/activation.php @@ -0,0 +1,23 @@ + esc_html__( 'Archive SEO data', 'wds-headless-seo' ), + 'fields' => [ + 'title' => [ 'type' => 'String' ], + 'metaDesc' => [ 'type' => 'String' ], + 'metaRobotsNoindex' => [ 'type' => 'String' ], + 'metaRobotsNofollow' => [ 'type' => 'String' ], + 'canonical' => [ 'type' => 'String' ], + ], + ] + ); + + // Get post types that support archives (will not include "post" PT). + $post_types = get_post_types( + [ + 'has_archive' => true, + ], + 'objects' + ); + + // Bail if we don't have an array of post types. + if ( empty( $post_types ) || ! is_array( $post_types ) ) { + return; + } + + // Register GraphQL field on each post type's plural/archive connection. + foreach ( $post_types as $post_type => $post_type_object ) { + if ( ! $post_type_object->show_in_graphql || ! $post_type_object->graphql_single_name ) { + break; + } + + $pt_singular = ucfirst( $post_type_object->graphql_single_name ); + + register_graphql_field( + "RootQueryTo{$pt_singular}Connection", + 'archiveSeo', + [ + 'type' => 'ArchiveSeo', + 'description' => sprintf( + /* translators: the post type label. */ + __( 'The Yoast SEO data of the %s post type archive', 'wds-headless-seo' ), + $post_type_object->label + ), + 'resolve' => function () use ( $post_type, $post_type_object ) { + // Surface info: https://developer.yoast.com/blog/yoast-seo-14-0-using-yoast-seo-surfaces/. + /* We would ideally use this surface to determine meta title and desc, but the surface is not pulling the correct meta for those fields (as of Yoast 15.9.2, it seems to be pulling some default archive meta title and a blank desc). */ + $archive_seo = YoastSEO()->meta->for_post_type_archive( $post_type ); + + // Retrieve Yoast SEO options for archive title and desc instead. + $wpseo_options = WPSEO_Options::get_instance(); + $title = $wpseo_options->get( "title-ptarchive-{$post_type}" ); + $description = $wpseo_options->get( "metadesc-ptarchive-{$post_type}", $archive_seo->description ); + + /* Manually replace title vars that won't get caught in next step (e.g., pt_single, pt_plural) -- these appear to be "advanced" vars that require a single post to be passed, rather than a post type object. */ + $title = str_ireplace( '%%pt_single%%', $post_type_object->labels->singular_name, $title ); + $title = str_ireplace( '%%pt_plural%%', $post_type_object->labels->name, $title ); + + // Replace standard title vars with post type object. + $title = wpseo_replace_vars( $title, $post_type_object ); + + return [ + 'title' => wp_gql_seo_format_string( $title ?? $archive_seo->title ), + 'metaDesc' => wp_gql_seo_format_string( $description ), + 'metaRobotsNoindex' => $archive_seo->robots['index'], + 'metaRobotsNofollow' => $archive_seo->robots['follow'], + 'canonical' => $archive_seo->canonical, + ]; + }, + ] + ); + } +} +add_action( 'graphql_register_types', __NAMESPACE__ . '\register_archive_seo' ); diff --git a/wds-headless-seo.php b/wds-headless-seo.php new file mode 100644 index 0000000..520ba86 --- /dev/null +++ b/wds-headless-seo.php @@ -0,0 +1,33 @@ + + * Author URI: https://webdevstudios.com + * Version: 1.0.0 + * Requires at least: 5.6 + * Requires PHP: 7.4 + * License: GPL-2 + * License URI: https://www.gnu.org/licenses/gpl-2.0.html + * + * @package WDS_Headless_SEO + */ + +namespace WDS_Headless_SEO; + +if ( ! defined( 'ABSPATH' ) ) { + return; +} + +// Define constants. +define( 'WDS_HEADLESS_SEO_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); +define( 'WDS_HEADLESS_SEO_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); +define( 'WDS_HEADLESS_SEO_VERSION', '1.0.0' ); + +// Register de/activation hooks. +register_activation_hook( __FILE__, __NAMESPACE__ . '\activation_callback' ); +register_deactivation_hook( __FILE__, __NAMESPACE__ . '\deactivation_callback' ); + +require_once 'inc/links.php'; +require_once 'inc/wp-graphql.php';