This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,151 @@
<?php
namespace Automattic\Jetpack_Boost\Lib\Cornerstone;
use Automattic\Jetpack\Schema\Schema;
use Automattic\Jetpack_Boost\Contracts\Has_Setup;
use Automattic\Jetpack_Boost\Data_Sync\Cornerstone_Pages_Entry;
use Automattic\Jetpack_Boost\Lib\Premium_Features;
class Cornerstone_Pages implements Has_Setup {
const PREMIUM_MAX_PAGES = 10;
const FREE_MAX_PAGES = 1;
public function setup() {
$this->register_ds_stores();
add_filter( 'jetpack_boost_critical_css_providers', array( $this, 'remove_ccss_front_page_provider' ), 10, 2 );
add_filter( 'display_post_states', array( $this, 'add_display_post_states' ), 10, 2 );
add_action( 'init', array( $this, 'set_default_pages' ), 0 );
}
/**
* Set the default pages for the first time Boost is running on the website.
*/
public function set_default_pages() {
// Since the DS store always returns an empty array, we can't know if the user
// wants an empty list of pages or this is the first time Boost is running on the website
// and we need to actually load the default pages.
$raw_pages = \get_option( 'jetpack_boost_ds_cornerstone_pages_list' );
if ( $raw_pages === false ) {
jetpack_boost_ds_set( 'cornerstone_pages_list', $this->default_pages() );
}
}
private function register_ds_stores() {
$schema = Schema::as_array( Schema::as_string() )->fallback( array() );
jetpack_boost_register_option( 'cornerstone_pages_list', $schema, new Cornerstone_Pages_Entry( 'cornerstone_pages_list' ) );
jetpack_boost_register_readonly_option( 'cornerstone_pages_properties', array( $this, 'get_properties' ) );
}
public function remove_ccss_front_page_provider( $providers ) {
$filtered_providers = array();
foreach ( $providers as $provider ) {
if ( $provider['key'] !== 'core_front_page' ) {
$filtered_providers[] = $provider;
}
}
return $filtered_providers;
}
private function default_pages() {
$max_pages = $this->get_max_pages();
$yoast_cornerstone_pages = $this->get_yoast_cornerstone_pages();
$woocommerce_pages = $this->get_woocommerce_pages();
$urls = array_unique( array_merge( $woocommerce_pages, $yoast_cornerstone_pages ) );
$urls = array_map( 'untrailingslashit', $urls );
// An empty string represents the home page.
// Remove it if it's in the list since the home page is a predefined page.
$urls = array_filter( $urls );
return array_slice( $urls, 0, $max_pages );
}
private function get_yoast_cornerstone_pages() {
$urls = array();
if ( ! function_exists( 'wpseo_init' ) ) {
return $urls;
}
$max_pages = $this->get_max_pages();
$yoast_cornerstone_content = get_posts(
array(
'meta_key' => '_yoast_wpseo_is_cornerstone',
'meta_value' => '1',
'post_type' => 'any',
'posts_per_page' => $max_pages,
)
);
foreach ( $yoast_cornerstone_content as $post ) {
$permalink = get_permalink( $post->ID );
if ( $permalink ) {
$relative_permalink = $this->make_relative_url( $permalink );
$urls[] = $relative_permalink;
}
}
return $urls;
}
private function get_woocommerce_pages() {
$urls = array();
if ( ! function_exists( 'wc_get_page_id' ) ) {
return $urls;
}
$shop_page_id = \wc_get_page_id( 'shop' );
if ( $shop_page_id ) {
$url = get_permalink( $shop_page_id );
if ( $url ) {
$relative_url = $this->make_relative_url( $url );
if ( $relative_url ) {
$urls[] = $relative_url;
}
}
}
return $urls;
}
private function make_relative_url( $url ) {
if ( is_string( $url ) && strpos( $url, home_url() ) === 0 ) {
$url = substr( $url, strlen( home_url() ) );
}
return $url;
}
public function get_properties() {
$properties = array(
'max_pages' => $this->get_max_pages(),
'max_pages_premium' => static::PREMIUM_MAX_PAGES,
'default_pages' => array(),
'predefined_pages' => Cornerstone_Utils::get_predefined_list(),
);
// We need this to ensure we don't include the home page in the default pages since an empty array is returned when no default pages are found.
$default_pages = $this->default_pages();
if ( ! empty( $default_pages ) ) {
$properties['default_pages'] = array_map( 'home_url', $default_pages );
}
return $properties;
}
public function add_display_post_states( $post_states, $post ) {
if ( Cornerstone_Utils::is_cornerstone_page( $post->ID ) ) {
$post_states[] = __( 'Cornerstone Page', 'jetpack-boost' );
}
return $post_states;
}
private function get_max_pages() {
return Premium_Features::has_feature( Premium_Features::CORNERSTONE_TEN_PAGES ) ? static::PREMIUM_MAX_PAGES : static::FREE_MAX_PAGES;
}
}
@@ -0,0 +1,147 @@
<?php
namespace Automattic\Jetpack_Boost\Lib\Cornerstone;
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\Cornerstone_Provider;
class Cornerstone_Utils {
/**
* Get the list of cornerstone pages.
*
* @return string[] The relative URLs of all the cornerstone pages.
*/
public static function get_list() {
/**
* Filters the list of cornerstone pages. This list includes the predefined and custom pages.
* If you want to change the list of custom pages, use `jetpack_boost_cornerstone_pages_list` instead.
*
* @since 4.4.0-beta1
*
* @param string[] $urls The absolute URLs of all the cornerstone pages.
*/
return apply_filters( 'jetpack_boost_cornerstone_pages_list_complete', array_merge( self::get_predefined_list(), self::get_custom_list() ) );
}
/**
* Gets the list of Cornerstone Pages that the user has added to the custom list.
*
* @return string[] The absolute URLs of the cornerstone pages.
*
* @since 4.2.0
*/
public static function get_custom_list() {
$pages = jetpack_boost_ds_get( 'cornerstone_pages_list' );
// Bail early if no pages are found.
if ( empty( $pages ) ) {
return array();
}
return self::maybe_trailing_slash_urls( $pages );
}
/**
* Gets the list of Cornerstone Pages that the user cannot remove.
*
* @return string[] The absolute URLs of the cornerstone pages.
*
* @since 4.2.0
*/
public static function get_predefined_list() {
return self::maybe_trailing_slash_urls( array( home_url() ) );
}
/**
* Checks if a URL is a cornerstone page.
*
* @param string $url The URL to check.
* @return bool True if the URL is a cornerstone page, false otherwise.
*/
public static function is_cornerstone_page_by_url( $url ) {
$cornerstone_pages = self::get_list();
if ( empty( $cornerstone_pages ) ) {
return false;
}
$cornerstone_pages = array_map( 'untrailingslashit', $cornerstone_pages );
return in_array( self::sanitize_url( $url ), $cornerstone_pages, true );
}
/**
* Sanitize a URL to make it a compatible cornerstone page URL.
*
* @param string $url The URL to sanitize.
* @return string The sanitized URL.
*/
public static function sanitize_url( $url ) {
return untrailingslashit( $url );
}
/**
* Get the provider key for a given URL.
*
* @param string $url The URL to get the provider key for.
* @return string The provider key.
*/
public static function get_provider_key( $url ) {
return Cornerstone_Provider::get_provider_key( self::sanitize_url( $url ) );
}
/**
* Prepare provider data for a given URL.
* This is usually sent to the Cloud API.
*
* @param string $url The URL to prepare provider data for.
* @return array The provider data.
*/
public static function prepare_provider_data( $url ) {
return array(
'key' => self::get_provider_key( $url ),
// Send the URL verbatim (do not sanitize/untrailingslashit it). The cloud analyzer
// fetches this exact URL, so it must keep the site's canonical trailing-slash form to
// avoid edge/WAF 301s or 403s that break analysis. The `key` is intentionally still
// stripped so storage/retrieval stays slash-agnostic. Do not "simplify" url back to
// self::sanitize_url( $url ).
'url' => $url,
);
}
/**
* Checks if the current page is a cornerstone page.
*
* @return bool True if the current page is a cornerstone page, false otherwise.
*
* @since 3.13.1
*/
public static function is_current_page_cornerstone() {
return self::is_cornerstone_page_by_url( Cornerstone_Provider::get_request_url() );
}
/**
* Check if a post ID is a cornerstone page.
*
* @param int $post_id The ID of the post to check.
* @return bool True if the post is a cornerstone page, false otherwise.
*/
public static function is_cornerstone_page( $post_id ) {
return self::is_cornerstone_page_by_url( get_permalink( $post_id ) );
}
/**
* Adds trailing slashes to URLs if the current permalink structure requires it.
*
* @param string[] $urls The URLs to process.
* @return string[] The processed URLs.
*/
public static function maybe_trailing_slash_urls( $urls ) {
$permalink_structure = \get_option( 'permalink_structure' );
// If permalink structure ends with slash, add trailing slashes.
if ( $permalink_structure && substr( $permalink_structure, -1 ) === '/' ) {
$urls = array_map( 'trailingslashit', $urls );
}
return $urls;
}
}