From 16016d72fd5ac39d2e74bced093321f3b90f4d82 Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Tue, 27 Jan 2026 12:27:19 +0100 Subject: [PATCH 1/4] Handle elementor background image replacement --- php/class-plugin.php | 4 +- php/integrations/class-elementor.php | 106 +++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 php/integrations/class-elementor.php diff --git a/php/class-plugin.php b/php/class-plugin.php index 2a775b2d..8fc84875 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -14,6 +14,7 @@ use Cloudinary\Delivery\Lazy_Load; use Cloudinary\Delivery\Responsive_Breakpoints; use Cloudinary\Assets as CLD_Assets; +use Cloudinary\Integrations\Elementor; use Cloudinary\Integrations\WPML; use Cloudinary\Media\Gallery; use Cloudinary\Sync\Storage; @@ -31,7 +32,7 @@ final class Plugin { * * @since 0.1 * - * @var Admin|CLD_Assets|Connect|Dashboard|Deactivation|Delivery|Extensions|Gallery|Lazy_Load|Media|Meta_Box|Relate|Report|Responsive_Breakpoints|REST_API|State|Storage|SVG|Sync|URL[]|WPML|null + * @var Admin|CLD_Assets|Connect|Dashboard|Deactivation|Delivery|Extensions|Gallery|Lazy_Load|Media|Meta_Box|Relate|Report|Responsive_Breakpoints|REST_API|State|Storage|SVG|Sync|URL[]|WPML|Elementor|null */ public $components; /** @@ -136,6 +137,7 @@ public function plugins_loaded() { $this->components['metabox'] = new Meta_Box( $this ); $this->components['url'] = new URL( $this ); $this->components['wpml'] = new WPML( $this ); + $this->components['elementor'] = new Elementor( $this ); $this->components['special_offer'] = new Special_Offer( $this ); } diff --git a/php/integrations/class-elementor.php b/php/integrations/class-elementor.php new file mode 100644 index 00000000..534a043c --- /dev/null +++ b/php/integrations/class-elementor.php @@ -0,0 +1,106 @@ +plugin->get_component( 'media' ); + $settings = $element->get_settings_for_display(); + + // Define all background image related keys and their specificities. + $background_keys = array( + '_background_image' => array( + 'device' => 'desktop', + 'suffix' => '', + ), + '_background_hover_image' => array( + 'device' => 'desktop', + 'suffix' => ':hover', + ), + '_background_image_tablet' => array( + 'device' => 'tablet', + 'suffix' => '', + ), + '_background_hover_image_tablet' => array( + 'device' => 'tablet', + 'suffix' => ':hover', + ), + '_background_image_mobile' => array( + 'device' => 'mobile', + 'suffix' => '', + ), + '_background_hover_image_mobile' => array( + 'device' => 'mobile', + 'suffix' => ':hover', + ), + ); + + foreach ( $background_keys as $background_key => $background_data ) { + if ( ! isset( $settings[ $background_key ]['url'], $settings[ $background_key ]['id'] ) ) { + continue; + } + + $original_url = $settings[ $background_key ]['url']; + $media_id = $settings[ $background_key ]['id']; + $media_size = isset( $settings[ $background_key ]['size'] ) ? $settings[ $background_key ]['size'] : array(); + + $cloudinary_url = $media->cloudinary_url( $media_id, $media_size ); + + // Skip if the URL is unchanged or the cloudinary URL cannot be generated somehow. + if ( ! $cloudinary_url || $cloudinary_url === $original_url ) { + continue; + } + + // Build the CSS selector and rule. + $css_selector = $post_css->get_element_unique_selector( $element ) . $background_data['suffix']; + $css_rule = array( 'background-image' => "url('$cloudinary_url')" ); + + // Retrieve the specific media query rule for non-desktop devices. + $media_query = null; + $device = $background_data['device']; // either 'desktop', 'tablet' or 'mobile'. + if ( 'desktop' !== $device ) { + $media_query = array( 'max' => $device ); + } + + $post_css->get_stylesheet()->add_rules( $css_selector, $css_rule, $media_query ); + } + } +} From 8239f2ac966d7755bd467e3a98415645a31625d3 Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Tue, 27 Jan 2026 12:49:43 +0100 Subject: [PATCH 2/4] Handle more edge cases --- php/integrations/class-elementor.php | 81 ++++++++++++++++------------ 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/php/integrations/class-elementor.php b/php/integrations/class-elementor.php index 534a043c..6c5967c5 100644 --- a/php/integrations/class-elementor.php +++ b/php/integrations/class-elementor.php @@ -16,6 +16,38 @@ */ class Elementor extends Integrations { + /** + * List of Elementor background image settings keys, along with their device and CSS suffix. + * + * @var array + */ + const ELEMENTOR_BACKGROUND_IMAGES = array( + '_background_image' => array( + 'device' => 'desktop', + 'suffix' => '', + ), + '_background_hover_image' => array( + 'device' => 'desktop', + 'suffix' => ':hover', + ), + '_background_image_tablet' => array( + 'device' => 'tablet', + 'suffix' => '', + ), + '_background_hover_image_tablet' => array( + 'device' => 'tablet', + 'suffix' => ':hover', + ), + '_background_image_mobile' => array( + 'device' => 'mobile', + 'suffix' => '', + ), + '_background_hover_image_mobile' => array( + 'device' => 'mobile', + 'suffix' => ':hover', + ), + ); + /** * Check if the integration can be enabled. * @@ -42,38 +74,16 @@ public function register_hooks() { * @return void */ public function replace_bg_images_in_css( $post_css, $element ) { - $media = $this->plugin->get_component( 'media' ); - $settings = $element->get_settings_for_display(); + $settings = $element->get_settings_for_display(); + $media = $this->plugin->get_component( 'media' ); + $delivery = $this->plugin->get_component( 'delivery' ); - // Define all background image related keys and their specificities. - $background_keys = array( - '_background_image' => array( - 'device' => 'desktop', - 'suffix' => '', - ), - '_background_hover_image' => array( - 'device' => 'desktop', - 'suffix' => ':hover', - ), - '_background_image_tablet' => array( - 'device' => 'tablet', - 'suffix' => '', - ), - '_background_hover_image_tablet' => array( - 'device' => 'tablet', - 'suffix' => ':hover', - ), - '_background_image_mobile' => array( - 'device' => 'mobile', - 'suffix' => '', - ), - '_background_hover_image_mobile' => array( - 'device' => 'mobile', - 'suffix' => ':hover', - ), - ); + if ( ! $media || ! $delivery ) { + return; + } - foreach ( $background_keys as $background_key => $background_data ) { + foreach ( self::ELEMENTOR_BACKGROUND_IMAGES as $background_key => $background_data ) { + // We need to have both URL and ID from the image to proceed. if ( ! isset( $settings[ $background_key ]['url'], $settings[ $background_key ]['id'] ) ) { continue; } @@ -82,13 +92,16 @@ public function replace_bg_images_in_css( $post_css, $element ) { $media_id = $settings[ $background_key ]['id']; $media_size = isset( $settings[ $background_key ]['size'] ) ? $settings[ $background_key ]['size'] : array(); - $cloudinary_url = $media->cloudinary_url( $media_id, $media_size ); - - // Skip if the URL is unchanged or the cloudinary URL cannot be generated somehow. - if ( ! $cloudinary_url || $cloudinary_url === $original_url ) { + // Skip if the media is not deliverable via Cloudinary. + if ( ! $delivery->is_deliverable( $media_id ) ) { continue; } + // If the original URL is already a Cloudinary URL, use it directly; otherwise, generate the Cloudinary URL. + $cloudinary_url = $media->is_cloudinary_url( $original_url ) + ? $original_url + : $media->cloudinary_url( $media_id, $media_size ); + // Build the CSS selector and rule. $css_selector = $post_css->get_element_unique_selector( $element ) . $background_data['suffix']; $css_rule = array( 'background-image' => "url('$cloudinary_url')" ); From e8eae5ec7f06bf1988028651747d30f6c6592ee8 Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Tue, 27 Jan 2026 13:08:19 +0100 Subject: [PATCH 3/4] Handle cache flushing for Elementor when cloudinary settings are changed --- php/integrations/class-elementor.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/php/integrations/class-elementor.php b/php/integrations/class-elementor.php index 6c5967c5..d50fd609 100644 --- a/php/integrations/class-elementor.php +++ b/php/integrations/class-elementor.php @@ -64,6 +64,7 @@ public function can_enable() { */ public function register_hooks() { add_action( 'elementor/element/parse_css', array( $this, 'replace_bg_images_in_css' ), 10, 2 ); + add_action( 'cloudinary_flush_cache', array( $this, 'clear_elementor_css_cache' ) ); } /** @@ -116,4 +117,17 @@ public function replace_bg_images_in_css( $post_css, $element ) { $post_css->get_stylesheet()->add_rules( $css_selector, $css_rule, $media_query ); } } + + /** + * Clear Elementor CSS cache. + * This is called when Cloudinary cache is flushed, so that any change in media URLs is reflected in Elementor CSS files. + * + * @return void + */ + public function clear_elementor_css_cache() { + if ( class_exists( 'Elementor\Plugin' ) ) { + $elementor = Plugin::instance(); + $elementor->files_manager->clear_cache(); + } + } } From 959f7a433b463b50581ccd933fa7e7fe453e8ae8 Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Tue, 27 Jan 2026 14:02:28 +0100 Subject: [PATCH 4/4] Generate URL even if Elementor one seems to come from Cloudinary The one coming from Elementor doesn't contain all the options from the settings and therefore isn't accurate enough --- php/integrations/class-elementor.php | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/php/integrations/class-elementor.php b/php/integrations/class-elementor.php index d50fd609..01e1fe31 100644 --- a/php/integrations/class-elementor.php +++ b/php/integrations/class-elementor.php @@ -63,7 +63,7 @@ public function can_enable() { * @return void */ public function register_hooks() { - add_action( 'elementor/element/parse_css', array( $this, 'replace_bg_images_in_css' ), 10, 2 ); + add_action( 'elementor/element/parse_css', array( $this, 'replace_background_images_in_css' ), 10, 2 ); add_action( 'cloudinary_flush_cache', array( $this, 'clear_elementor_css_cache' ) ); } @@ -74,7 +74,7 @@ public function register_hooks() { * @param Element_Base $element The Elementor element. * @return void */ - public function replace_bg_images_in_css( $post_css, $element ) { + public function replace_background_images_in_css( $post_css, $element ) { $settings = $element->get_settings_for_display(); $media = $this->plugin->get_component( 'media' ); $delivery = $this->plugin->get_component( 'delivery' ); @@ -84,24 +84,21 @@ public function replace_bg_images_in_css( $post_css, $element ) { } foreach ( self::ELEMENTOR_BACKGROUND_IMAGES as $background_key => $background_data ) { - // We need to have both URL and ID from the image to proceed. - if ( ! isset( $settings[ $background_key ]['url'], $settings[ $background_key ]['id'] ) ) { + // We need to have the ID from the image to proceed. + if ( ! isset( $settings[ $background_key ]['id'] ) ) { continue; } - $original_url = $settings[ $background_key ]['url']; - $media_id = $settings[ $background_key ]['id']; - $media_size = isset( $settings[ $background_key ]['size'] ) ? $settings[ $background_key ]['size'] : array(); + $media_id = $settings[ $background_key ]['id']; + $media_size = isset( $settings[ $background_key ]['size'] ) ? $settings[ $background_key ]['size'] : array(); // Skip if the media is not deliverable via Cloudinary. if ( ! $delivery->is_deliverable( $media_id ) ) { continue; } - // If the original URL is already a Cloudinary URL, use it directly; otherwise, generate the Cloudinary URL. - $cloudinary_url = $media->is_cloudinary_url( $original_url ) - ? $original_url - : $media->cloudinary_url( $media_id, $media_size ); + // Generate the Cloudinary URL. + $cloudinary_url = $media->cloudinary_url( $media_id, $media_size ); // Build the CSS selector and rule. $css_selector = $post_css->get_element_unique_selector( $element ) . $background_data['suffix']; @@ -109,11 +106,11 @@ public function replace_bg_images_in_css( $post_css, $element ) { // Retrieve the specific media query rule for non-desktop devices. $media_query = null; - $device = $background_data['device']; // either 'desktop', 'tablet' or 'mobile'. - if ( 'desktop' !== $device ) { - $media_query = array( 'max' => $device ); + if ( 'desktop' !== $background_data['device'] ) { + $media_query = array( 'max' => $background_data['device'] ); } + // Override the CSS rule in Elementor. $post_css->get_stylesheet()->add_rules( $css_selector, $css_rule, $media_query ); } }