Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-theme
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Compat
*
* @package TCB\Lightspeed
*/
class Compat {
public static function init() {
if ( Main::is_optimizing() ) {
add_filter( 'get_post_metadata', [ __CLASS__, 'disable_seo_press' ], 10, 3 );
add_filter( 'get_term_metadata', [ __CLASS__, 'disable_seo_press' ], 10, 3 );
add_filter( 'option_wpo_minify_config', [ __CLASS__, 'disable_wpo_css_merge' ] );
add_filter( 'option_301_redirects', '__return_empty_array' );
add_action( 'plugins_loaded', [ __CLASS__, 'disable_autoptimize' ], 0 );
/* disable optimole replace when optimizing */
add_filter( 'optml_should_replace_page', '__return_false' );
add_filter( 'optml_extracted_urls', '__return_empty_array' );
/* Disable wp rocket combine and minify when we optimize assets */
add_filter( 'get_rocket_option_minify_concatenate_css', '__return_false' );
add_filter( 'get_rocket_option_minify_css', '__return_false' );
static::disable_redirection_plugin();
static::disable_wp_fastest_cache();
static::disable_litespeed();
}
}
/**
* When running an optimization, prevent any redirects from SEO Press
*
* @param $value
* @param $object_id
* @param $meta_key
*
* @return false|mixed
*/
public static function disable_seo_press( $value, $object_id, $meta_key ) {
if ( $meta_key === '_seopress_redirections_enabled' ) {
$value = false;
}
return $value;
}
/**
* Disable wp optimize css merge when we optimize the css so we can find it in the page
*
* @param $options
*
* @return mixed
*/
public static function disable_wpo_css_merge( $options ) {
if ( is_array( $options ) ) {
$options['enable_merging_of_css'] = false;
$options['enable_css_minification'] = false;
$options['inline_css'] = false;
}
return $options;
}
/**
* Disable redirection plugin when we try to optimize a page
*/
public static function disable_redirection_plugin() {
define( 'REDIRECTION_DISABLE', true );
}
/**
* Disable litespeed css combine when optimization is running
*/
public static function disable_litespeed() {
if ( class_exists( '\LiteSpeed\Conf', false ) ) {
\LiteSpeed\Conf::get_instance()->force_option( 'optm-css_comb', false );
}
}
/**
* Disable fastest cache while optimizing assets
*/
public static function disable_wp_fastest_cache() {
if ( ! isset( $GLOBALS['wp_fastest_cache_options'] ) ) {
return;
}
if ( is_array( $GLOBALS['wp_fastest_cache_options'] ) ) {
$GLOBALS['wp_fastest_cache_options']['wpFastestCacheCombineCss'] = false;
$GLOBALS['wp_fastest_cache_options']['wpFastestCacheMinifyCss'] = false;
$GLOBALS['wp_fastest_cache_options']['wpFastestCacheStatus'] = false;
} else if ( is_object( $GLOBALS['wp_fastest_cache_options'] ) ) {
$GLOBALS['wp_fastest_cache_options']->wpFastestCacheCombineCss = false;
$GLOBALS['wp_fastest_cache_options']->wpFastestCacheMinifyCss = false;
$GLOBALS['wp_fastest_cache_options']->wpFastestCacheStatus = false;
}
}
/**
* Disable autoptimize when optimizing assets
*/
public static function disable_autoptimize() {
if ( function_exists( 'autoptimize' ) ) {
remove_action( 'autoptimize_setup_done', array( autoptimize(), 'check_cache_and_run' ) );
}
}
}

View File

