9 87.1594C87.6772 89.1315 95.2572 85.9895 102.387 83.4258C109.572 80.8472 116.941 78.8043 124.436 77.3248C129.006 76.423 133.858 75.6341 137.429 72.6331C137.711 72.3937 138.006 72.0835 137.992 71.7175C137.978 71.4073 137.739 71.1541 137.514 70.9424C134.195 67.885 129.47 66.6172 126.391 63.306C126.053 62.94 125.716 62.4607 125.843 61.9819C125.941 61.6015 126.306 61.3477 126.644 61.1365C130.005 59.009 133.352 56.8816 136.712 54.7541C132.41 50.1187 128.036 45.5397 123.593 41.0453C122.242 39.6786 119.781 38.5796 118.165 39.6223Z" fill="#EE686B"/> '; $output .= '' . esc_html( $string ) . ''; $output .= ''; echo $output; } /** * Check if we should rewrite the urls. * * @return bool If we can replace the image. */ public function should_replace() { if ( apply_filters( 'optml_should_replace_page', false ) ) { return false; } if ( apply_filters( 'optml_force_replacement', false ) === true ) { return true; } if ( is_customize_preview() && $this->settings->get( 'offload_media' ) !== 'enabled' ) { return false; } if ( ( is_admin() && ! self::is_ajax_request() ) || ! $this->settings->is_connected() || ! $this->settings->is_enabled() ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'preview', $_GET ) && ! empty( $_GET['preview'] ) && ! $this->settings->is_offload_enabled() ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'optml_off', $_GET ) && 'true' === $_GET['optml_off'] ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'elementor-preview', $_GET ) && ! empty( $_GET['elementor-preview'] ) ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'ct_builder', $_GET ) && ! empty( $_GET['ct_builder'] ) ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'et_fb', $_GET ) && ! empty( $_GET['et_fb'] ) ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'tve', $_GET ) && $_GET['tve'] === 'true' ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'trp-edit-translation', $_GET ) && ( $_GET['trp-edit-translation'] === 'true' || $_GET['trp-edit-translation'] === 'preview' ) ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'context', $_GET ) && $_GET['context'] === 'edit' ) { return false; // @codeCoverageIgnore } // avada if ( array_key_exists( 'fb-edit', $_GET ) && ! empty( $_GET['fb-edit'] ) ) { return false; // @codeCoverageIgnore } if ( array_key_exists( 'builder', $_GET ) && ! empty( $_GET['builder'] ) && array_key_exists( 'builder_id', $_GET ) && ! empty( $_GET['builder_id'] ) ) { return false; // @codeCoverageIgnore } // Motion.page iFrame & builder if ( ( array_key_exists( 'motionpage_iframe', $_GET ) && $_GET['motionpage_iframe'] === 'true' ) || ( array_key_exists( 'page', $_GET ) && $_GET['page'] === 'motionpage' ) ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison return false; // @codeCoverageIgnore } /** * Disable replacement on POST request and when user is logged in, but allows for sample image call widget in dashboard */ if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST' && is_user_logged_in() && ( ! isset( $_GET['quality'] ) || ! current_user_can( 'manage_options' ) ) ) { return false; // @codeCoverageIgnore } if ( class_exists( 'FLBuilderModel', false ) ) { $post_data = FLBuilderModel::get_post_data(); if ( isset( $_GET['fl_builder'] ) || isset( $post_data['fl_builder'] ) ) { return false; } } $filters = $this->settings->get_filters(); return Optml_Filters::should_do_page( $filters[ Optml_Settings::FILTER_TYPE_OPTIMIZE ][ Optml_Settings::FILTER_URL ], $filters[ Optml_Settings::FILTER_TYPE_OPTIMIZE ][ Optml_Settings::FILTER_URL_MATCH ] ); } /** * Check if we are in a ajax contex where we should enable replacement. * * @return bool Is ajax request? */ public static function is_ajax_request() { if ( apply_filters( 'optml_force_replacement_on', false ) === true ) { return true; } if ( ! function_exists( 'is_user_logged_in' ) ) { return false; } // Disable for logged in users to avoid unexpected results. if ( is_user_logged_in() ) { return false; } if ( ! function_exists( 'wp_doing_ajax' ) ) { return false; } if ( ! wp_doing_ajax() ) { return false; } if ( isset( $_REQUEST['action'] ) && strpos( $_REQUEST['action'], 'wpmdb' ) !== false ) { return false; } return true; } /** * Register frontend replacer hooks. */ public function register_hooks() { do_action( 'optml_replacer_setup' ); if ( $this->settings->get( 'native_lazyload' ) === 'disabled' ) { add_filter( 'wp_lazy_loading_enabled', '__return_false' ); } add_filter( 'the_content', [ $this, 'process_images_from_content' ], PHP_INT_MAX ); /** * When we have to process cdn images, i.e MIRROR is defined, * we need this as late as possible for other replacers to occur. * Otherwise, we can hook first to avoid any other plugins to take care of replacement. */ add_action( self::is_ajax_request() ? 'init' : 'template_redirect', [ $this, 'process_template_redirect_content', ], defined( 'OPTML_SITE_MIRROR' ) ? PHP_INT_MAX : PHP_INT_MIN ); add_action( 'template_redirect', [ $this, 'register_after_setup' ] ); add_action( 'rest_api_init', [ $this, 'process_template_redirect_content' ], PHP_INT_MIN ); add_action( 'shutdown', [ $this, 'close_buffer' ] ); foreach ( self::$loaded_compatibilities as $registered_compatibility ) { $registered_compatibility->register(); } } /** * Run after Optimole is fully setup. */ public function register_after_setup() { do_action( 'optml_after_setup' ); } /** * Filter raw HTML content for urls. * * @param string $html HTML to filter. * * @return mixed Filtered content. */ public function replace_content( $html ) { if ( defined( 'REST_REQUEST' ) && REST_REQUEST && is_user_logged_in() && ( apply_filters( 'optml_force_replacement', false ) !== true ) ) { return $html; } $html = $this->add_html_class( $html ); $html = $this->process_images_from_content( $html ); if ( $this->settings->get( 'video_lazyload' ) === 'enabled' ) { $html = apply_filters( 'optml_video_replace', $html ); if ( Optml_Lazyload_Replacer::found_iframe() === true ) { if ( strpos( $html, Optml_Lazyload_Replacer::IFRAME_TEMP_COMMENT ) !== false ) { $html = str_replace( Optml_Lazyload_Replacer::IFRAME_TEMP_COMMENT, Optml_Lazyload_Replacer::IFRAME_PLACEHOLDER_CLASS, $html ); } else { $html = preg_replace( '/(.*)<\/head>/ism', ' $1' . Optml_Lazyload_Replacer::IFRAME_PLACEHOLDER_STYLE . '', $html ); } } } $html = apply_filters( 'optml_url_pre_process', $html ); $html = $this->process_urls_from_content( $html ); $html = apply_filters( 'optml_url_post_process', $html ); return $html; } /** * Adds a filter that allows adding classes to the HTML tag. * * @param string $content The HTML content. * * @return mixed */ public function add_html_class( $content ) { if ( empty( $content ) ) { return $content; } $additional_html_classes = apply_filters( 'optml_additional_html_classes', [] ); if ( ! $additional_html_classes ) { return $content; } if ( preg_match( '//ismU', $content, $matches, PREG_OFFSET_CAPTURE ) === 1 ) { $add_classes = implode( ' ', $additional_html_classes ); foreach ( $matches as $match ) { if ( strpos( $match[0], 'class' ) !== false ) { $new_tag = str_replace( [ 'class="', "class='" ], [ 'class="' . $add_classes, "class='" . $add_classes ], $match[0] ); } else { $new_tag = str_replace( 'html ', 'html class="' . $add_classes . '" ', $match[0] ); } $content = str_replace( $match[0], $new_tag, $content ); } } return $content; } /** * Adds a filter with detected images tags and the content. * * @param string $content The HTML content. * * @return mixed */ public function process_images_from_content( $content ) { if ( self::should_ignore_image_tags() ) { return $content; } $images = self::parse_images_from_html( $content ); if ( empty( $images ) ) { return $content; } return apply_filters( 'optml_content_images_tags', $content, $images ); } /** * Check if we are on a amp endpoint. * * IMPORTANT: This needs to be used after parse_query hook, otherwise will return false positives. * * @return bool */ public static function should_ignore_image_tags() { // Ignore image tag replacement in feed context as we don't need it. if ( is_feed() ) { return true; } // Ignore image tags replacement in amp context as they are not available. if ( function_exists( 'is_amp_endpoint' ) ) { return is_amp_endpoint(); } if ( function_exists( 'ampforwp_is_amp_endpoint' ) ) { return ampforwp_is_amp_endpoint(); } return apply_filters( 'optml_should_ignore_image_tags', false ) === true; } /** * Match all images and any relevant tags in a block of HTML. * * @param string $content Some HTML. * * @return array An array of $images matches, where $images[0] is * an array of full matches, and the link_url, img_tag, * and img_url keys are arrays of those matches. */ public static function parse_images_from_html( $content ) { $images = []; $header_start = null; $header_end = null; if ( preg_match( '//ismU', $content, $matches, PREG_OFFSET_CAPTURE ) === 1 ) { $header_start = $matches[0][1]; $header_end = $header_start + strlen( $matches[0][0] ); } $regex = '/(?:]+?href=["|\'](?P[^\s]+?)["|\'][^>]*?>\s*)?(?P(?:\s*)?]*?\s?(?:' . implode( '|', array_merge( [ 'src' ], Optml_Tag_Replacer::possible_src_attributes() ) ) . ')=["\'\\\\]*?(?P[' . Optml_Config::$chars . ']{10,}).*?>(?:\s*<\/noscript\s*>)?){1}(?:\s*<\/a>)?/ismu'; if ( preg_match_all( $regex, $content, $images, PREG_OFFSET_CAPTURE ) ) { if ( OPTML_DEBUG ) { do_action( 'optml_log', $images ); } foreach ( $images as $key => $unused ) { // Simplify the output as much as possible, mostly for confirming test results. if ( is_numeric( $key ) && $key > 0 ) { unset( $images[ $key ] ); continue; } $is_no_script = false; foreach ( $unused as $url_key => $url_value ) { if ( $key === 'img_url' ) { $images[ $key ][ $url_key ] = rtrim( $url_value[0], '\\' ); continue; } $images[ $key ][ $url_key ] = $url_value[0]; if ( $key === 0 ) { $images['in_header'][ $url_key ] = $header_start !== null ? ( $url_value[1] > $header_start && $url_value[1] < $header_end ) : false; // Check if we are in the noscript context. if ( $is_no_script === false ) { $is_no_script = strpos( $images[0][ $url_key ], 'extract_urls_from_content( $html ); if ( OPTML_DEBUG ) { do_action( 'optml_log', 'matched urls' ); do_action( 'optml_log', $extracted_urls ); } return $this->do_url_replacement( $html, $extracted_urls ); } /** * Method to extract assets from content. * * @param string $content The HTML content. * * @return array */ public function extract_urls_from_content( $content ) { $extensions = array_keys( Optml_Config::$image_extensions ); if ( $this->settings->use_cdn() && ! self::should_ignore_image_tags() ) { $extensions = array_merge( $extensions, array_keys( Optml_Config::$assets_extensions ) ); } $regex = '/(?:[(|\s\';",=\]])((?:http|\/|\\\\){1}(?:[' . Optml_Config::$chars . ']{10,}\.(?:' . implode( '|', $extensions ) . ')))(?=(?:http|>|%3F|\?|"|&|,|\s|\'|\)|\||\\\\|}|\[))/Uu'; preg_match_all( $regex, $content, $urls ); return $this->normalize_urls( $urls[1] ); } /** * Normalize extracted urls. * * @param array $urls Raw urls extracted. * * @return array Normalized array. */ private function normalize_urls( $urls ) { $urls = array_map( function ( $value ) { $value = str_replace( '"', '', $value ); return rtrim( $value, '\\";\'' ); }, $urls ); $urls = array_unique( $urls ); return array_values( $urls ); } /** * Process string content and replace possible urls. * * @param string $html String content. * @param array $extracted_urls Urls to check. * * @return string Processed html. */ public function do_url_replacement( $html, $extracted_urls ) { $extracted_urls = apply_filters( 'optml_extracted_urls', $extracted_urls ); if ( empty( $extracted_urls ) ) { return $html; } $slashed_config = addcslashes( Optml_Config::$service_url, '/' ); $extracted_urls = array_filter( $extracted_urls, function ( $value ) use ( $slashed_config ) { return strpos( $value, Optml_Config::$service_url ) === false && strpos( $value, $slashed_config ) === false || Optml_Media_Offload::is_not_processed_image( $value ) || $this->tag_replacer->url_has_dam_flag( $value ); } ); $upload_resource = $this->tag_replacer->get_upload_resource(); $urls = array_combine( $extracted_urls, $extracted_urls ); $urls = array_map( function ( $url ) use ( $upload_resource ) { $is_slashed = strpos( $url, '\/' ) !== false; $is_relative = strpos( $url, $is_slashed ? addcslashes( $upload_resource['content_path'], '/' ) : $upload_resource['content_path'] ) === 0; if ( $is_relative ) { $url = $upload_resource['content_host'] . $url; } return apply_filters( 'optml_content_url', $url ); }, $urls ); foreach ( $urls as $origin => $replace ) { $html = preg_replace( '/(?