Files

243 lines
7.6 KiB
PHP
Raw Permalink Normal View History

<?php
namespace DeliciousBrains\WP_Offload_Media\Pro\Integrations;
use DeliciousBrains\WP_Offload_Media\Integrations\Integration;
use Elementor\Core\Files\CSS\Post;
use Elementor\Element_Base;
use Elementor\Plugin;
use AS3CF_Utils;
class Elementor extends Integration {
/**
* Keep track update_metadata recursion level
*
* @var int
*/
private $recursion_level = 0;
/**
* Is Elementor installed?
*
* @return bool
*/
public static function is_installed(): bool {
if ( defined( 'ELEMENTOR_VERSION' ) ) {
return true;
}
return false;
}
/**
* Init integration.
*/
public function init() {
// Nothing to do.
}
/**
* @inheritDoc
*/
public function setup() {
add_filter( 'elementor/editor/localize_settings', array( $this, 'localize_settings' ) );
add_action( 'elementor/frontend/before_render', array( $this, 'frontend_before_render' ) );
add_filter( 'update_post_metadata', array( $this, 'update_post_metadata' ), 10, 5 );
add_action( 'wp_print_styles', array( $this, 'wp_print_styles' ) );
if ( isset( $_REQUEST['action'] ) && 'elementor_ajax' === $_REQUEST['action'] ) {
add_filter( 'widget_update_callback', array( $this, 'widget_update_callback' ), 10, 2 );
}
// Hooks to clear Elementor cache after OME bulk actions
add_action( 'as3cf_copy_buckets_cancelled', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_copy_buckets_completed', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_download_and_remover_cancelled', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_download_and_remover_completed', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_move_objects_cancelled', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_move_objects_completed', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_move_private_objects_cancelled', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_move_private_objects_completed', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_move_public_objects_cancelled', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_move_public_objects_completed', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_uploader_cancelled', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_uploader_completed', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_elementor_analyze_and_repair_cancelled', array( $this, 'clear_elementor_css_cache' ) );
add_action( 'as3cf_elementor_analyze_and_repair_completed', array( $this, 'clear_elementor_css_cache' ) );
}
/**
* Rewrite media library URLs from local to remote when settings are read from
* database.
*
* @param object $config
*
* @return object
*
* @handles elementor/editor/localize_settings
*/
public function localize_settings( $config ) {
if ( ! is_array( $config ) || ! isset( $config['initial_document'] ) ) {
return $config;
}
if ( ! is_array( $config['initial_document'] ) || ! isset( $config['initial_document']['elements'] ) ) {
return $config;
}
$filtered = json_decode(
$this->as3cf->filter_local->filter_post( json_encode( $config['initial_document']['elements'], JSON_UNESCAPED_SLASHES ) ),
true
);
// Avoid replacing content if the filtering failed
if ( false !== $filtered ) {
$config['initial_document']['elements'] = $filtered;
}
return $config;
}
/**
* Replace local URLs in settings that Elementor renders in HTML for some attributes, i.e json structs for
* the section background slideshow
*
* @param Element_Base $element
*
* @handles elementor/frontend/before_render
*/
public function frontend_before_render( $element ) {
$element->set_settings(
json_decode(
$this->as3cf->filter_local->filter_post( json_encode( $element->get_settings(), JSON_UNESCAPED_SLASHES ) ),
true
)
);
}
/**
* Handle Elementor's call to update_metadata() for _elementor_data when saving
* a post or page. Rewrites remote URLs to local.
*
* @param bool $check
* @param int $object_id
* @param string $meta_key
* @param mixed $meta_value
* @param mixed $prev_value
*
* @handles update_post_metadata
*
* @return bool
*/
public function update_post_metadata( $check, $object_id, $meta_key, $meta_value, $prev_value ) {
if ( '_elementor_css' === $meta_key ) {
$this->rewrite_css( $object_id, $meta_value );
return $check;
}
if ( '_elementor_data' !== $meta_key ) {
return $check;
}
// We expect the meta value to be a JSON formatted string from Elementor. Exit early if it's not.
if ( ! is_string( $meta_value ) || ! AS3CF_Utils::is_json( $meta_value ) ) {
return $check;
}
// We're calling update_metadata recursively and need to make sure
// we never nest deeper than one level.
if ( 0 === $this->recursion_level ) {
$this->recursion_level++;
// We get here from an update_metadata() call that has already done some string sanitizing
// including wp_unslash(), but the original json from Elementor still needs slashes
// removed for our filters to work.
// Note: wp_unslash can't be used because it also unescapes any embedded HTML.
$json = str_replace( '\/', '/', $meta_value );
$json = $this->as3cf->filter_provider->filter_post( $json );
$meta_value = wp_slash( str_replace( '/', '\/', $json ) );
update_metadata( 'post', $object_id, '_elementor_data', $meta_value, $prev_value );
// Reset recursion level and let update_metadata we already handled saving the meta
$this->recursion_level = 0;
return true;
}
return $check;
}
/**
* Rewrites local URLs in generated CSS files
*
* @param int $object_id
* @param array $meta_value
*/
private function rewrite_css( $object_id, $meta_value ) {
if ( 'file' === $meta_value['status'] ) {
$elementor_css = new Post( $object_id );
$file = Post::get_base_uploads_dir() . Post::DEFAULT_FILES_DIR . $elementor_css->get_file_name();
if ( file_exists( $file ) ) {
$old_content = file_get_contents( $file );
if ( ! empty( $old_content ) ) {
file_put_contents(
$file,
$this->as3cf->filter_local->filter_post( $old_content )
);
}
}
}
}
/**
* Some widgets, specifically any standard WordPress widgets, make ajax requests back to the server
* before the edited section gets rendered in the Elementor editor. When they do, Elementor picks up
* properties directly from the saved post meta.
*
* @param array $instance
* @param array $new_instance
*
* @handles widget_update_callback
*
* @return mixed
*/
public function widget_update_callback( $instance, $new_instance ) {
return json_decode(
$this->as3cf->filter_local->filter_post( json_encode( $instance, JSON_UNESCAPED_SLASHES ) ),
true
);
}
/**
* Clears the Elementor cache
*
* @handles Multiple as3cf_*_completed and as3cf_*_cancelled actions
*/
public function clear_elementor_css_cache() {
if ( class_exists( '\Elementor\Plugin' ) ) {
Plugin::instance()->files_manager->clear_cache();
}
}
/**
* Rewrite URLs in Elementor frontend inline CSS before it's rendered/printed by WordPress
*
* @implements wp_print_styles
*/
public function wp_print_styles() {
$wp_styles = wp_styles();
if ( empty( $wp_styles->registered['elementor-frontend']->extra['after'] ) ) {
return;
}
foreach ( $wp_styles->registered['elementor-frontend']->extra['after'] as &$extra_css ) {
$filtered_css = $this->as3cf->filter_local->filter_post( $extra_css );
if ( ! empty( $filtered_css ) ) {
$extra_css = $filtered_css;
}
}
}
}