@@ -0,0 +1,469 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class Css
*
* @package TCB\Lightspeed
*/
class Css {
private static $instances = [];
private $styles_loaded = [];
private $ID;
private $post;
public static function get_instance( $post_id = 0 ) {
if ( empty( self::$instances[ $post_id ] ) ) {
self::$instances[ $post_id ] = new self( $post_id );
}
return self::$instances[ $post_id ];
}
public function __construct( $post_id ) {
$this->ID = (int) $post_id;
$this->post = get_post( $this->ID );
}
/**
* Css file is saved because we change it every time
*
* @param string $type
* @param bool $regenerate
*
* @return mixed|string
*/
public function get_css_filename( $type = '', $regenerate = false ) {
$meta_key = '_tve_' . $type . '_css_file';
$filename = get_post_meta( $this->ID, $meta_key, true );
if ( $regenerate ) {
$filename = 'tcb-' . $type . '-css-' . $this->ID . '-' . time() . '.css';
update_post_meta( $this->ID, $meta_key, $filename );
}
return $filename;
}
/**
* Return the handle used for enqueue-ing the style for the current post
*
* @param string $type
*
* @return string
*/
public function get_style_handle( $type = '' ) {
return sprintf( 'tcb-style-%s-%s-%s', $type, $this->post->post_type, $this->ID );
}
/**
* Save css either in post meta or in individual file
*
* @param string $type
* @param string $css
*/
public function save_optimized_css( $type = 'base', $css = '' ) {
$inline_css_meta_key = "_tve_{$type}_inline_css";
if ( ! is_string( $css ) ) {
$css = '';
}
/* we want to keep the encoded open/close tag characters (they might be part of background SVGs) */
$css = str_replace( [ '%3C', '%3E' ], [ '[tcb-encoded-open-tag]', '[tcb-encoded-close-tag]' ], $css );
$css = sanitize_text_field( $css );
/* restore the encoded tag characters */
$css = str_replace( [ '[tcb-encoded-open-tag]', '[tcb-encoded-close-tag]' ], [ '%3C', '%3E' ], $css );
$css = tve_minify_css( $css );
$css = static::compat( $css, $this->ID );
switch ( $this->get_css_location( $type ) ) {
case 'file':
if ( ! $this->write_css_file( $type, $css ) ) {
/* as fallback, if we can't write the file, save the css in meta */
update_post_meta( $this->ID, $inline_css_meta_key, $css );
}
break;
case 'head':
case 'footer':
default:
update_post_meta( $this->ID, $inline_css_meta_key, $css );
break;
}
/* if no css is being saved, then remove the lightspeed version */
update_post_meta( $this->ID, Main::OPTIMIZATION_VERSION_META, empty( $css ) ? 0 : Main::LIGHTSPEED_VERSION );
}
/**
* Depending on settings, load post style
* > if nothing is set, load the the whole flat styles file
* > if css optimization is on, load only specific styles, either from post meta or from individual file
*
* @param string $type
* @param array $deps
*/
public function load_optimized_style( $type, $deps = [] ) {
if ( empty( $this->styles_loaded[ $type ] ) ) {
if ( $this->should_load_optimized_styles() ) {
$location = $this->get_css_location( $type );
if ( $location === 'file' ) {
/* if by any chance the file doesn't exist, load the css in head */
$file = $this->get_css_filename( $type );
if ( empty( $file ) || ! file_exists( Main::upload_dir( 'basedir' ) . $file ) ) {
$location = 'head';
}
}
switch ( $location ) {
case 'file':
if ( did_action( 'wp_head' ) ) {
echo $this->get_optimized_styles( 'file', $type );
} else {
$this->enqueue_style( $type, $deps );
}
break;
case 'head':
case 'footer':
$hook = 'wp_' . $location;
if ( tve_post_is_landing_page( $this->ID ) || did_action( $hook ) ) {
echo $this->get_optimized_styles( 'inline', $type );
} else {
add_action( $hook, function () use ( $type ) {
echo $this->get_optimized_styles( 'inline', $type );
}, 0 );
}
break;
case 'inline':
default:
echo $this->get_optimized_styles( 'inline', $type );
}
} else {
/* load thrive flat only if this post needs it. */
if ( static::should_load_flat() || ! Main::is_enabled() || Main::requires_architect_assets( $this->ID ) ) {
static::enqueue_flat();
}
/**
* Action called when we're not optimizing the css, just so we can take any other action needed
*
* @param $type string type of css that we wanted to display
* @param $post \WP_Post post object with all the information
*/
do_action( 'tcb_lightspeed_load_unoptimized_styles', $type, $this->post );
}
}
$this->styles_loaded[ $type ] = true;
}
/**
* Enqueue style based on type
*
* @param string $type
* @param array $deps
*/
public function enqueue_style( $type = '', $deps = [] ) {
wp_enqueue_style( $this->get_style_handle( $type ), Main::upload_dir( 'baseurl' ) . $this->get_css_filename( $type ), $deps );
}
/**
* Enqueue thrive flat styles
*/
public static function enqueue_flat() {
tve_enqueue_style( tve_get_style_enqueue_id(), static::get_flat_url( false ) );
}
/**
* Return the inline css for the current post
*
* @param string $type
*
* @return mixed|void
*/
public function get_inline_css( $type = 'base' ) {
$inline_css = get_post_meta( $this->ID, "_tve_{$type}_inline_css", true );
if ( ! is_string( $inline_css ) ) {
$inline_css = '';
}
$inline_css = static::get_compat_css( $inline_css, $this->ID );
/**
* Filters the inline css for the current post.
*
* @param $inline_css string the css that we want to use
* @param $type string type of css in case we have multiple saves for a single post
* @param $ID int ID of the current post
*
* @return string
*/
return apply_filters( 'tcb_lightspeed_inline_css', $inline_css, $type, $this->ID );
}
/**
* Run compat function on CSS
*
* @param $inline_css
* @param $id
*
* @return string
*/
public static function get_compat_css( $inline_css, $id = 0 ) {
$inline_css = Fonts::parse_google_fonts( $inline_css );
$inline_css = tve_minify_css( $inline_css );
$inline_css = static::compat( $inline_css, $id );
return $inline_css;
}
/**
* Render styles node for the current post
*
* @param string $location
* @param string $type
* @param boolean $optimize_on_load
*/
public function get_optimized_styles( $location = 'inline', $type = 'base', $optimize_on_load = true ) {
switch ( $location ) {
case 'file':
$styles = sprintf(
"<link rel='stylesheet' id='%s' href='%s' type='text/css' media='all' />",
$this->get_style_handle( $type ),
Main::upload_dir( 'baseurl' ) . $this->get_css_filename( $type )
);
break;
case 'inline':
default:
$styles = static:: get_inline_style_node( $this->get_style_handle( $type ), $this->get_inline_css( $type ), $optimize_on_load );
break;
}
return $styles;
}
/**
* Build a style node for CSS
*
* @param $handle
* @param $css
* @param $should_optimize
*
* @return string
*/
public static function get_inline_style_node( $handle, $css, $should_optimize = true ) {
return sprintf( '<style type="text/css" id="%s" %s class="tcb-lightspeed-style">%s</style>',
$handle,
$should_optimize ? ' onLoad="typeof window.lightspeedOptimizeStylesheet === \'function\' && window.lightspeedOptimizeStylesheet()"' : '',
$css );
}
/**
* Always load flat file in the editor and when we set a specific param
*
* @return bool
*/
public static function should_load_flat() {
$should_load_flat = isset( $_GET['force-flat'] ) || is_editor_page_raw( true );
/**
* Allow plugins to short-circuit the loading of optimized assets on certain pages
*
* @param boolean $should_load_flat if true, we'll load thrive_flat.css on the current request
*/
return apply_filters( 'tcb_lightspeed_should_load_flat', $should_load_flat );
}
/**
* General check to see if we load flat or we load optimized styles
*
* @return bool
*/
public function should_load_optimized_styles() {
return Main::has_optimized_assets( $this->ID ) && ! static::should_load_flat();
}
/**
* Save css in individual file
*
* @param string $type
* @param string $css
*
* @return boolean
*/
public function write_css_file( $type, $css ) {
$styles_dir = Main::upload_dir( 'basedir' );
/* remove old style files for this post */
foreach ( scandir( $styles_dir ) as $file ) {
if ( strpos( $file, 'tcb-' . $type . '-css-' . $this->ID . '-' ) === 0 ) {
unlink( $styles_dir . $file );
}
}
return \TCB_Utils::write_file( $styles_dir . $this->get_css_filename( $type, true ), wp_unslash( $css ) );
}
/**
* Each post reads from specific stylesheets and save styles
*
* @return mixed|void
*/
public function get_styles_to_optimize() {
$default_styles = [
'tve_style_family_tve_flt',
'tve_landing_page_base_css',
'the_editor_no_theme',
];
/**
* Filters used to decide for each post what styles need optimization and from what stylesheets
*
* @param array $default_styles array
* @param int $post_id
*
* @return array
*/
return apply_filters( 'tcb_lightspeed_styles_to_optimize', $default_styles, $this->ID );
}
/**
* If in some cases we just need the inline flat style file
*
* @return string
*/
public static function inline_flat_style() {
return sprintf(
'<link rel="stylesheet" id="%s-css" href="%s" type="text/css" media="all" onLoad="typeof window.lightspeedOptimizeFlat === \'function\' && window.lightspeedOptimizeFlat(this)" />',
tve_get_style_enqueue_id(),
static::get_flat_url()
);
}
/**
* Get the location of thrive flat
*
* @param bool $include_version
*
* @return string
*/
public static function get_flat_url( $include_version = true ) {
return tve_editor_css( 'thrive_flat.css' ) . ( $include_version ? '?v=' . TVE_VERSION : '' );
}
/**
* Add specific browser rules in case they weren't included
*
* @param string $css
* @param int $id
*
* @return string
*/
public static function compat( $css, $id ) {
/* fit-content for chrome vs. -moz-fit-content for firefox */
$css = preg_replace_callback( '/[;|{]([^:]*):(-moz-)?fit-content( !important)?/m', static function ( $matches ) {
$pre = stripos( $matches[0], '-moz' ) === false ? '-moz-' : '';
$is_important = strpos( $matches[0], 'important' );
return $matches[0] . ';' . $matches[1] . ':' . $pre . 'fit-content' . ( $is_important ? ' !important' : '' );
}, $css );
$css = preg_replace_callback( '/inset:([^;]*);/m', static function ( $matches ) {
/* there are cases where 'inset:' comes with a space after ':' */
$matched_values = explode( ' ', ltrim( $matches[1] ) );
switch ( count( $matched_values ) ) {
case 4:
/* inset:2.4em 3em 3em 5em; - top right bottom left */
list( $top, $right, $bottom, $left ) = $matched_values;
break;
case 3:
/* inset:5% 15px 10px; - top left/right bottom */
$top = $matched_values[0];
$left = $matched_values[1];
$right = $matched_values[1];
$bottom = $matched_values[2];
break;
case 2:
list( $top, $right ) = $matched_values;
/* inset:4px 8px; - top/bottom left/right */
list( $bottom, $left ) = $matched_values;
break;
/* inset:10px; - applied to all edges */
case 1:
default:
$top = $bottom = $left = $right = $matches[1];
break;
}
return
'top:' . $top . ';' .
'right:' . $right . ';' .
'bottom:' . $bottom . ';' .
'left:' . $left . ';';
}, $css );
$css = preg_replace_callback( '/mask-\w*:[^;]*;/m', static function ( $matches ) {
return $matches[0] . '-webkit-' . $matches[0];
}, $css );
/**
* Filters the inline css for the current post.
*
* @param $css string the css that we want to use
* @param $id int content ID
*
* @return string
*/
return apply_filters( 'tcb_lightspeed_css_compat', $css, $id );
}
/**
* Where should we display the css
*
* @param $type
*
* @return string
*/
public function get_css_location( $type ) {
/* this will allow controlling the location of stored CSS directly on client sites by defining this constant */
$css_location = defined( 'TVE_CSS_LOCATION' ) ? TVE_CSS_LOCATION : 'inline';
/**
* Filter the location of the css for the current type and post
*
* @param string $location file|inline|head|footer
* @param string $type
* @param int $post_id
*/
return apply_filters( 'tcb_lightspeed_css_location', $css_location, $type, $this->ID );
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class Dashboard
*
* @package TCB\Lightspeed
*/
class Dashboard {
const MENU_SLUG = 'tve_lightspeed';
const TITLE = 'Project Lightspeed';
public static function init() {
add_action( 'admin_menu', [ __CLASS__, 'admin_menu' ] );
add_filter( 'tve_dash_filter_features', [ __CLASS__, 'tve_dash_filter_features' ] );
add_filter( 'tve_dash_features', [ __CLASS__, 'tve_dash_features' ] );
add_action( 'admin_enqueue_scripts', [ __CLASS__, 'admin_enqueue_scripts' ] );
if ( ! \tvd_is_during_update() ) {
add_action( 'admin_print_footer_scripts', [ __CLASS__, 'render_backbone_templates' ] );
}
}
public static function admin_menu() {
add_submenu_page(
'',
static::TITLE,
static::TITLE,
'manage_options',
static::MENU_SLUG,
static function () {
include TVE_TCB_ROOT_PATH . '/admin/includes/views/asset-optimization.php';
}
);
}
/**
* Add Card to dashboard
*
* @param array $features
*
* @return array
*/
public static function tve_dash_filter_features( $features ) {
$features['lightspeed'] = array(
'icon' => 'tvd-lightspeed',
'title' => static::TITLE,
'description' => __( 'Optimize your site assets for speed', 'thrive-cb' ),
'btn_link' => add_query_arg( 'page', static::MENU_SLUG, admin_url( 'admin.php' ) ),
'btn_text' => __( 'Speed settings', 'thrive-cb' ),
);
return $features;
}
/**
* Enable lightspeed card when architect is active
*
* @param $features
*
* @return mixed
*/
public static function tve_dash_features( $features ) {
$features['lightspeed'] = true;
return $features;
}
public static function admin_enqueue_scripts( $screen = '' ) {
if ( ! empty( $screen ) && $screen === 'admin_page_tve_lightspeed' ) {
tve_dash_enqueue();
tve_dash_enqueue_script( 'tcb-admin-lightspeed', tve_editor_url( 'admin/assets/js/lightspeed.min.js' ), [
'jquery',
'backbone',
], TVE_VERSION, true );
tve_dash_enqueue_style( 'tcb-admin-lightspeed', tve_editor_url( 'admin/assets/css/admin-lightspeed.css' ) );
$data = [
'options' => [
'is_enabled' => Main::is_enabled(),
Fonts::ENABLE_ASYNC_FONTS_LOAD => Fonts::is_loading_fonts_async(),
Fonts::ENABLE_FONTS_OPTIMIZATION => Fonts::is_enabled(),
Fonts::DISABLE_GOOGLE_FONTS => Fonts::is_blocking_google_fonts(),
Gutenberg::DISABLE_GUTENBERG => Gutenberg::is_gutenberg_disabled(),
Gutenberg::DISABLE_GUTENBERG_LP => Gutenberg::is_gutenberg_disabled( true ),
Emoji::DISABLE_EMOJI => Emoji::is_emoji_disabled()
],
'nonce' => wp_create_nonce( 'wp_rest' ),
'route' => get_rest_url( get_current_blog_id(), 'tcb/v1/lightspeed' ),
/* if a user needs a bigger timeout, he can set the constant in wp config */
'timeout' => defined( 'LIGHTSPEED_TIMEOUT' ) ? LIGHTSPEED_TIMEOUT : 10,
't' => array(
'assets' => __( 'Asset Optimization', 'thrive-cb' ),
'fonts' => __( 'Font Settings', 'thrive-cb' ),
'advanced' => __( 'Advanced Settings', 'thrive-cb' ),
)
];
if ( \TCB\Integrations\WooCommerce\Main::active() ) {
$data['options'][ Woocommerce::DISABLE_WOOCOMMERCE ] = Woocommerce::is_woocommerce_disabled();
$data['options'][ Woocommerce::DISABLE_WOOCOMMERCE_LP ] = Woocommerce::is_woocommerce_disabled( true );
}
wp_localize_script( 'tcb-admin-lightspeed', 'lightspeed_localize', $data );
}
}
public static function render_backbone_templates() {
$templates = tve_dash_get_backbone_templates( TVE_TCB_ROOT_PATH . 'admin/includes/views/templates/lightspeed', 'lightspeed' );
tve_dash_output_backbone_templates( $templates, 'lightspeed-' );
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
class Emoji {
const DISABLE_EMOJI = '_tve_disable_emoji';
/**
* Checks if emoji scripts are disabled on a certain page
*
* @return bool
*/
public static function is_emoji_disabled() {
return ! empty( get_option( static::DISABLE_EMOJI ) );
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
class Fonts {
public static $fonts_rendered = false;
public static $font_families = [];
public static $font_subsets = [];
const ENABLE_FONTS_OPTIMIZATION = '_tve_enable_fonts_optimization';
const ENABLE_ASYNC_FONTS_LOAD = '_tve_enable_fonts_async_load';
const DISABLE_GOOGLE_FONTS = 'tve_google_fonts_disable_api_call';
/**
* Check if we deliver optimized fonts
* @return bool
*/
public static function is_enabled() {
return ! empty( get_option( static::ENABLE_FONTS_OPTIMIZATION, 0 ) );
}
/**
* Check if fonts are delivered async or not
* @return bool
*/
public static function is_loading_fonts_async() {
return ! empty( get_option( static::ENABLE_ASYNC_FONTS_LOAD, 0 ) );
}
/**
* Check if we remove google fonts from editor css
* @return bool
*/
public static function is_blocking_google_fonts() {
return ! empty( get_option( static::DISABLE_GOOGLE_FONTS, 0 ) );
}
/**
* Save google fonts from inline css
*
* @param string $inline_css
*
* @return string
*/
public static function parse_google_fonts( $inline_css ) {
if ( static::$fonts_rendered || wp_doing_ajax() || \TCB_Utils::is_rest() || ! static::is_enabled() || is_editor_page() ) {
return $inline_css;
}
preg_match_all( '/@import url\("[^"]*fonts\.googleapis\.com\/css\?family=([^&]*)&subset=([^"]*)"\);/m', $inline_css, $matches );
if ( ! empty( $matches ) && count( $matches ) === 3 ) {
list( $font_string, $font_families, $subsets ) = $matches;
if ( ! empty( $font_families ) ) {
static::$font_families = array_unique( array_merge( static::$font_families, $font_families ) );
static::$font_subsets = array_unique( array_merge( static::$font_subsets, $subsets ) );
$inline_css = str_replace( $font_string, '', $inline_css );
}
}
return $inline_css;
}
/**
* Merge google fonts into one request
*/
public static function render_optimized_google_fonts() {
if ( ! empty( static::$font_families ) ) {
echo '<link href="https://fonts.gstatic.com" crossorigin rel="preconnect" />';
if ( static::is_loading_fonts_async() ) {
/* if we want to preload fonts async */
$attr = 'rel="preload" as="style" onload="this.rel=\'stylesheet\'"';
} else {
/* load fonts normally */
$attr = 'rel="stylesheet"';
}
echo sprintf( '<link type="text/css" %s href="https://fonts.googleapis.com/css?family=%s&subset=%s&display=swap">',
$attr,
implode( '|', static::$font_families ),
implode( '&', static::$font_subsets )
);
static::$fonts_rendered = true;
}
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class Gutenberg
*
* @package TCB\Lightspeed
*/
class Gutenberg {
const DISABLE_GUTENBERG_LP = '_tve_disable_gutenberg_lp';
const DISABLE_GUTENBERG = '_tve_disable_gutenberg';
const HAS_GUTENBERG = '_tve_js_modules_gutenberg';
public static function get_gutenberg_assets( $module = '', $key = '' ) {
$data = [
'gutenberg' => [
'identifier' => '[class^="wp-block"]',
],
];
if ( ! empty( $key ) ) {
$data = array_map( static function ( $item ) use ( $key ) {
return empty( $item[ $key ] ) ? [] : $item[ $key ];
}, $data );
}
return empty( $module ) ? $data : $data[ $module ];
}
/**
* Checks if gutenberg scripts are disabled on a certain page
*
* @param false $is_lp
*
* @return bool
*/
public static function is_gutenberg_disabled( $is_lp = false ) {
return ! empty( get_option( $is_lp ? static::DISABLE_GUTENBERG_LP : static::DISABLE_GUTENBERG, 0 ) );
}
public static function needs_gutenberg_assets() {
$id = get_the_ID();
if ( ! static::is_gutenberg_disabled( tve_post_is_landing_page( $id ) ) || ! empty( $_GET['force-all-js'] ) || is_editor_page_raw() || empty( get_post_meta( $id, 'tcb2_ready', true ) ) ) {
return true;
}
$has_gutenberg = static::process_gutenberg_meta( $id );
return ! empty( get_post_meta( $id, static::HAS_GUTENBERG, true ) ) && $has_gutenberg;
}
/**
* Process the gutenberg meta at the post level
*
* @param $id
*
* @return bool
*/
public static function process_gutenberg_meta( $id ) {
$gutenberg_meta = get_post_meta( $id, static::DISABLE_GUTENBERG, true );
/* for enabled */
$has_gutenberg = true;
if ( $gutenberg_meta === '' ) {
/* for inherit */
$gutenberg_disabled = static::is_gutenberg_disabled( tve_post_is_landing_page( $id ) );
$has_gutenberg = ! $gutenberg_disabled;
} else if ( $gutenberg_meta === '0' ) {
/* for disabled */
$has_gutenberg = false;
}
return $has_gutenberg;
}
/**
* f we have gutenberg added on this page we save the optimize modules
*
* @param $post_id
* @param $post_content
*/
public static function update_post( $post_id, $post ) {
/* We need this only if we are editing with WP */
if ( ! empty( $_POST['action'] ) && $_POST['action'] === 'tcb_editor_ajax' ) {
return;
}
$post_content = $post->post_content;
$data = $post_content && ( strpos( $post_content, 'wp-block' ) !== false || strpos( $post_content, '/wp:' ) !== false ) ? [ 'gutenberg' ] : [];
update_post_meta( $post_id, static::HAS_GUTENBERG, $data );
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-theme
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Hooks
*
* @package TCB\Lightspeed
*/
class Hooks {
public static function add_actions() {
add_action( 'init', [ Dashboard::class, 'init' ] );
add_action( 'wp_enqueue_scripts', [ __CLASS__, 'wp_enqueue_scripts' ] );
add_action( 'rest_api_init', [ Rest_Api::class, 'register_routes' ] );
add_action( 'wp_head', [ Fonts::class, 'render_optimized_google_fonts' ], PHP_INT_MAX );
if ( Main::is_enabled() ) {
add_action( 'wp_head', [ __CLASS__, 'insert_optimization_script' ], - 24 );
}
add_action( 'post_updated', [ Gutenberg::class, 'update_post' ], 10, 3 );
}
public static function wp_enqueue_scripts() {
/**
* Filter the post id that we're trying to optimize
*
* @param int $post_id
*
* @return int
*/
$post_to_optimize = apply_filters( 'tcb_lightspeed_post_to_optimize', get_the_ID() );
if ( Main::is_optimizing() && current_user_can( 'edit_post', $post_to_optimize ) ) {
tve_dash_enqueue_script(
'tcb-lightspeed-optimize', tve_editor_js( '/lightspeed-optimize.min.js' ),
/**
* Filer used to add dependencies for the optimization script in case we want something to be loaded earlier
*
* @param array $dependencies
* @param int $post_to_optimize
*
* @return array
*/
apply_filters( 'tcb_lightspeed_front_optimize_dependencies', [ 'jquery' ], $post_to_optimize ),
false,
true
);
/**
* Filter the data that is sent to the optimization script
*
* @param array $data
*
* @return array
*/
$localize_data = apply_filters( 'tcb_lightspeed_optimize_localize_data', [
'post' => get_post( $post_to_optimize ),
'key' => '',
'route' => get_rest_url( get_current_blog_id(), 'tcb/v1/lightspeed/optimize' ),
'nonce' => \TCB_Utils::create_nonce(),
'styles' => Css::get_instance( $post_to_optimize )->get_styles_to_optimize(),
'js_modules' => JS::get_module_data( '', 'identifier' ),
'optimization_type' => [ 'css', 'js' ],
] );
if ( \TCB\Integrations\WooCommerce\Main::active() ) {
$localize_data['optimization_type'][] = 'woo';
$localize_data['woo_modules'] = Woocommerce::get_woocommerce_assets( null, 'identifier' );
}
$localize_data['optimization_type'][] = 'gutenberg';
$localize_data['gutenberg_modules'] = Gutenberg::get_gutenberg_assets( null, 'identifier' );
wp_localize_script( 'tcb-lightspeed-optimize', 'tcb_lightspeed_optimize', $localize_data );
}
}
public static function insert_optimization_script() {
?>
<script type="text/javascript">
window.flatStyles = window.flatStyles || ''
window.lightspeedOptimizeStylesheet = function () {
const currentStylesheet = document.querySelector( '.tcb-lightspeed-style:not([data-ls-optimized])' )
if ( currentStylesheet ) {
try {
if ( currentStylesheet.sheet && currentStylesheet.sheet.cssRules ) {
if ( window.flatStyles ) {
if ( this.optimizing ) {
setTimeout( window.lightspeedOptimizeStylesheet.bind( this ), 24 )
} else {
this.optimizing = true;
let rulesIndex = 0;
while ( rulesIndex < currentStylesheet.sheet.cssRules.length ) {
const rule = currentStylesheet.sheet.cssRules[ rulesIndex ]
/* remove rules that already exist in the page */
if ( rule.type === CSSRule.STYLE_RULE && window.flatStyles.includes( `${rule.selectorText}{` ) ) {
currentStylesheet.sheet.deleteRule( rulesIndex )
} else {
rulesIndex ++
}
}
/* optimize, mark it such, move to the next file, append the styles we have until now */
currentStylesheet.setAttribute( 'data-ls-optimized', '1' )
window.flatStyles += currentStylesheet.innerHTML
this.optimizing = false
}
} else {
window.flatStyles = currentStylesheet.innerHTML
currentStylesheet.setAttribute( 'data-ls-optimized', '1' )
}
}
} catch ( error ) {
console.warn( error )
}
if ( currentStylesheet.parentElement.tagName !== 'HEAD' ) {
/* always make sure that those styles end up in the head */
const stylesheetID = currentStylesheet.id;
/**
* make sure that there is only one copy of the css
* e.g display CSS
*/
if ( ( ! stylesheetID || ( stylesheetID && ! document.querySelector( `head #${stylesheetID}` ) ) ) ) {
document.head.prepend( currentStylesheet )
} else {
currentStylesheet.remove();
}
}
}
}
window.lightspeedOptimizeFlat = function ( styleSheetElement ) {
if ( document.querySelectorAll( 'link[href*="thrive_flat.css"]' ).length > 1 ) {
/* disable this flat if we already have one */
styleSheetElement.setAttribute( 'disabled', true )
} else {
/* if this is the first one, make sure he's in head */
if ( styleSheetElement.parentElement.tagName !== 'HEAD' ) {
document.head.append( styleSheetElement )
}
}
}
</script>
<?php
}
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class JS_Module
*
* @package TCB\Lightspeed
*/
class JSModule {
private $key;
private $libraries;
private $dependencies;
private $loaded;
private static $instances = [];
public static function get_instance( $key = '', $libraries = [] ) {
if ( empty( self::$instances[ $key ] ) ) {
self::$instances[ $key ] = new self( $key, $libraries );
}
return self::$instances[ $key ];
}
public function __construct( $key = '', $libraries = [] ) {
$this->key = $key;
$this->libraries = $libraries;
}
private function get_enqueue_key() {
return 'tve_frontend_' . $this->key;
}
/**
* Module url
*
* @param boolean $include_version
*
* @return string
*/
public function get_url( $include_version = true ) {
return tve_editor_js( '/modules/' . $this->key . \TCB_Utils::get_js_suffix() ) . ( $include_version ? '?v=' . TVE_VERSION : '' );
}
/**
* @param bool $print_inline
*/
public function load( $print_inline = false ) {
/* allow multiple enqueue because it doesn't affect the code */
if ( $this->loaded === null || ! $print_inline ) {
$this->dependencies = [];
$file_url = $this->get_url();
$enqueue_key = $this->get_enqueue_key();
/* load any additional libraries that this module needs */
$this->loaded = $this->load_libraries( $print_inline );
if ( $print_inline ) {
$this->loaded .= sprintf( '<script id="%s" type="text/javascript" src="%s" ></script>', $enqueue_key, $file_url );
} else {
/* all the modules have to depend on tve_frontend in order to makes sure that it's always loaded first */
$this->dependencies[] = 'tve_frontend';
tve_enqueue_script( $enqueue_key, $file_url, $this->dependencies, false, true );
$this->loaded = '';
}
}
return $this->loaded;
}
/**
* @param bool $print_inline
*/
public function load_libraries( $print_inline = false ) {
$inline_libraries = '';
foreach ( $this->libraries as $handler => $url ) {
if ( $print_inline ) {
$inline_libraries .= sprintf( '<script type="text/javascript" src="%s"></script>', $url );
} elseif ( ! in_array( $handler, JS::LIBRARIES_ENQUEUED_AUTOMATICALLY, true ) ) {
tve_enqueue_script( $handler, $url, [ 'jquery' ], false, true );
$this->dependencies[] = $handler;
}
}
return $inline_libraries;
}
}

View File

@@ -0,0 +1,362 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class JS
*
* @package TCB\Lightspeed
*/
class JS {
private static $instances = [];
private $ID;
private $key;
private $modules;
public static $is_fully_loaded = false;
public static function get_instance( $post_id = 0, $key = '' ) {
$instance = $post_id . '_' . $key;
if ( empty( self::$instances[ $instance ] ) ) {
self::$instances[ $instance ] = new self( $post_id );
}
return self::$instances[ $instance ]->set_key( "_tve_js_modules$key" );
}
public function __construct( $post_id ) {
$this->ID = $post_id;
}
/**
* Set meta key for reading/writing modules
*
* @param $key
*
* @return $this
*/
public function set_key( $key ) {
$this->key = $key;
return $this;
}
/**
* Save js modules that are used in this post
*
* @param array $modules Array of modules
*/
public function save_js_modules( $modules = [] ) {
if ( ! is_array( $modules ) ) {
$modules = [];
}
update_post_meta( $this->ID, $this->key, $modules );
}
public function enqueue_scripts() {
/* enqueue general file with the base functionality */
tve_enqueue_script( 'tve_frontend', tve_editor_js() . '/modules/general' . \TCB_Utils::get_js_suffix(), [
'jquery',
'jquery-ui-autocomplete',
'jquery-masonry',
] );
$this->load_modules();
}
/**
* Load js modules needed for the current post, or all of them if it was not optimized
*
* @param false $return_inline
*
* @return string
*/
public function load_modules( $return_inline = false ) {
$js_modules = '';
$modules_to_load = $this->get_modules_to_load();
foreach ( $modules_to_load as $module ) {
$js_modules .= JSModule::get_instance( $module, static::get_module_data( $module, 'libraries' ) )->load( $return_inline );
}
do_action( 'tve_lightspeed_enqueue_module_scripts', $this->ID, $modules_to_load );
return $js_modules;
}
/**
* Return urls for the modules we have to load
*
* @return array|array[]
*/
public function get_modules_urls() {
$modules_to_load = $this->get_modules_to_load();
return JS::get_js_urls( $modules_to_load );
}
/**
* Return urls for an array of modules
*
* @return array|array[]
*/
public static function get_js_urls( $modules ) {
$urls = [];
if ( is_array( $modules ) ) {
foreach ( $modules as $module ) {
$urls = array_merge( $urls, static::get_module_data( $module, 'libraries' ) );
$urls[ $module ] = JSModule::get_instance( $module, [] )->get_url();
}
}
return $urls;
}
public function has_optimized_modules() {
return
empty( $_GET['force-all-js'] ) &&
Main::is_enabled() &&
! is_editor_page_raw() && /* never optimize editor JS */
metadata_exists( 'post', $this->ID, $this->key ); /* make sure the meta is set */
}
public function get_modules_to_load() {
if ( $this->has_optimized_modules() ) {
if ( empty( $this->modules ) ) {
$this->modules = get_post_meta( $this->ID, $this->key, true );
if ( empty( $this->modules ) || ! is_array( $this->modules ) ) {
$this->modules = [];
}
if ( ! empty( $this->modules ) ) {
$this->load_module_dependencies();
}
$this->include_post_format_modules();
}
} else if ( Main::requires_architect_assets( $this->ID ) || Main::has_architect_content( $this->ID ) ) {
/* load all modules */
$this->modules = array_keys( static::get_module_data() );
if ( ! empty( $this->modules ) ) {
$this->load_module_dependencies();
}
} else {
$this->modules = [];
}
return $this->modules;
}
/**
* Each module can have other modules as dependencies, and through this function they are also added to the module list
*/
public function load_module_dependencies() {
/* load the dependencies for each module */
foreach ( $this->modules as $module ) {
$dependencies = static::get_module_data( $module, 'dependencies' );
foreach ( $dependencies as $dependency ) {
$this->add_module( $dependency );
}
}
}
/**
* If this post has an audio/video format, add that JS file to the modules that we're loading ( if it's not already included )
*/
public function include_post_format_modules() {
$post_format = get_post_format( $this->ID );
if ( ! empty( $post_format ) && in_array( $post_format, [ 'audio', 'video' ], true ) ) {
$this->add_module( $post_format );
}
}
/**
* @param $module_to_add
*/
public function add_module( $module_to_add ) {
if ( ! in_array( $module_to_add, $this->modules, true ) ) {
$this->modules[] = $module_to_add;
}
}
//todo add more modules, including 'general', which depends on buttons, content boxes, etc
public static function get_module_data( $module = '', $key = '' ) {
$data = [
'acf-dynamic-elements' => [
/**
* '.tcb-custom-field-source' is the general ACF class
* [data-tar-shortcode-attr] is for some frontend json parsing
* a[data-shortcode-id^="acf_"] is for checking ACF shortcode links
*/
'identifier' => '.tcb-custom-field-source,[data-tar-shortcode-attr],a[data-shortcode-id^="acf_"]',
],
'audio' => [
'identifier' => '.thrv_audio',
],
'carousel' => [
'identifier' => '[data-type="carousel"]',
'libraries' => [
'carousel-libs' => tve_editor_js( '/carousel-libs' . \TCB_Utils::get_js_suffix() ),
],
],
/* this is the old contact form element which is no longer visible in the sidebar, but still has frontend JS */
'contact-form-compat' => [
'identifier' => '.thrv-contact-form',
],
'content-reveal' => [
'identifier' => '.thrv_content_reveal',
],
'countdown' => [
'identifier' => '.tve-countdown',
],
'conditional-display' => [
'identifier' => '[data-display-group]',
],
'search-form' => [
'identifier' => '.thrv-search-form',
],
'dropdown' => [
'identifier' => '.tve_lg_dropdown, .tve_lg_state, .tcb-form-dropdown, .tve-dynamic-dropdown',
],
'country' => [
'identifier' => '.tve_lg_country',
],
'state' => [
'identifier' => '.tve_lg_state',
],
'datepicker' => [
'identifier' => '.lg-date-picker',
'libraries' => [
'date-picker' => tve_editor_js( '/date-picker' . \TCB_Utils::get_js_suffix() ),
],
],
'divider' => [
'identifier' => '.thrv-divider',
],
'file-upload' => [
'identifier' => '.tve_lg_file',
'libraries' => [
'moxie' => includes_url() . 'js/plupload/moxie.min.js',
'plupload' => includes_url() . 'js/plupload/plupload.min.js',
],
],
'avatar-picker' => [
'identifier' => '.tve-avatar-picker-element',
'dependencies' => [ 'google-api', 'facebook-api', 'modal' ],
'libraries' => [
'google-client' => 'https://accounts.google.com/gsi/client',
'google-api' => 'https://apis.google.com/js/api.js',
],
],
'fill-counter' => [
'identifier' => '.thrv-fill-counter',
],
'number-counter' => [
'identifier' => '.tve-number-counter',
],
'image-gallery' => [
'identifier' => '.tcb-image-gallery',
'libraries' => [
'image-gallery-libs' => tve_editor_js( '/image-gallery-libs.min.js' ),
],
'dependencies' => [ 'carousel' ],
],
'lead-generation' => [
'identifier' => '.thrv_lead_generation',
'dependencies' => [ 'dropdown' ],
],
'login' => [
'identifier' => '.thrv-login-element',
'dependencies' => [ 'lead-generation' ],
],
'menu' => [
'identifier' => '.thrv_widget_menu',
],
'number-counter-compat' => [
'identifier' => '.thrv_number_counter',
],
'post-grid-compat' => [
'identifier' => '.thrv_post_grid',
],
'pagination' => [
'identifier' => '.tcb-pagination',
'dependencies' => [ 'post-list' ],
],
'post-list' => [
'identifier' => '.tcb-post-list, .tva-course-list, .thrive-display-testimonials',
'dependencies' => [ 'post-grid-compat', 'dropdown' ],
],
'post-list-filter' => [
'identifier' => '.tcb-post-list-filter',
],
'pricing-table' => [
'identifier' => '.thrv-pricing-table',
],
'progress-bar' => [
'identifier' => '.tve-progress-bar-wrapper',
],
'social-share' => [
'identifier' => '.thrv_social_custom,.tve_social_custom',
],
'table' => [
'identifier' => '.thrv_table',
],
'tabs' => [
'identifier' => '.thrv_tabs_shortcode, .thrv-tabbed-content',
],
'timer' => [
'identifier' => '.thrv-countdown_timer_evergreen,.tve_countdown_timer_evergreen,.thrv-countdown_timer_plain,.thrv_countdown_timer',
],
'toc' => [
'identifier' => '.tve-toc, .thrv_contents_table', /* thrv_contents_table is the old TOC */
],
'toggle' => [
'identifier' => '.thrv_toggle, .thrv_toggle_shortcode',
],
'twitter' => [
'identifier' => '.thrv_tw_qs',
],
'user-profile' => [
'identifier' => '.tve-user-profile',
],
'video' => [
'identifier' => '.thrv_responsive_video, .tcb-responsive-video, .tcb-video-background-el',
],
];
if ( ! empty( $key ) ) {
$data = array_map( static function ( $item ) use ( $key ) {
return empty( $item[ $key ] ) ? [] : $item[ $key ];
}, $data );
}
if ( ! empty( $module ) ) {
$data = isset( $data[ $module ] ) ? $data[ $module ] : [];
}
return $data;
}
/**
* these libraries are enqueued from wordpress, so we don't have to do it ourselves; however, when printing inline, they still have to be printed
*/
const LIBRARIES_ENQUEUED_AUTOMATICALLY = [ 'moxie' ];
}

View File

@@ -0,0 +1,425 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class Main
*
* @package TCB\Lightspeed
*/
class Main {
const LIGHTSPEED_VERSION = 1;
const ENABLE_LIGHTSPEED_OPTION = '_tve_enable_lightspeed';
const OPTIMIZATION_VERSION_META = '_tve_lightspeed_version';
const ASSETS_TO_PRELOAD = '_tve_assets_to_preload';
const OPTIMIZE_FLAG = 'tcb-lightspeed-optimize';
const ADVANCED_OPTIMIZE_FLAG = 'tcb-advanced-optimize';
public static function init() {
static::includes();
Hooks::add_actions();
Compat::init();
}
public static function includes() {
require_once __DIR__ . '/class-dashboard.php';
require_once __DIR__ . '/class-rest-api.php';
require_once __DIR__ . '/class-compat.php';
require_once __DIR__ . '/class-hooks.php';
require_once __DIR__ . '/class-fonts.php';
require_once __DIR__ . '/class-css.php';
require_once __DIR__ . '/class-js.php';
require_once __DIR__ . '/class-js-module.php';
require_once __DIR__ . '/class-gutenberg.php';
require_once __DIR__ . '/class-woocommerce.php';
require_once __DIR__ . '/class-emoji.php';
}
public static function is_enabled() {
return isset( $_GET['force-lightspeed'] ) || ! empty( get_option( static::ENABLE_LIGHTSPEED_OPTION, 0 ) );
}
public static function is_optimizing() {
return isset( $_GET[ static::OPTIMIZE_FLAG ] );
}
public static function is_advanced_optimizing() {
return isset( $_GET[ static::ADVANCED_OPTIMIZE_FLAG ] );
}
/**
* Check if we can and should optimize assets for the current post
*
* @param int $post_id
* @param bool $editor_check
*
* @return bool
*/
public static function has_optimized_assets( $post_id, $editor_check = true, $check_if_enabled = true ) {
$optimized_version = (int) get_post_meta( $post_id, static::OPTIMIZATION_VERSION_META, true );
$is_optimized = ! empty( $optimized_version );
if ( $check_if_enabled ) {
$is_optimized = $is_optimized && static::is_enabled();
}
if ( $editor_check ) {
/* we don't optimize inside the editor, because we load everything there */
$is_optimized = $is_optimized && ! is_editor_page_raw( true );
}
/**
* Decide if we're going to optimize the css for the current post
*
* @param $is_optimized bool boolean value on what should we do for the current post
* @param $post_id int current post id
*
* @return bool
*/
return apply_filters( 'tcb_lightspeed_has_optimized_assets', $is_optimized, $post_id );
}
/**
* Check to see if the current post needs optimization or not
*
* @param $post_id
*
* @return mixed|void
*/
public static function requires_architect_assets( $post_id ) {
$is_lp = tve_post_is_landing_page( $post_id );
$meta_key = $is_lp ? "tve_custom_css_$is_lp" : 'tve_custom_css';
$has_architect_css = ! empty( get_post_meta( $post_id, $meta_key, true ) );
$is_editor = is_editor_page_raw( true );
/**
* Filter if the current post requires optimization or not. By default we check to see if the user has architect styles saved
*
* @param boolean $requires
* @param int $post_id
*
* @return boolean
*/
return apply_filters( 'tcb_lightspeed_requires_architect_assets', $has_architect_css || $is_editor, $post_id );
}
/**
* @param $post_id
*
* @return bool
*/
public static function has_architect_content( $post_id ) {
return ! empty( tve_get_post_meta( $post_id, 'tve_updated_post' ) );
}
/**
* Return upload dir path/url depending on key
*
* @param string $key
*
* @return string
*/
public static function upload_dir( $key = 'url' ) {
$upload_dir = wp_upload_dir();
$styles_dir = $upload_dir[ $key ] . '/thrive/';
if ( ! is_dir( $styles_dir ) ) {
wp_mkdir_p( $styles_dir );
}
return preg_replace( '/https?:/', '', $styles_dir );
}
/**
* @param $post_id
* @param $assets_to_preload
*/
public static function save_assets_to_preload( $post_id, $assets_to_preload ) {
update_post_meta( $post_id, static::ASSETS_TO_PRELOAD, $assets_to_preload );
}
/**
* Handles all the saved data used for optimize
*
* @param $post_id
* @param $data
* @param string $key
*/
public static function handle_optimize_saves( $post_id, $data, $key = '' ) {
static::save_assets_to_preload( $post_id, isset( $data['assets_to_preload'] ) ? $data['assets_to_preload'] : '' );
Css::get_instance( $post_id )->save_optimized_css( 'base' . $key, isset( $data['optimized_styles'] ) ? $data['optimized_styles'] : '' );
Js::get_instance( $post_id, $key )->save_js_modules( isset( $data['js_modules'] ) ? $data['js_modules'] : [] );
static::optimized_advanced_assets( $post_id, $data, $key );
}
/**
* Handles the save of advanced assets, for now Woocommerce and Gutenberg
*
* @param $post_id
* @param $data
*/
public static function optimized_advanced_assets( $post_id, $data, $key = '' ) {
if ( \TCB\Integrations\WooCommerce\Main::active() ) {
Js::get_instance( $post_id, '_woo' . $key )->save_js_modules( isset( $data['woo_modules'] ) ? $data['woo_modules'] : [] );
}
update_post_meta( $post_id, Gutenberg::HAS_GUTENBERG, isset( $data['gutenberg_modules'] ) ? $data['gutenberg_modules'] : [] );
}
/**
* @param $id
*/
public static function preload_assets( $id ) {
if ( is_editor_page_raw( true ) ) {
return;
}
$assets_to_preload = get_post_meta( $id, static::ASSETS_TO_PRELOAD, true );
if ( ! empty( $assets_to_preload ) ) {
foreach ( $assets_to_preload as $asset ) {
switch ( $asset['type'] ) {
case 'image':
echo static::get_preload_link( $asset['url'], 'image' );
break;
case 'dynamic':
if ( $asset['url'] === 'featured' && has_post_thumbnail() ) {
echo static::get_preload_link( \TCB_Post_List_Shortcodes::shortcode_function_content( 'the_post_thumbnail_url' ), 'image' );
}
break;
default:
break;
}
}
}
do_action( 'tve_lightspeed_preload_assets', $id );
}
/**
* Function called to make sure the lightspeed optimization is active when a plugin is activated
*/
public static function first_time_enable_lightspeed() {
$lightspeed = get_option( '_tve_enable_lightspeed' );
if ( $lightspeed === false ) {
update_option( '_tve_enable_lightspeed', 1 );
}
}
/**
* @param $url
* @param $type
*
* @return string
*/
public static function get_preload_link( $url, $type ) {
return '<link rel="preload" as="' . $type . '" href="' . $url . '">';
}
/**
* Get the excluded post types
*
* @return mixed|void
*/
public static function get_excluded_post_types() {
/**
* Filter posts we don't want to optimize
*
* @param array $post_types
*
* @return array
*/
return apply_filters( 'tcb_lightspeed_excluded_post_types', [
'attachment',
\TVD\Login_Editor\Post_Type::NAME,
'product_variation',
//TODO: this is just temporary
'tva_module',
'tva_lesson',
'tvo_capture',
'tvo_display',
'tcb_content_template',
'shortcodesultimate', /* 'Shortcodes Ultimate' plugin */
'tm_global_cp', /* 'Extra Checkout Options for WooCommerce TM Extra Product Options' plugin */
] );
}
/**
* Return all posts that have architect content saved
*
* @return array
*/
public static function get_architect_posts_for_optimization() {
$excluded_post_types = static::get_excluded_post_types();
$all_post_types = array_filter( array_values( get_post_types() ), static function ( $post_type ) use ( $excluded_post_types ) {
return ! in_array( $post_type, $excluded_post_types, true );
} );
$posts = get_posts( [
'posts_per_page' => - 1,
'post_type' => $all_post_types,
'fields' => 'ids',
/* exclude blog page */
'post__not_in' => [ get_option( 'page_for_posts' ) ],
'update_post_meta_cache' => false,
'meta_query' => [
'relation' => 'OR',
[
'key' => 'tve_custom_css',
'compare' => 'EXISTS',
],
[
'key' => 'tve_landing_page',
'compare' => 'EXISTS',
],
],
] );
$groups = [];
foreach ( $posts as $post_id ) {
$post = get_post( $post_id );
if ( empty( $groups[ $post->post_type ] ) ) {
$groups[ $post->post_type ] = static::prepare_post_type( $post->post_type );
}
$groups[ $post->post_type ]['items'][] = [
'id' => $post->ID,
'name' => $post->post_title,
'optimized' => (int) $post->{static::OPTIMIZATION_VERSION_META} === static::LIGHTSPEED_VERSION ? 1 : 0,
'url' => get_permalink( $post->ID ),
];
}
return $groups;
}
/**
* This content includes everything but LPs
*
* @return array|void
*/
public static function get_content_for_optimization( $request ) {
$excluded_post_types = static::get_excluded_post_types();
$excluded_post_types[] = 'tve_landing_page';
$all_post_types = array_filter( array_values( get_post_types() ), static function ( $post_type ) use ( $excluded_post_types ) {
return ! in_array( $post_type, $excluded_post_types, true );
} );
$posts = get_posts( [
'posts_per_page' => - 1,
'post_type' => $all_post_types,
'fields' => 'ids',
/* exclude blog page */
'post__not_in' => [ get_option( 'page_for_posts' ) ],
'update_post_meta_cache' => false,
'meta_query' => [
[
'key' => 'tve_custom_css',
'compare' => 'EXISTS',
],
],
] );
$groups = static::get_advanced_groups( $posts );
return array_merge( $groups, apply_filters( 'tve_lightspeed_items_to_optimize', $groups, $request ) );
}
public static function get_lp_for_optimize() {
$posts = get_posts( [
'posts_per_page' => - 1,
'post_type' => 'page',
'fields' => 'ids',
/* exclude blog page */
'post__not_in' => [ get_option( 'page_for_posts' ) ],
'update_post_meta_cache' => false,
'meta_query' => [
[
'key' => 'tve_landing_page',
'compare' => 'EXISTS',
],
],
] );
return static::get_advanced_groups( $posts );
}
/**
* Get the advanced(that have woo or gutenberg) groups to be optimized
*
* @param $posts
*
* @return array
*/
public static function get_advanced_groups( $posts ) {
$groups = [];
foreach ( $posts as $post_id ) {
$post = get_post( $post_id );
if ( empty( $groups[ $post->post_type ] ) ) {
$groups[ $post->post_type ] = static::prepare_post_type( $post->post_type );
}
$item = [
'id' => $post->ID,
'name' => $post->post_title,
'gutenberg_optimized' => metadata_exists( 'post', $post->ID, Gutenberg::HAS_GUTENBERG ),
'url' => get_permalink( $post->ID ),
];
if ( \TCB\Integrations\WooCommerce\Main::active() ) {
$item['woo_optimized'] = metadata_exists( 'post', $post->ID, Woocommerce::WOO_MODULE_META_NAME );
}
$groups[ $post->post_type ]['items'][] = $item;
}
return $groups;
}
public static function prepare_post_type( $post_type ) {
$post_type_object = get_post_type_object( $post_type );
return [
'type' => $post_type,
'label' => $post_type_object === null ? $post_type : $post_type_object->label,
'items' => [],
];
}
}

View File

@@ -0,0 +1,175 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-theme
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Rest_Api
*
* @package TCB\Lightspeed
*/
class Rest_Api {
const REST_NAMESPACE = 'tcb/v1';
const REST_ROUTE = 'lightspeed/';
public static function register_routes() {
register_rest_route( static::REST_NAMESPACE, static::REST_ROUTE . 'analyze', [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ __CLASS__, 'analyze' ],
'permission_callback' => [ __CLASS__, 'has_admin_access' ],
'args' => [
'to_analyze' => [
'type' => 'array',
'required' => false,
],
],
],
] );
register_rest_route( static::REST_NAMESPACE, static::REST_ROUTE . 'options', [
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ __CLASS__, 'options' ],
'permission_callback' => [ __CLASS__, 'has_admin_access' ],
'args' => [
'key' => [
'type' => 'string',
'required' => true,
'validate_callback' => static function ( $key ) {
return in_array( $key, [
Main::ENABLE_LIGHTSPEED_OPTION,
Fonts::ENABLE_ASYNC_FONTS_LOAD,
Fonts::ENABLE_FONTS_OPTIMIZATION,
Fonts::DISABLE_GOOGLE_FONTS,
Gutenberg::DISABLE_GUTENBERG,
Gutenberg::DISABLE_GUTENBERG_LP,
Woocommerce::DISABLE_WOOCOMMERCE,
Woocommerce::DISABLE_WOOCOMMERCE_LP,
Emoji::DISABLE_EMOJI,
], true );
},
],
'value' => [
'type' => 'int',
'required' => true,
'validate_callback' => static function ( $value ) {
return is_numeric( $value );
},
],
],
],
] );
register_rest_route( static::REST_NAMESPACE, static::REST_ROUTE . 'optimize', [
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ __CLASS__, 'optimize' ],
'permission_callback' => [ __CLASS__, 'can_edit_posts' ],
],
] );
}
/**
* Analyze posts/pages/templates ... to see what is optimized and what not
*
* @return \WP_REST_Response
*/
public static function analyze( $request ) {
/* make sure there's enough memory to analyze lots of content */
@ini_set( 'memory_limit', TVE_EXTENDED_MEMORY_LIMIT );
$groups = [];
$to_analyze = $request->get_param( 'to_analyze' );
if ( ! empty( $to_analyze ) ) {
if ( in_array( 'lp', $to_analyze ) ) {
$groups = Main::get_lp_for_optimize();
}
if ( in_array( 'ttb', $to_analyze ) ) {
$groups = Main::get_content_for_optimization( $request );
}
} else {
/**
* returns an array of items to be optimized
*
* @return array
*/
$groups = Main::get_architect_posts_for_optimization();
}
$groups = apply_filters( 'tve_lightspeed_items_to_optimize', $groups, $request );
return new \WP_REST_Response( $groups );
}
/**
* Enable/disable lightspeed
*
* @param \WP_REST_Request $request data about the request.
*
* @return \WP_REST_Response
*/
public static function options( $request ) {
$key = $request->get_param( 'key' );
$value = (int) $request->get_param( 'value' );
update_option( $key, $value );
return new \WP_REST_Response( $value );
}
/**
* Optimize assets
*
* @param \WP_REST_Request $request data about the request.
*
* @return \WP_REST_Response
*/
public static function optimize( $request ) {
$post_id = (int) $request->get_param( 'id' );
$key = empty( $request->get_param( 'key' ) ) ? '' : '_' . $request->get_param( 'key' );
Main::handle_optimize_saves( $post_id, $request, $key );
/**
* Action called after an item has been optimized
*
* @param int $post_id
* @param int|string $key
* @param \WP_REST_Request $request
*/
do_action( 'tcb_lightspeed_item_optimized', $post_id, $request->get_param( 'key' ), $request );
return new \WP_REST_Response( [ 'success' => 1 ] );
}
/**
* Allow only admins to access rest routes
*
* @return bool
*/
public static function has_admin_access() {
return current_user_can( 'manage_options' );
}
/**
* Allow only users that can edit posts on this route
*
* @param \WP_REST_Request $request data about the request.
*
* @return bool
*/
public static function can_edit_posts( $request ) {
return current_user_can( 'edit_post', $request->get_param( 'id' ) );
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\Lightspeed;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class Woocommerce
*
* @package TCB\Lightspeed
*/
class Woocommerce {
const DISABLE_WOOCOMMERCE = '_tve_disable_woocommerce';
const DISABLE_WOOCOMMERCE_LP = '_tve_disable_woocommerce_lp';
const WOO_MODULE_META_NAME = '_tve_js_modules_woo';
public static function get_woocommerce_assets( $module = '', $key = '' ) {
$data = [
'woo' => [
'identifier' => '.woocommerce, .products, .product, .tcb-woo-mini-cart, .thrive-shop, [data-block-name^="woocommerce"]',
],
'woo-cart' => [
'identifier' => '.add_to_cart_button, .woocommerce-cart, .tcb-woo-mini-cart',
],
];
if ( ! empty( $key ) ) {
$data = array_map( static function ( $item ) use ( $key ) {
return empty( $item[ $key ] ) ? [] : $item[ $key ];
}, $data );
}
return empty( $module ) ? $data : $data[ $module ];
}
public static function is_woocommerce_disabled( $is_lp = false ) {
return ! empty( get_option( $is_lp ? static::DISABLE_WOOCOMMERCE_LP : static::DISABLE_WOOCOMMERCE, 0 ) );
}
public static function get_woo_js_modules() {
return [
'tve_woo' => tve_editor_js( '/woo' . \TCB_Utils::get_js_suffix() ),
'selectWoo' => static::get_modules_urls( 'assets/js/selectWoo/selectWoo.full.js' ),
'woocommerce' => static::get_modules_urls( 'assets/js/frontend/woocommerce.js' ),
'cart-fragments' => static::get_modules_urls( 'assets/js/frontend/cart-fragments.js' ),
'add-to-cart' => static::get_modules_urls( 'assets/js/frontend/add-to-cart.js' ),
];
}
public static function get_woo_styles() {
return apply_filters( 'tcb_lightspeed_woo_scripts', [
'woocommerce-layout' => static::get_modules_urls( 'assets/css/woocommerce-layout.css?ver=5.7.1' ),
'woocommerce-smallscreen' => static::get_modules_urls( 'assets/css/woocommerce-smallscreen.css?ver=5.7.1' ),
'woocommerce-general' => static::get_modules_urls( 'assets/css/woocommerce.css?ver=5.7.1' ),
] );
}
public static function get_modules_urls( $path ) {
return plugins_url( $path, WC_PLUGIN_FILE );
}
public static function get_modules( $post_id, $key = '' ) {
return get_post_meta( $post_id, static::WOO_MODULE_META_NAME . $key, true );
}
}