=> '15px', 'right' => '15px', 'background-color' => '#fff', 'padding' => '8px 6px', 'font-size' => '12px', 'font-weight' => '600', 'color' => '#444', 'border' => '1px solid #E9E9E9', 'z-index' => '9999999999', 'border-radius' => '4px', 'text-decoration' => 'none', 'font-family' => 'Arial, Helvetica, sans-serif', ]; $logo = OPTML_URL . 'assets/img/logo.svg'; $link = tsdk_translate_link( 'https://optimole.com/wordpress/?from=badgeOn' ); $css = ''; foreach ( $div_style as $key => $value ) { $css .= $key . ':' . $value . ';'; } $output = ''; $output .= ' '; $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( '/(?