Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
9 / 9
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%
9 / 9
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%
5 / 5
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            /** This filter is documented in projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-utils.php */
35            && ! apply_filters( 'jetpack_disable_seo_tools', false )
36            && ( new Modules() )->is_active( 'seo-tools' )
37            && Current_Plan::supports( 'advanced-seo' );
38    }
39
40    /**
41     * Whether an AI SEO surface can actually run here, so a control that governs
42     * nothing is never offered. The sidebar's suggestions need a host that loads
43     * the sidebar; the editor's generation needs `ai-seo-enhancer`.
44     *
45     * @since 0.9.1
46     *
47     * @return bool
48     */
49    public static function has_reachable_surface() {
50        return self::is_available()
51            && ( self::is_sidebar_reachable() || Current_Plan::supports( 'ai-seo-enhancer' ) );
52    }
53
54    /**
55     * Whether this host loads the AI sidebar. Called as a string callable: the
56     * class lives in plugins/jetpack, which bundles this package, and asking it
57     * anything wider would route back through {@see self::is_available()}.
58     *
59     * @return bool
60     */
61    private static function is_sidebar_reachable() {
62        $callback = array( 'Automattic\\Jetpack\\Extensions\\AiAssistantPlugin\\Jetpack_AI_Sidebar', 'is_host_enabled' );
63
64        // @phan-suppress-next-line PhanUndeclaredClassInCallable -- Jetpack_AI_Sidebar lives in plugins/jetpack and is guarded by is_callable.
65        return is_callable( $callback ) && (bool) call_user_func( $callback );
66    }
67}