Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
Ai_Seo
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
3 / 3
9
100.00% covered (success)
100.00%
1 / 1
 is_available
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 has_reachable_surface
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 is_sidebar_reachable
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * The AI SEO feature's availability gate. This package owns the SEO surfaces
4 * the feature writes to, so the shared predicate lives here.
5 *
6 * @package automattic/jetpack-seo-package
7 */
8
9namespace Automattic\Jetpack\SEO;
10
11use Automattic\Jetpack\Current_Plan;
12use Automattic\Jetpack\Modules;
13
14/**
15 * Whether AI SEO is offered on this site.
16 */
17class Ai_Seo {
18
19    /**
20     * Whether the site can offer AI SEO, independent of the admin's toggle.
21     * Callers own the outer AI master and host gates.
22     *
23     * AI keeps the legacy `advanced-seo` entitlement; ordinary SEO no longer uses it.
24     * Automatic generation also checks `ai-seo-enhancer` where it runs.
25     *
26     * @since 0.8.2
27     *
28     * @return bool
29     */
30    public static function is_available() {
31        /** This filter is documented in projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php */
32        return (bool) apply_filters( 'ai_seo_enhancer_enabled', true )
33            && ! Seo_Tools::is_disabled()
34            && ( new Modules() )->is_active( 'seo-tools' )
35            && Current_Plan::supports( 'advanced-seo' );
36    }
37
38    /**
39     * Whether an AI SEO surface can actually run here, so a control that governs
40     * nothing is never offered. The sidebar's suggestions need a host that loads
41     * the sidebar; the editor's generation needs `ai-seo-enhancer`.
42     *
43     * @since 0.9.1
44     *
45     * @return bool
46     */
47    public static function has_reachable_surface() {
48        return self::is_available()
49            && ( self::is_sidebar_reachable() || Current_Plan::supports( 'ai-seo-enhancer' ) );
50    }
51
52    /**
53     * Whether this host loads the AI sidebar. Called as a string callable: the
54     * class lives in plugins/jetpack, which bundles this package, and asking it
55     * anything wider would route back through {@see self::is_available()}.
56     *
57     * @return bool
58     */
59    private static function is_sidebar_reachable() {
60        $callback = array( 'Automattic\\Jetpack\\Extensions\\AiAssistantPlugin\\Jetpack_AI_Sidebar', 'is_host_enabled' );
61
62        // @phan-suppress-next-line PhanUndeclaredClassInCallable -- Jetpack_AI_Sidebar lives in plugins/jetpack and is guarded by is_callable.
63        return is_callable( $callback ) && (bool) call_user_func( $callback );
64    }
65}