diff --git a/includes/Api/Controllers/CacheController.php b/includes/Api/Controllers/CacheController.php index 07065f5..9444001 100644 --- a/includes/Api/Controllers/CacheController.php +++ b/includes/Api/Controllers/CacheController.php @@ -4,6 +4,7 @@ use NewfoldLabs\WP\Module\Patterns\SiteClassification; use NewfoldLabs\WP\Module\Data\WonderBlocks\Requests\Fetch as WonderBlocksFetchRequest; use NewfoldLabs\WP\Module\Data\WonderBlocks\WonderBlocks; +use NewfoldLabs\WP\Module\Patterns\CSSUtilities; /** * Controller for cache. @@ -71,6 +72,9 @@ public static function clear_cache( \WP_REST_Request $request ) { $response['categories'] = 'Cache cleared'; } + + // Refresh the CSS utilities assets. + CSSUtilities::get_instance()->refresh_assets(); return new \WP_REST_Response( $response, 200 ); } diff --git a/includes/CSSUtilities.php b/includes/CSSUtilities.php index f1a9480..b747b14 100644 --- a/includes/CSSUtilities.php +++ b/includes/CSSUtilities.php @@ -5,6 +5,13 @@ use NewfoldLabs\WP\Module\Data\WonderBlocks\Requests\Fetch; class CSSUtilities { + /** + * The single instance of the class. + * + * @var CSSUtilities|null + */ + private static $instance = null; + /** * The production base URL. * @@ -19,10 +26,22 @@ class CSSUtilities { */ protected static $local_base_url = 'http://localhost:8888'; + /** + * Get the single instance of the class. + * + * @return CSSUtilities The instance of the class. + */ + public static function get_instance(): CSSUtilities { + if ( null === self::$instance ) { + self::$instance = new self(); + } + return self::$instance; + } + /** * Constructor. */ - public function __construct() { + private function __construct() { \add_action( 'enqueue_block_assets', array( $this, 'enqueue' ) ); \add_action( 'enqueue_nfd_wonder_blocks_utilities', array( $this, 'enqueue' ) ); } @@ -33,46 +52,52 @@ public function __construct() { * @return void */ public function enqueue() { - - $base_url = $this->get_base_url(); + // Refresh assets if 24 hours have passed since the last refresh. + $this->conditional_refresh_assets(); - $remote_css = $base_url . '/assets/css/utilities.css'; - $remote_js = $base_url . '/assets/js/utilities.js'; - - $css_url = $this->is_valid_remote_file( $remote_css ) - ? $remote_css - : constant( 'NFD_WONDER_BLOCKS_URL' ) . '/assets/build/utilities.css'; - $css_version = $css_url === $remote_css - ? strtotime( 'midnight' ) - : constant( 'NFD_WONDER_BLOCKS_VERSION' ); - - $js_url = $this->is_valid_remote_file( $remote_js ) - ? $remote_js - : constant( 'NFD_WONDER_BLOCKS_URL' ) . '/assets/build/utilities.js'; - $js_version = $js_url === $remote_js - ? strtotime( 'midnight' ) - : constant( 'NFD_WONDER_BLOCKS_VERSION' ); - - \wp_register_style( - 'nfd-wonder-blocks-utilities', - $css_url, - array(), - $css_version - ); - - \wp_register_script( - 'nfd-wonder-blocks-utilities', - $js_url, - array(), - $js_version - ); - - \wp_enqueue_style( 'nfd-wonder-blocks-utilities' ); - \wp_enqueue_script( 'nfd-wonder-blocks-utilities' ); + $css_content = $this->get_asset_content( 'utilities_css' ); + $js_content = $this->get_asset_content( 'utilities_js' ); + + if ( $css_content ) { + \wp_register_style( 'nfd-wonder-blocks-utilities', false ); + \wp_enqueue_style( 'nfd-wonder-blocks-utilities'); + \wp_add_inline_style( 'nfd-wonder-blocks-utilities', $css_content ); + } else { + \wp_enqueue_style( + 'nfd-wonder-blocks-utilities', + constant( 'NFD_WONDER_BLOCKS_URL' ) . '/assets/build/utilities.css', + array(), + constant( 'NFD_WONDER_BLOCKS_VERSION' ) + ); + } + + if ( $js_content ) { + \wp_register_script( 'nfd-wonder-blocks-utilities', false ); + \wp_enqueue_script( 'nfd-wonder-blocks-utilities' ); + \wp_add_inline_script( 'nfd-wonder-blocks-utilities', $js_content ); + } else { + \wp_enqueue_script( + 'nfd-wonder-blocks-utilities', + constant( 'NFD_WONDER_BLOCKS_URL' ) . '/assets/build/utilities.js', + array(), + constant( 'NFD_WONDER_BLOCKS_VERSION' ) + ); + } \wp_add_inline_style( 'nfd-wonder-blocks-utilities', $this->get_inline_css() ); } + /** + * Get the content of an asset. + * + * @param string $asset The asset to get the content of. + * @return string The content of the asset. + */ + private function get_asset_content( string $option_key ) { + $sanitized_key = sanitize_key( 'nfd_' . $option_key ); + return get_option( $sanitized_key, false ); + } + /** * Generates inline CSS based on the current active theme. * @@ -83,7 +108,6 @@ public function enqueue() { * @return string The generated CSS. */ private function get_inline_css() { - $theme = \wp_get_theme()->get_template(); $css = ''; @@ -120,7 +144,7 @@ private function get_inline_css() { --nfd-cp-text-secondary: var(--wp--preset--color--secondary, #000); }"; - $css = "body .is-layout-constrained:has(.wndb-container.is-layout-constrained) > .wndb-container.is-layout-constrained { + $css .= "body .is-layout-constrained:has(.wndb-container.is-layout-constrained) > .wndb-container.is-layout-constrained { width: 100%; max-width: unset; }"; @@ -141,43 +165,90 @@ private function get_inline_css() { } /** - * Check if a remote file is valid. - * - * Stores the resulting HTTP status (or "error") in a transient for 24 hours. - * {@see wp_remote_retrieve_response_code()} returns 200 even for redirects. - * - * @param string $url URL of the remote file. + * Get the base URL + * + * @return string The base URL. */ - private function is_valid_remote_file( string $url ): bool { - // Reverse the url because transient key length is limited and truncated and the unique part of the URL is its end. - $transient_key = 'nfd_css_utilities_valid_' . strrev($url ); + public function get_base_url(): string { + if ( defined( 'NFD_DATA_WB_DEV_MODE' ) && constant( 'NFD_DATA_WB_DEV_MODE' ) ) { + return self::$local_base_url; + } - $status_code = get_transient( $transient_key ); + return self::$production_base_url; + } - if( false === $status_code || ! is_numeric( $status_code ) ) { + /** + * Conditionally refresh CSS and JS assets from remote sources if 24 hours have passed. + * + * @return void + */ + public function conditional_refresh_assets() { + $last_refresh = get_option( 'nfd_utilities_last_refresh_time', 0 ); + $current_time = time(); + + if ( ( $current_time - $last_refresh ) > DAY_IN_SECONDS ) { + $this->refresh_assets(); + update_option( 'nfd_utilities_last_refresh_time', $current_time ); + } + } - $response = \wp_remote_head( $url, array( 'timeout' => 5 ) ); + /** + * Refresh CSS and JS assets from remote sources. + * This method can be manually triggered by other actions or hooks as needed. + * + * @return void + */ + public function refresh_assets() { + $this->fetch_and_store_asset( '/assets/css/utilities.css', 'utilities_css' ); + $this->fetch_and_store_asset( '/assets/js/utilities.js', 'utilities_js' ); + } - $status_code = is_wp_error( $response ) - ? 'error' - : \wp_remote_retrieve_response_code( $response ); + /** + * Fetch and store the asset content in the database with minification. + * + * @param string $path The path of the remote asset. + * @param string $option_key The option key to store the content. + * + * @return void + */ + private function fetch_and_store_asset( string $path, string $option_key ) { + $base_url = $this->get_base_url(); + $url = esc_url_raw( $base_url . $path ); - set_transient( $transient_key, $status_code, constant( 'DAY_IN_SECONDS' ) ); + $response = \wp_remote_get( $url ); + + if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) { + $content = wp_remote_retrieve_body( $response ); + + // Minify the content for storage + $minified_content = $this->minify_content( $content, $path ); + $sanitized_key = sanitize_key( 'nfd_' . $option_key ); + update_option( $sanitized_key, wp_slash( $minified_content ) ); } - - return 200 === intval( $status_code ); } /** - * Get the base URL - * - * @return string The base URL. + * Minify the CSS or JS content. + * + * @param string $content The content to minify. + * @param string $path The path to determine if it's CSS or JS. + * + * @return string The minified content. */ - public function get_base_url(): string { - if ( defined( 'NFD_DATA_WB_DEV_MODE' ) && constant( 'NFD_DATA_WB_DEV_MODE' ) ) { - return self::$local_base_url; + private function minify_content( string $content, string $path ): string { + if ( strpos( $path, '.css' ) !== false ) { + // Minify CSS by removing comments, whitespace, etc. + $content = preg_replace( '/\s+/', ' ', $content ); + $content = preg_replace( '/\s*([{};:>+~,])\s*/', '$1', $content ); + $content = preg_replace( '/;}/', '}', $content ); + $content = preg_replace( '/\/\*.*?\*\//', '', $content ); + } elseif ( strpos( $path, '.js' ) !== false ) { + // Minify JS by removing comments and whitespace. + $content = preg_replace( '/\/\*.*?\*\//s', '', $content ); + $content = preg_replace( '/\s+/', ' ', $content ); + $content = preg_replace( '/\s*([{};,:>+\-~])\s*/', '$1', $content ); + $content = preg_replace( '/;}/', '}', $content ); } - - return self::$production_base_url; + return trim( $content ); } } diff --git a/includes/Patterns.php b/includes/Patterns.php index e7e462f..7e2b9ba 100644 --- a/includes/Patterns.php +++ b/includes/Patterns.php @@ -35,7 +35,8 @@ public function __construct( Container $container ) { new CTA(); } - new CSSUtilities(); + CSSUtilities::get_instance(); + new RestApi(); new BlockStyles(); }