'boolean', 'sanitize_callback' => static function ( $value ) { return (int) (bool) $value; }, 'default' => 1, ) ); register_setting( 'smfree_settings_group', SMFREE_OPTION_PREFIX . 'coexistence_mode', array( 'type' => 'string', 'sanitize_callback' => array( __CLASS__, 'sanitize_coexistence_mode' ), 'default' => Coexistence::MODE_MERGE, ) ); register_setting( 'smfree_settings_group', SMFree_FAQ_Detector::OPTION_ENABLED, array( 'type' => 'boolean', 'sanitize_callback' => array( __CLASS__, 'sanitize_checkbox' ), 'default' => 1, ) ); register_setting( 'smfree_settings_group', SMFree_FAQ_Detector::OPTION_DEBUG, array( 'type' => 'boolean', 'sanitize_callback' => array( __CLASS__, 'sanitize_checkbox' ), 'default' => 0, ) ); } /** * Sanitize a checkbox setting to 0/1. * * @param mixed $value Raw input. */ public static function sanitize_checkbox( $value ): int { return (int) (bool) $value; } /** * Sanitize the coexistence mode setting. * * @param mixed $value Raw input. */ public static function sanitize_coexistence_mode( $value ): string { return smfree_normalize_coexistence_mode( is_string( $value ) ? sanitize_key( $value ) : '' ); } /** * Add a Settings link on the Plugins list row. * * @param array $links Existing links. * @return array */ public static function plugin_action_links( array $links ): array { $settings_url = admin_url( 'admin.php?page=' . self::MENU_SLUG ); $upgrade_url = 'https://schemamonster.com/pricing/'; $prepend = array( sprintf( '%s', esc_url( $settings_url ), esc_html__( 'Settings', 'schema-monster' ) ), sprintf( '%s', esc_url( $upgrade_url ), esc_html__( 'Upgrade to Mega', 'schema-monster' ) ), ); return array_merge( $prepend, $links ); } // ------------------------------------------------------------------ // Shared header, footer, wrapper. // ------------------------------------------------------------------ /** * Render the plugin's header (wordmark + version badge). */ private static function render_header(): void { $logo_url = SMFREE_PLUGIN_URL . 'assets/images/schema-monster-logo.png'; ?>
<?php esc_attr_e( 'Schema Monster', 'schema-monster' ); ?>
v

/>

/>

/>

' . esc_html( 'wp-content/uploads/' . SMFree_FAQ_Detector::LOG_DIR . '/' ) . '' ); ?>

true ), 'names' ); $blocklist = self::internal_post_type_blocklist(); foreach ( $blocklist as $slug ) { unset( $post_types[ $slug ] ); } unset( $post_types['attachment'] ); /** * Filter: modify the list of post types shown on the Coverage page. * Return an array of post type slugs. * * @param array $post_types Post type slugs to include. */ $post_types = (array) apply_filters( 'smfree_coverage_post_types', $post_types ); // Deprecated pre-1.0.15 name, kept for back-compat. // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound $post_types = (array) apply_filters_deprecated( 'smf_coverage_post_types', array( $post_types ), '1.0.15', 'smfree_coverage_post_types' ); $candidates = get_posts( array( 'post_type' => array_values( $post_types ), 'post_status' => 'publish', 'posts_per_page' => 100, 'orderby' => 'modified', 'order' => 'DESC', 'no_found_rows' => true, ) ); $posts = array(); foreach ( $candidates as $candidate ) { if ( self::is_excluded_from_coverage( $candidate ) ) { continue; } $posts[] = $candidate; if ( count( $posts ) >= 50 ) { break; } } ?>

ID ); $types = self::extract_types( $graph ); ?>
post_type ); ?>

*/ private static function internal_post_type_blocklist(): array { $default = array( // Elementor and its addon ecosystem. 'elementor_library', 'e-landing-page', 'elementskit_content', // ElementsKit widget templates. 'elementskit_template', // ElementsKit page templates. 'ua-templates', // Ultimate Addons for Elementor. 'ha-templates', // Happy Addons templates. 'jet-engine', // JetEngine content types (often internal). // CartFlows. 'cartflows_step', 'cartflows_flow', // Themes. 'oceanwp_library', 'astra-advanced-hook', 'astra_adv_header', 'brizy_template', // Form plugins. 'wpforms', 'gravityforms', 'wpcf7_contact_form', // ACF. 'acf-field', 'acf-field-group', // WooCommerce internals. 'shop_order', 'shop_coupon', 'shop_order_refund', 'product_variation', // WordPress core internals. 'nav_menu_item', 'revision', ); /** * Filter: modify the list of internal post type slugs excluded from * Coverage. * * @param array $default Default blocklist. */ $internal = (array) apply_filters( 'smfree_coverage_internal_post_types', $default ); // Deprecated pre-1.0.15 name, kept for back-compat. // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound return (array) apply_filters_deprecated( 'smf_coverage_internal_post_types', array( $internal ), '1.0.15', 'smfree_coverage_internal_post_types' ); } /** * True when a post should not appear on the Coverage viewer because a * search engine wouldn't index it. * * @param \WP_Post $post Post to check. */ private static function is_excluded_from_coverage( \WP_Post $post ): bool { if ( '' !== (string) $post->post_password ) { return true; } $yoast_noindex = get_post_meta( (int) $post->ID, '_yoast_wpseo_meta-robots-noindex', true ); if ( '1' === (string) $yoast_noindex ) { return true; } $rm_robots = get_post_meta( (int) $post->ID, 'rank_math_robots', true ); if ( is_array( $rm_robots ) && in_array( 'noindex', $rm_robots, true ) ) { return true; } $aioseo_new = get_post_meta( (int) $post->ID, '_aioseo_noindex', true ); $aioseo_old = get_post_meta( (int) $post->ID, '_aioseop_noindex', true ); if ( in_array( (string) $aioseo_new, array( 'on', '1' ), true ) ) { return true; } if ( in_array( (string) $aioseo_old, array( 'on', '1' ), true ) ) { return true; } /** * Filter: allow a site to exclude additional posts from Coverage. * * @param bool $excluded Current exclusion decision. * @param \WP_Post $post The post being evaluated. */ $excluded = (bool) apply_filters( 'smfree_coverage_exclude_post', false, $post ); // Deprecated pre-1.0.15 name, kept for back-compat. // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound return (bool) apply_filters_deprecated( 'smf_coverage_exclude_post', array( $excluded, $post ), '1.0.15', 'smfree_coverage_exclude_post' ); } /** * Flatten @type values from a graph into a comma-friendly list. * * @param array $graph JSON-LD graph. * @return array */ private static function extract_types( array $graph ): array { if ( empty( $graph['@graph'] ) || ! is_array( $graph['@graph'] ) ) { return array(); } $types = array(); foreach ( $graph['@graph'] as $node ) { if ( ! is_array( $node ) || empty( $node['@type'] ) ) { continue; } $node_types = is_array( $node['@type'] ) ? $node['@type'] : array( $node['@type'] ); foreach ( $node_types as $t ) { if ( is_string( $t ) && '' !== $t ) { $types[] = $t; } } } return array_values( array_unique( $types ) ); } }