__( 'Search engines allowed to index the site', 'agentaisle' ), 'pass' => (bool) get_option( 'blog_public' ), 'help' => __( 'Settings > Reading: untick "Discourage search engines".', 'agentaisle' ), ); $checks[] = array( 'label' => __( 'Readable permalinks', 'agentaisle' ), 'pass' => '' !== (string) get_option( 'permalink_structure' ), 'help' => __( 'Settings > Permalinks: choose any option except "Plain".', 'agentaisle' ), ); $checks[] = array( 'label' => __( 'llms.txt is published', 'agentaisle' ), 'pass' => ! empty( agentaisle_settings()['llms_enabled'] ), 'help' => __( 'Turn it on in the llms.txt tab.', 'agentaisle' ), ); $checks[] = array( 'label' => __( 'WooCommerce product structured data is active', 'agentaisle' ), 'pass' => self::wc_schema_active(), 'help' => __( 'Another plugin or your theme may have removed it. Check your SEO plugin settings.', 'agentaisle' ), ); $checks[] = array( 'label' => __( 'Terms and conditions page set', 'agentaisle' ), 'pass' => function_exists( 'wc_terms_and_conditions_page_id' ) && wc_terms_and_conditions_page_id() > 0, 'help' => __( 'WooCommerce > Settings > Advanced > Page setup.', 'agentaisle' ), ); $checks[] = array( 'label' => __( 'Shipping information page published', 'agentaisle' ), 'pass' => self::page_exists( array( 'shipping', 'delivery' ) ), 'help' => __( 'Publish a page with "Shipping" or "Delivery" in the title.', 'agentaisle' ), ); $checks[] = array( 'label' => __( 'Returns or refund policy page published', 'agentaisle' ), 'pass' => self::page_exists( array( 'return', 'refund' ) ), 'help' => __( 'Publish a page with "Returns" or "Refund" in the title.', 'agentaisle' ), ); return $checks; } /** * Score up to PRODUCT_LIMIT products. * * @return array[] Each: id, name, edit, score, missing[]. */ public static function product_report() { if ( ! function_exists( 'wc_get_products' ) ) { return array(); } $products = wc_get_products( array( 'status' => 'publish', 'limit' => self::limit(), 'orderby' => 'date', 'order' => 'DESC', ) ); $rows = array(); foreach ( $products as $product ) { $rows[] = self::score_product( $product ); } usort( $rows, function ( $a, $b ) { return $a['score'] <=> $b['score']; } ); return $rows; } /** * Number of products the readiness report covers. * * @return int */ public static function limit() { return max( 1, (int) apply_filters( 'agentaisle_readiness_product_limit', self::PRODUCT_LIMIT ) ); } /** * Score one product. * * @param WC_Product $product Product. * @return array */ public static function score_product( $product ) { $checks = array( __( 'Price', 'agentaisle' ) => '' !== (string) $product->get_price(), __( 'Stock status', 'agentaisle' ) => '' !== (string) $product->get_stock_status(), __( 'Image', 'agentaisle' ) => (bool) $product->get_image_id(), __( 'Short description', 'agentaisle' ) => '' !== trim( wp_strip_all_tags( $product->get_short_description() ) ), __( 'Description (50+ words)', 'agentaisle' ) => str_word_count( wp_strip_all_tags( $product->get_description() ) ) >= 50, __( 'SKU', 'agentaisle' ) => '' !== (string) $product->get_sku(), __( 'GTIN / EAN / UPC', 'agentaisle' ) => self::has_gtin( $product ), __( 'Brand', 'agentaisle' ) => self::has_brand( $product ), __( 'Category', 'agentaisle' ) => (bool) array_diff( $product->get_category_ids(), array( (int) get_option( 'default_product_cat' ) ) ), ); $missing = array(); foreach ( $checks as $label => $pass ) { if ( ! $pass ) { $missing[] = $label; } } return array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'edit' => get_edit_post_link( $product->get_id(), 'raw' ), 'score' => (int) round( 100 * ( count( $checks ) - count( $missing ) ) / count( $checks ) ), 'missing' => $missing, ); } /** * Average of product scores. * * @param array $rows Product report rows. * @return int|null */ public static function average( $rows ) { if ( ! $rows ) { return null; } return (int) round( array_sum( wp_list_pluck( $rows, 'score' ) ) / count( $rows ) ); } /** * GTIN check: core field (WC 9.2+) or common meta keys. * * @param WC_Product $product Product. * @return bool */ public static function has_gtin( $product ) { if ( method_exists( $product, 'get_global_unique_id' ) && '' !== (string) $product->get_global_unique_id() ) { return true; } foreach ( array( '_gtin', '_ean', '_upc', '_barcode', 'gtin', 'ean' ) as $key ) { if ( '' !== (string) $product->get_meta( $key ) ) { return true; } } return false; } /** * Brand check: core brands taxonomy (WC 9.6+) or a brand attribute. * * @param WC_Product $product Product. * @return bool */ public static function has_brand( $product ) { if ( taxonomy_exists( 'product_brand' ) ) { $terms = wp_get_post_terms( $product->get_id(), 'product_brand', array( 'fields' => 'ids' ) ); if ( ! is_wp_error( $terms ) && $terms ) { return true; } } foreach ( array( 'pa_brand', 'brand' ) as $attr ) { if ( '' !== (string) $product->get_attribute( $attr ) ) { return true; } } return false; } /** * Whether WooCommerce's product structured data output is still hooked. * * @return bool */ private static function wc_schema_active() { if ( ! function_exists( 'WC' ) || ! isset( WC()->structured_data ) ) { return false; } return false !== has_action( 'woocommerce_single_product_summary', array( WC()->structured_data, 'generate_product_data' ) ); } /** * Whether a published page title contains any of the terms. * * @param string[] $terms Terms. * @return bool */ private static function page_exists( $terms ) { foreach ( $terms as $term ) { $found = get_posts( array( 'post_type' => 'page', 'post_status' => 'publish', 's' => $term, 'search_columns' => array( 'post_title' ), 'posts_per_page' => 1, 'fields' => 'ids', ) ); if ( $found ) { return true; } } return false; } }