PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.1-a.5
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-seo / src / class-initializer.php
jetpack / jetpack_vendor / automattic / jetpack-seo / src Last commit date
class-admin-page.php 4 weeks ago class-ai-crawlers.php 4 weeks ago class-author-schema-node.php 1 month ago class-breadcrumb-schema-node.php 1 month ago class-content-coverage.php 4 weeks ago class-dashboard-data.php 4 weeks ago class-initializer.php 4 weeks ago class-llms-txt.php 4 weeks ago class-local-business-schema-node.php 1 month ago class-organization-schema-node.php 1 month ago class-post-schema-node.php 1 month ago class-post-types.php 4 weeks ago class-schema-builder.php 1 month ago class-schema-graph.php 1 month ago class-schema-node-ids.php 1 month ago class-schema-settings-controller.php 1 month ago class-schema-settings.php 1 month ago class-surface-visibility.php 4 weeks ago class-website-schema-node.php 1 month ago
class-initializer.php
271 lines
1 <?php
2 /**
3 * Jetpack SEO — the visibility command center for WordPress sites.
4 *
5 * Gates the surface behind its feature flag and cohort, then wires the admin
6 * page ({@see Admin_Page}), the dashboard's REST reads ({@see Dashboard_Data}),
7 * the content-coverage cache invalidation ({@see Content_Coverage}), and the
8 * opt-in surface ({@see Surface_Visibility}).
9 *
10 * @package automattic/jetpack-seo-package
11 */
12
13 namespace Automattic\Jetpack\SEO;
14
15 use Automattic\Jetpack\Current_Plan;
16 use Automattic\Jetpack\Modules;
17 use Automattic\Jetpack\Status;
18 use Automattic\Jetpack\Status\Host;
19
20 /**
21 * Boots the package and carries its cross-plugin contract: the feature flag,
22 * the script-data key, and the option names / visibility reads other plugins consume.
23 */
24 class Initializer {
25
26 /**
27 * Jetpack SEO package version.
28 *
29 * @var string
30 */
31 const PACKAGE_VERSION = '0.7.0';
32
33 /**
34 * Filter name that gates the entire Jetpack SEO surface.
35 *
36 * When this filter returns true, the package registers its admin menu and
37 * loads the wp-build dashboard. Default false before release - when the
38 * filter is off the package registers no admin menu and no assets, and
39 * changes nothing about the existing Jetpack UI.
40 *
41 * @var string
42 */
43 const FEATURE_FILTER = 'rsm_jetpack_seo';
44
45 /**
46 * Key under `window.JetpackScriptData` the React app reads its state from
47 * (`window.JetpackScriptData.seo`). Must match the JS-side reader in
48 * `_inc/data/get-overview.ts`.
49 */
50 const SCRIPT_DATA_KEY = 'seo';
51
52 /**
53 * Option recording whether sitemap generation is enabled.
54 *
55 * Read in place of the standalone `sitemaps` module's active state. Module-active
56 * state is filtered against the modules present on disk, so once that module is
57 * removed it would read as inactive even for sites that had it on. A one-time
58 * migration in the Jetpack plugin seeds this option from the site's existing module
59 * state and keeps it in sync while the legacy module still exists. See
60 * `Jetpack::migrate_sitemaps_module_to_seo_option()`.
61 *
62 * @var string
63 */
64 const SITEMAP_ENABLED_OPTION = 'jetpack_seo_sitemap_enabled';
65
66 /**
67 * Option recording whether canonical URLs are enabled.
68 *
69 * Read in place of the standalone `canonical-urls` module's active state. Module-active
70 * state is filtered against the modules present on disk, so once that module is
71 * removed it would read as inactive even for sites that had it on. A one-time
72 * migration in the Jetpack plugin seeds this option from the site's existing module
73 * state and keeps it in sync while the legacy module still exists. See
74 * `Jetpack::migrate_canonical_urls_module_to_seo_option()`.
75 *
76 * @var string
77 */
78 const CANONICAL_ENABLED_OPTION = 'jetpack_seo_canonical_urls_enabled';
79
80 /**
81 * Option recording whether the Jetpack SEO surface is discoverable on this site.
82 *
83 * Gates whether the SEO admin menu registers on self-hosted sites. Seeded once by the
84 * Jetpack plugin on install/upgrade: fresh installs default to visible, existing
85 * installs default to hidden and opt in via the legacy Traffic page or My Jetpack.
86 * WordPress.com (Simple + Atomic) bypasses this option entirely and is always visible.
87 * Absent until seeded, in which case self-hosted defaults to hidden (the non-disruptive
88 * default). See {@see Surface_Visibility::is_visible()}.
89 *
90 * @var string
91 */
92 const VISIBILITY_OPTION = 'jetpack_seo_surface_visible';
93
94 /**
95 * Whether the package has been initialized.
96 *
97 * @var bool
98 */
99 private static $initialized = false;
100
101 /**
102 * Initialize the package.
103 *
104 * Called from the Jetpack plugin's `late_initialization()` hook.
105 *
106 * @return void
107 */
108 public static function init() {
109 if ( self::$initialized ) {
110 return;
111 }
112 self::$initialized = true;
113
114 // Gate the entire SEO surface behind the feature flag.
115 if ( ! (bool) apply_filters( self::FEATURE_FILTER, false ) ) {
116 return;
117 }
118
119 // The opt-in endpoint must be reachable even before the surface is visible, so
120 // existing self-hosted installs can switch to the new experience from the legacy
121 // Traffic page or My Jetpack (JETPACK-1700). Registered ahead of the cohort gate.
122 add_action( 'rest_api_init', array( Surface_Visibility::class, 'register_optin_route' ) );
123
124 // Expose opt-in availability to other admin surfaces (the legacy Traffic-page
125 // banner reads it via `@automattic/jetpack-script-data`). Hooked here — after the
126 // feature flag, before the cohort gate — so a still-hidden install gets the signal.
127 add_filter( 'jetpack_admin_js_script_data', array( Surface_Visibility::class, 'inject_optin_availability' ) );
128
129 // Discoverability cohort gate: the SEO surface is auto-discoverable for fresh
130 // installs and all WordPress.com sites; existing self-hosted installs opt in via
131 // the legacy Traffic page or My Jetpack (JETPACK-1700). Until it's visible we
132 // register nothing else here and let those opt-in surfaces drive discovery.
133 if ( ! self::is_seo_surface_visible() ) {
134 return;
135 }
136
137 // The admin menu and app shell register whenever the surface is visible, even
138 // when the `seo-tools` module is inactive, so SEO stays discoverable and can be
139 // turned on from within the page itself (JETPACK-1700). When the module is off,
140 // the Overview renders only its "enable SEO tools" affordance.
141 //
142 // Priority 1: load the wp-build bundle (and define its render function)
143 // before `add_menu_item()` runs at the default priority and needs it.
144 add_action( 'admin_menu', array( Admin_Page::class, 'maybe_load_wp_build' ), 1 );
145 add_action( 'admin_menu', array( Admin_Page::class, 'add_menu_item' ), 10 );
146
147 // Read-only REST routes the dashboard hydrates its initial state from. Preloaded
148 // into the page (see Admin_Page::inject_script_data) so a normal load resolves
149 // them with no request, and fetched by the app when that preload is missing or
150 // stale — so the dashboard recovers its data instead of dead-ending. Registered
151 // whenever the surface is visible (independent of the seo-tools module, like the
152 // Overview).
153 add_action( 'rest_api_init', array( Dashboard_Data::class, 'register_rest_reads' ) );
154
155 // Keep the Overview's cached content-coverage counts honest. Hooked here rather than
156 // alongside the admin surface above because posts are written from everywhere — the
157 // block editor (REST), the classic editor, wp-cli, cron, other plugins — and the
158 // cache has to be dropped wherever that happens, not just where it's read.
159 Content_Coverage::register_invalidation();
160
161 // The settings surface only comes online once SEO tools are active — there's
162 // nothing to configure while the module is off, so we don't register its REST
163 // endpoints until then. Expose the core `blog_public` option to the REST settings
164 // endpoint so the Settings tab can save search-engine visibility via
165 // `/wp/v2/settings` (the Jetpack settings endpoint only accepts Jetpack options).
166 // Writes are still capability-gated by the core settings controller.
167 if ( self::is_seo_tools_module_active() ) {
168 // Front-end JSON-LD schema output and author profile schema fields.
169 // Intentionally NOT gated: every site keeps emitting its structured data —
170 // a plan-gated site loses the schema *settings* card (a paid control), but
171 // stripping the schema its pages already carry would hurt SEO it has today.
172 // (Finer per-type gating — e.g. sitewide LocalBusiness to paid plans on
173 // self-hosted — is a separate follow-up, tracked in the schema project.)
174 Schema_Builder::init();
175 Author_Schema_Node::init();
176
177 // GEO-tab front-end services. These are paid surfaces on WordPress.com: a
178 // plan-gated site has the GEO tab hidden from its dashboard, so it must not
179 // keep emitting their front-end output either — otherwise it would still
180 // serve /llms.txt and AI-crawler robots.txt directives it doesn't qualify
181 // for. Self-hosted is never gated, so it always registers both.
182 if ( ! self::is_gated() ) {
183 // The /llms.txt handler. Self-hooks a front-end action, so it no-ops off
184 // the front end and stays behind the same gates as the schema above.
185 Llms_Txt::init();
186 // robots.txt directives for blocked AI crawlers. Self-hooks the
187 // `robots_txt` filter, so it stays inert off the front end.
188 Ai_Crawlers::init();
189 }
190
191 add_action( 'rest_api_init', array( Dashboard_Data::class, 'register_rest_settings' ) );
192 // Package-owned route for the site-level Schema settings (see the controller).
193 add_action( 'rest_api_init', array( Schema_Settings_Controller::class, 'register_routes' ) );
194 }
195
196 /**
197 * Fires after the Jetpack SEO package is initialized.
198 *
199 * @since 0.1.0
200 */
201 do_action( 'jetpack_seo_init' );
202 }
203
204 /**
205 * Whether the Jetpack SEO surface should be discoverable (admin menu registered).
206 *
207 * @return bool
208 */
209 public static function is_seo_surface_visible() {
210 return Surface_Visibility::is_visible();
211 }
212
213 /**
214 * Whether to offer an existing install the chance to opt into the new SEO experience.
215 *
216 * @return bool
217 */
218 public static function is_optin_available() {
219 return Surface_Visibility::is_optin_available();
220 }
221
222 /**
223 * Whether the SEO dashboard is plan-gated for this site.
224 *
225 * Gating applies only on WordPress.com (Simple + Atomic): `advanced-seo` is in the
226 * FREE plan's supports list, so `Current_Plan::supports( 'advanced-seo' )` returns
227 * true on self-hosted (never gated) and hijacks to `wpcom_site_has_feature()` on
228 * WordPress.com, where it's false below the Premium plan. Mirrors the AI SEO
229 * Enhancer's plan check in {@see Dashboard_Data::get_ai_data()}.
230 *
231 * Public because {@see Admin_Page::inject_script_data()} reads it to build the
232 * dashboard's gating payload, and {@see self::init()} uses it to decide whether the
233 * GEO-tab front-end services register at all.
234 *
235 * @return bool
236 */
237 public static function is_gated() {
238 return ( new Host() )->is_wpcom_platform()
239 && ! Current_Plan::supports( 'advanced-seo' );
240 }
241
242 /**
243 * The WordPress.com Premium checkout URL for this site, used by the upsell banner
244 * shown to gated sites.
245 *
246 * Built server-side because the client doesn't have the site slug. `value_bundle`
247 * is the wpcom Premium plan slug (see the `premium` entry in
248 * `Automattic\Jetpack\Current_Plan`), and `Status::get_site_suffix()` resolves the
249 * Calypso site slug (via `WPCOM_Masterbar::get_calypso_site_slug()` on wpcom).
250 *
251 * @return string
252 */
253 public static function get_upsell_url() {
254 $site_slug = ( new Status() )->get_site_suffix();
255
256 return sprintf( 'https://wordpress.com/checkout/%s/value_bundle', $site_slug );
257 }
258
259 /**
260 * Whether the `seo-tools` Jetpack module is currently active.
261 *
262 * @return bool
263 */
264 private static function is_seo_tools_module_active() {
265 if ( ! class_exists( 'Automattic\\Jetpack\\Modules' ) ) {
266 return false;
267 }
268 return ( new Modules() )->is_active( 'seo-tools' );
269 }
270 }
271