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     * The plan term is `advanced-seo`, the entitlement for the SEO title and
24     * meta description fields every AI SEO surface writes to. Automatic
25     * generation needs `ai-seo-enhancer` on top, checked where it runs.
26     *
27     * @since 0.8.2
28     *
29     * @return bool
30     */
31    public static function is_available() {
32        /** This filter is documented in projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php */
33        return (bool) apply_filters( 'ai_seo_enhancer_enabled', true )
34            && ! Seo_Tools::is_disabled()
35            && ( new Modules() )->is_active( 'seo-tools' )
36            && Current_Plan::supports( 'advanced-seo' );
37    }
38
39    /**
40     * Whether an AI SEO surface can actually run here, so a control that governs
41     * nothing is never offered. The sidebar's suggestions need a host that loads
42     * the sidebar; the editor's generation needs `ai-seo-enhancer`.
43     *
44     * @since 0.9.1
45     *
46     * @return bool
47     */
48    public static function has_reachable_surface() {
49        return self::is_available()
50            && ( self::is_sidebar_reachable() || Current_Plan::supports( 'ai-seo-enhancer' ) );
51    }
52
53    /**
54     * Whether this host loads the AI sidebar. Called as a string callable: the
55     * class lives in plugins/jetpack, which bundles this package, and asking it
56     * anything wider would route back through {@see self::is_available()}.
57     *
58     * @return bool
59     */
60    private static function is_sidebar_reachable() {
61        $callback = array( 'Automattic\\Jetpack\\Extensions\\AiAssistantPlugin\\Jetpack_AI_Sidebar', 'is_host_enabled' );
62
63        // @phan-suppress-next-line PhanUndeclaredClassInCallable -- Jetpack_AI_Sidebar lives in plugins/jetpack and is guarded by is_callable.
64        return is_callable( $callback ) && (bool) call_user_func( $callback );
65    }
66}