* @since 1.50.0 */ namespace AdvancedAds\Importers; use AdvancedAds\Abstracts\Ad; use AdvancedAds\Ads\Ad_Repository; use AdvancedAds\Constants; use AdvancedAds\Framework\Utilities\Arr; use AdvancedAds\Framework\Utilities\Params; use AdvancedAds\Interfaces\Importer as Interface_Importer; use AdvancedAds\Utilities\WordPress; use Exception; use XML_Encoder; defined( 'ABSPATH' ) || exit; /** * XML Importer. * TODO: Refactor logic. */ class XML_Importer extends Importer implements Interface_Importer { /** * Uploaded XML file path * * @var string */ private $import_id; /** * Status messages * * @var array */ private $messages = []; /** * Imported data mapped with previous data, e.g. ['ads'][ new_ad_id => old_ad_id (or null if does not exist) ] * * @var array */ public $imported_data = [ 'ads' => [], 'groups' => [], 'placements' => [], ]; /** * Attachments, created for Image Ads and images in ad content * * @var array */ private $created_attachments = []; /** * Post data indexs to set before inserting into the database * * @var array */ private $post_data_index = []; /** * Constructor. */ public function __construct() { add_action( 'advanced-ads-cleanup-import-file', [ $this, 'delete_old_import_file' ] ); add_filter( 'advanced-ads-new-ad-data', [ $this, 'ad_before_inserting' ], 10, 2 ); remove_action( 'advanced-ads-ad-pre-save', [ \Advanced_Ads_AdSense_Admin::get_instance(), 'save_ad_options' ] ); } /** * Get the unique identifier (ID) of the importer. * * @return string The unique ID of the importer. */ public function get_id(): string { return 'xml'; } /** * Get the title or name of the importer. * * @return string The title of the importer. */ public function get_title(): string { return __( 'XML', 'advanced-ads' ); } /** * Get a description of the importer. * * @return string The description of the importer. */ public function get_description(): string { return ''; } /** * Get the icon to this importer. * * @return string The icon for the importer. */ public function get_icon(): string { return ''; } /** * Detect the importer in database. * * @return bool True if detected; otherwise, false. */ public function detect(): bool { return false; } /** * Render form. * * @return void */ public function render_form(): void { } /** * Import data. * * @return array|string */ public function import() { switch ( Params::post( 'import_type' ) ) { case 'xml_content': if ( '' === Params::post( 'xml_textarea', '' ) ) { $this->messages[] = [ 'error', __( 'Please enter XML content', 'advanced-ads' ) ]; break; } $content = stripslashes( Params::post( 'xml_textarea' ) ); $this->import_content( $content ); break; case 'xml_file': if ( $this->handle_upload() ) { $content = file_get_contents( $this->import_id ); $this->import_content( $content ); @unlink( $this->import_id ); } break; default: return [ 'error', __( 'Please select import type', 'advanced-ads' ) ]; } if ( $this->created_attachments ) { /* translators: %s number of attachments */ $this->messages[] = [ 'success', sprintf( _n( '%s attachment uploaded', '%s attachments uploaded', count( $this->created_attachments ), 'advanced-ads' ), count( $this->created_attachments) ) ]; } return $this->messages; } /** * The main controller for the actual import stage * * @param string $xml_content XML content to import. */ public function import_content( &$xml_content ) { @set_time_limit( 0 ); @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) ); $xml_content = trim( $xml_content ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'source XML:' ); error_log( $xml_content ); } try { $decoded = XML_Encoder::get_instance()->decode( $xml_content ); } catch ( Exception $e ) { error_log( $e->getMessage() ); $this->messages[] = [ 'error', $e->getMessage() ]; return; } if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'decoded XML:' ); error_log( print_r( $decoded, true ) ); } $this->import_ads( $decoded['ads'] ?? [] ); $this->import_groups( $decoded['groups'] ?? [], $decoded['ads'] ?? [] ); $this->import_placements( $decoded['placements'] ?? [] ); $this->import_options( $decoded ); do_action_ref_array( 'advanced-ads-import', [ &$decoded, &$this->imported_data, &$this->messages ] ); wp_cache_flush(); } /** * Set the ad data before inserting into the database * * @param array $post_data Default post data. * @param Ad $ad Ad object. * * @return array */ public function ad_before_inserting( $post_data, $ad ) { foreach ( $this->post_data_index as $index ) { if ( ! empty( $ad->get_prop( $index ) ) ) { $post_data[ $index ] = $ad->get_prop( $index ); } } return $post_data; } /** * Delete old import file via cron * * @param string $path Path to the file. * * @return void */ public function delete_old_import_file( $path ) { if ( file_exists( $path ) ) { @unlink( $path ); } } /** * Handles the XML upload * * @return bool false if error, true otherwise */ private function handle_upload() { $uploads_dir = wp_upload_dir(); if ( ! empty( $uploads_dir['error'] ) ) { $this->messages[] = [ 'error', $uploads_dir['error'] ]; return false; } $import_dir = $uploads_dir['basedir'] . '/advads-import'; $this->import_id = $import_dir . '/' . md5( time() . NONCE_SALT ); if ( ! is_dir( $import_dir ) && ! wp_mkdir_p( $import_dir ) ) { /* translators: %s import directory */ $this->messages[] = [ 'error', sprintf( __( 'Failed to create import directory %s', 'advanced-ads' ), $import_dir ) ]; return false; } if ( ! is_writable( $import_dir ) ) { /* translators: %s import directory */ $this->messages[] = [ 'error', sprintf( __( 'Import directory is not writable: %s', 'advanced-ads' ), $import_dir ) ]; return false; } if ( ! @file_exists( $import_dir . '/index.php' ) ) { @touch( $import_dir . '/index.php' ); } if ( ! isset( $_FILES['import'] ) ) { $this->messages[] = [ 'error', __( 'File is empty, uploads are disabled or post_max_size is smaller than upload_max_filesize in php.ini', 'advanced-ads' ) ]; return false; } $file = $_FILES['import']; // determine if uploaded file exceeds space quota. $file = apply_filters( 'wp_handle_upload_prefilter', $file ); if ( ! empty( $file['error'] ) ) { /* translators: %s error in file */ $this->messages[] = [ 'error', sprintf( __( 'Failed to upload file, error: %s', 'advanced-ads' ), $file['error'] ) ]; return false; } if ( ! ( $file['size'] > 0 ) ) { $this->messages[] = [ 'error', __( 'File is empty.', 'advanced-ads' ), $file['error'] ]; return false; } if ( ! is_uploaded_file( $file['tmp_name'] ) || ! @ move_uploaded_file( $file['tmp_name'], $this->import_id ) || ! is_readable( $this->import_id ) ) { /* translators: %s import id */ $this->messages[] = [ 'error', sprintf( __( 'The file could not be created: %s. This is probably a permissions problem', 'advanced-ads' ), $this->import_id ) ]; return false; } // Set correct file permissions. $stat = stat( dirname( $import_dir ) ); $perms = $stat['mode'] & 0000666; @ chmod( $this->import_id, $perms ); // cleanup in case of failed import. wp_schedule_single_event( time() + 10 * MINUTE_IN_SECONDS, 'advanced-ads-cleanup-import-file', [ $this->import_id ] ); return true; } /** * Create new ads and groups based on import information * * @param array $decoded decoded XML. */ private function import_ads( $decoded ) { // Early bail!! if ( empty( $decoded ) ) { return; } $count_attachment = 0; $count_ads = 0; foreach ( $decoded as $ad ) { if ( isset( $ad['meta_input'] ) && is_array( $ad['meta_input'] ) ) { foreach ( $ad['meta_input'] as $meta_k => &$meta_v ) { if ( Ad_Repository::OPTION_METAKEY !== $meta_k ) { $meta_v = WordPress::maybe_unserialize( $meta_v ); } } } // upload images for Image ad type. if ( isset( $ad['attached_img_url'] ) && ( isset( $ad['meta_input']['advanced_ads_ad_options']['output']['image_id'] ) || isset( $ad['meta_input']['advanced_ads_ad_options']['image_id'] ) ) ) { $attached_img_url = $this->replace_placeholders( $ad['attached_img_url'] ); $attachment_id = null; if ( isset( $this->created_attachments[ $attached_img_url ] ) ) { $attachment_id = $this->created_attachments[ $attached_img_url ]['post_id']; } else if ( $attachment = $this->upload_image_from_url( $attached_img_url ) ) { $link = ( $link = get_attachment_link( $attachment['post_id'] ) ) ? sprintf( '%s', esc_url( $link ), __( 'Edit', 'advanced-ads' ) ) : ''; $attachment_id = $attachment['post_id']; ++$count_attachment; $this->created_attachments[ $attached_img_url ] = $attachment; } if ( $attachment_id ) { $ad['meta_input']['advanced_ads_ad_options']['output']['image_id'] = $attachment_id; } } $ad_obj = wp_advads_create_new_ad( $ad['meta_input']['advanced_ads_ad_options']['type'] ); if ( ! $ad_obj ) { /* translators: %s Ad title */ $this->messages[] = [ 'error', sprintf( __( 'Failed to import %s', 'advanced-ads' ), esc_html( Arr::get( $ad, 'post_title' ) ) ) ]; continue; } $ad_obj->set_title( Arr::get( $ad, 'post_title' ) ); $ad_obj->set_content( $this->process_ad_content( Arr::get( $ad, 'post_content', '' ) ) ); $ad_obj->set_status( Arr::get( $ad, 'post_status', 'publish' ) ); $ad_obj->set_author_id( get_current_user_id() ); // set on filter 'advanced-ads-new-ad-data'. $ad_obj->set_prop_temp( 'post_date', Arr::get( $ad, 'post_date' ) ); $ad_obj->set_prop_temp( 'post_date_gmt', Arr::get( $ad, 'post_date_gmt' ) ); $ad_obj->set_prop_temp( 'post_password', Arr::get( $ad, 'post_password', '' ) ); $ad_obj->set_prop_temp( 'post_name', Arr::get( $ad, 'post_name', '' ) ); $ad_obj->set_prop_temp( 'post_modified', Arr::get( $ad, 'post_modified', 0 ) ); $ad_obj->set_prop_temp( 'post_modified_gmt', Arr::get( $ad, 'post_modified_gmt', 0 ) ); $ad_obj->set_prop_temp( 'guid', Arr::get( $ad, 'guid', '' ) ); $this->post_data_index = [ 'post_date', 'post_date_gmt', 'post_password', 'post_name', 'post_modified', 'post_modified_gmt', 'guid', ]; foreach ( Arr::get( $ad, 'meta_input.advanced_ads_ad_options', [] ) as $key => $value ) { if ( 'output' === $key ) { foreach ( $value as $prop => $inner_value ) { $ad_obj->set_prop( $prop, $inner_value ); } } elseif ( 'tracking' === $key ) { foreach ( Arr::get( $ad, 'meta_input.advanced_ads_ad_options.tracking' ) as $inner_key => $inner_value ) { $ad_obj->set_prop( "tracking.{$inner_key}", $inner_value ); } } else { $ad_obj->set_prop_temp( $key, $value ); } } if ( $ad_obj->is_type( 'adsense' ) ) { $content = json_decode( str_replace( [ "\n", "\r", ' ' ], '', wp_unslash( $ad['post_content'] ) ), true ); if ( in_array( $content['unitType'] ?? 'none', [ 'responsive', 'link', 'link-responsive', 'matched-content', 'in-article', 'in-feed', ], true ) ) { $ad_obj->set_width( 0 ); $ad_obj->set_height( 0 ); } } $ad_obj->save(); if ( Arr::get( $ad, 'meta_input.advanced_ads_selling_order' ) ) { update_post_meta( $ad_obj->get_id(), 'advanced_ads_selling_order', absint( $ad['meta_input']['advanced_ads_selling_order'] ) ); } if ( Arr::get( $ad, 'meta_input.advanced_ads_selling_order_item' ) ) { update_post_meta( $ad_obj->get_id(), 'advanced_ads_selling_order_item', absint( $ad['meta_input']['advanced_ads_selling_order_item'] ) ); } ++$count_ads; // new ad id => old ad id, if exists. $this->imported_data['ads'][ $ad_obj->get_id() ] = isset( $ad['ID'] ) ? absint( $ad['ID'] ) : null; } if ( $count_ads ) { /* translators: %s number of ads */ $this->messages[] = [ 'success', sprintf( _n( '%s ad imported', '%s ads imported', $count_ads, 'advanced-ads' ), $count_ads ) ]; } } /** * Create new empty groups based on import information * * @param array $decoded group related info. * @param array $ads ads related info. * * @return void */ private function import_groups( $decoded, $ads ) { // Early bail!! if ( empty( $decoded ) ) { return; } foreach ( $decoded as $_group ) { $group = wp_advads_create_new_group( $_group['type'] ?? 'default' ); $group->set_name( $_group['name'] ?? '' ); $group->set_ad_count( $_group['ad_count'] ?? 1 ); $group->set_options( $_group['options'] ?? [] ); $ad_weights = []; if ( isset( $_group['weight'] ) ) { foreach ( $_group['weight'] ?? [] as $old_ad_id => $weight ) { $ad_id = array_search( $old_ad_id, $this->imported_data['ads'] ); if ( $ad_id ) { $ad_weights[ $ad_id ] = $weight ?? Constants::GROUP_AD_DEFAULT_WEIGHT; } } } else { foreach ( $ads as $ad ) { if ( ! isset( $ad['groups'] ) ) { continue; } foreach ( $ad['groups'] as $group_of_current_ad ) { if ( $group_of_current_ad['term_id'] !== $_group['term_id'] ) { continue; } $ad_id = $this->search_item( $ad['ID'], 'ad' ); if ( $ad_id ) { $ad_weights[ $ad_id ] = $group_of_current_ad['weight'] ?? Constants::GROUP_AD_DEFAULT_WEIGHT; } } } } $group->set_ad_weights( $ad_weights ); $group->save(); $this->imported_data['groups'][ $group->get_id() ] = $_group['term_id'] ?? null; } if ( count( $this->imported_data['groups'] ) ) { /* translators: %s number of groups */ $this->messages[] = [ 'success', sprintf( _n( '%s group imported', '%s groups imported', count( $this->imported_data['groups'] ), 'advanced-ads' ), count( $this->imported_data['groups'] ) ) ]; } } /** * Create new placements based on import information * * @param array $decoded decoded XML. */ private function import_placements( $decoded ) { // Early bail!! if ( empty( $decoded ) ) { return; } $existing_placements = wp_advads_get_placements(); $updated_placements = $existing_placements; foreach ( $decoded as &$placement ) { $use_existing = ! empty( $placement['use_existing'] ); if ( $use_existing ) { if ( empty( $placement['key'] ) ) { continue; } $placement_key_uniq = sanitize_title( $placement['key'] ); if ( ! isset( $existing_placements[ $placement_key_uniq ] ) ) { continue; } $existing_placement = $existing_placements[ $placement_key_uniq ]; $existing_placement['key'] = $placement_key_uniq; } else { $placement_key_uniq = $placement['ID'] ?? $placement['key']; $placement_type = Arr::get( $placement, 'meta_input.type', $placement['type'] ?? '' ); $placement['type'] = wp_advads_has_placement_type( $placement_type ) ? $placement_type : 'default'; $placement['name'] = $placement['post_title'] ?? $placement['name']; $placement['item'] = Arr::get( $placement, 'meta_input.item', $placement['item'] ?? null ) ?? ''; // make sure the key in placement array is unique. if ( isset( $existing_placements[ $placement_key_uniq ] ) ) { $count = 1; while ( isset( $existing_placements[ $placement_key_uniq . '_' . $count ] ) ) { ++$count; } $placement_key_uniq .= '_' . $count; } // new placement key => old placement key. $this->imported_data['placements'][ $placement_key_uniq ] = $placement['ID'] ?? null; } // try to set "Item" (ad or group). if ( ! empty( $placement['item'] ) ) { $_item = explode( '_', $placement['item'] ); if ( ! empty( $_item[1] ) ) { switch ( $_item[0] ) { case 'ad': case Constants::ENTITY_AD: $found = $this->search_item( $_item[1], Constants::ENTITY_AD ); if ( false === $found ) { break; } if ( $use_existing ) { // assign new ad to an existing placement // - if the placement has no or a single ad assigned, it will be swapped against the new one // - if a group is assigned to the placement, the new ad will be added to this group with a weight of 1. $placement = $existing_placement; if ( ! empty( $placement['item'] ) ) { // get the item from the existing placement. $_item_existing = explode( '_', $placement['item'] ); if ( ! empty( $_item_existing[1] ) && Constants::ENTITY_GROUP === $_item_existing[0] ) { $advads_ad_weights = get_option( 'advads-ad-weights', [] ); if ( term_exists( absint( $_item_existing[1] ), Constants::TAXONOMY_GROUP ) ) { wp_set_post_terms( $found, $_item_existing[1], Constants::TAXONOMY_GROUP, true ); /** * By default, a new add added to a group receives the weight of 5 * so that users could set the weight of existing ads either higher or lower * depending on whether they want to show the new ad with a higher weight or not. * This is especially useful with Selling Ads to replace an existing ad in a group * with a newly sold one * * Advanced users could use the `advanced-ads-import-default-group-weight` filter * to manipulate the value */ $advads_ad_weights[ $_item_existing[1] ][ $found ] = apply_filters( 'advanced-ads-import-default-group-weight', 5 ); update_option( 'advads-ad-weights', $advads_ad_weights ); // new placement key => old placement key. $this->imported_data['placements'][ $placement_key_uniq ] = $placement_key_uniq; break; } } } } $placement['item'] = 'ad_' . $found; // new placement key => old placement key. $this->imported_data['placements'][ $placement_key_uniq ] = $placement_key_uniq; break; case Constants::ENTITY_GROUP: $found = $this->search_item( $_item[1], Constants::ENTITY_GROUP ); if ( false === $found ) { break; } $placement['item'] = 'group_' . $found; // new placement key => old placement key. $this->imported_data['placements'][ $placement_key_uniq ] = $placement_key_uniq; break; } } } if ( ! isset( $placement['options'] ) ) { $placement['options'] = $placement['meta_input']['options'] ?? []; } $updated_placements[ $placement_key_uniq ] = apply_filters( 'advanced-ads-import-placement', $placement, $this ); } if ( $existing_placements !== $updated_placements ) { $count_placements = 0; foreach ( $updated_placements as $placement_key => $placement_data ) { if ( isset( $existing_placements[ $placement_key ] ) ) { continue; } $new_placement = wp_advads_create_new_placement( $placement_data['type'] ); $new_placement->set_title( $placement_data['name'] ); $new_placement->set_item( $placement_data['item'] ?? '' ); foreach ( $placement_data['options'] as $key => $option ) { if ( 'placement_conditions' === $key ) { foreach ( $placement_data['options']['placement_conditions'] as $prop => $value ) { $new_placement->set_prop( $prop, $value ); } } else { $new_placement->set_prop( $key, $option ); } } $new_placement->save(); ++$count_placements; $this->imported_data['placements'][ $placement_key ] = $new_placement->get_id(); } if ( $count_placements ) { /* translators: %s number of placements */ $this->messages[] = [ 'success', sprintf( _n( '%s placement imported', '%s placements imported', $count_placements, 'advanced-ads' ), $count_placements ) ]; } } } /** * Search for ad/group id * * @param string $id ad/group Group id. * @param string $type Group type. * @return int|bool * - int id of the imported ad/group if exists * - or int id of the existing ad/group if exists * - or bool false */ public function search_item( $id, $type ) { $found = false; switch ( $type ) { case 'ad': case Constants::ENTITY_AD: // if the ad was imported. $found = array_search( $id, $this->imported_data['ads'] ); if ( ! $found ) { // if the ad already exists. if ( get_post_type( $id ) === Constants::POST_TYPE_AD ) { $found = $id; } } break; case Constants::ENTITY_GROUP: $found = array_search( $id, $this->imported_data['groups'] ); if ( ! $found ) { if ( term_exists( absint( $id ), Constants::TAXONOMY_GROUP ) ) { $found = $id; } } break; } return (int) $found; } /** * Create new options based on import information. * * @param array $decoded decoded XML. */ private function import_options( $decoded ) { if ( isset( $decoded['options'] ) && is_array( $decoded['options'] ) ) { $count_options = 0; foreach ( $decoded['options'] as $option_name => $imported_option ) { // Ignore options not belonging to advanced ads. if ( 0 !== strpos( $option_name, 'advads-' ) && 0 !== strpos( $option_name, 'advads_' ) && 0 !== strpos( $option_name, 'advanced-ads' ) && 0 !== strpos( $option_name, 'advanced_ads' ) ) { continue; } $existing_option = get_option( $option_name, [] ); if ( ! is_array( $imported_option ) ) { $imported_option = []; } if ( ! is_array( $existing_option ) ) { $existing_option = []; } $option_to_import = array_merge( $existing_option, $imported_option ); $count_options++; update_option( $option_name, WordPress::maybe_unserialize( $option_to_import ) ); } if ( $count_options ) { /* translators: %s number of options */ $this->messages[] = [ 'success', sprintf( _n( '%s option imported', '%s options imported', $count_options, 'advanced-ads' ), $count_options ) ]; } } } /** * Replace placeholders * * @param string $content The content. * * @return string with replaced placeholders */ private function replace_placeholders( $content ) { $content = str_replace( '{ADVADS_BASE_URL}', ADVADS_BASE_URL, $content ); return $content; } /** * Upload image from URL and create attachment * * @param string $image_url Image url. * @return array with indices: post_id, attachment_url, false on failure */ private function upload_image_from_url( $image_url ) { $file_name = basename( current( explode( '?', $image_url ) ) ); $wp_filetype = wp_check_filetype( $file_name, null ); $parsed_url = @parse_url( $image_url ); $image_url = str_replace( ' ', '%20', $image_url ); if ( ! $wp_filetype['type'] ) { /* translators: %s image url */ $this->messages[] = [ 'error', sprintf( __( 'Invalid filetype %s', 'advanced-ads' ), $image_url ) ]; return false; } if ( ! $parsed_url || ! is_array( $parsed_url ) ) { /* translators: %s image url */ $this->messages[] = [ 'error', sprintf( __( 'Error getting remote image %s', 'advanced-ads' ), $image_url ) ]; return false; } $response = wp_safe_remote_get( $image_url, [ 'timeout' => 20 ] ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { /* translators: %s image url */ $this->messages[] = [ 'error', sprintf( __( 'Error getting remote image %s', 'advanced-ads' ), $image_url ) ]; return false; } // Upload the file. $upload = wp_upload_bits( $file_name, '', wp_remote_retrieve_body( $response ) ); if ( $upload['error'] ) { /* translators: %s image url */ $this->messages[] = [ 'error', sprintf( __( 'Error getting remote image %s', 'advanced-ads' ), $image_url ) ]; return false; } // Get filesize. $filesize = filesize( $upload['file'] ); if ( 0 == $filesize ) { @unlink( $upload['file'] ); /* translators: %s image url */ $this->messages[] = [ 'error', sprintf( __( 'Zero size file downloaded %s', 'advanced-ads' ), $image_url ) ]; return false; } /** * Get allowed image mime types. * * @var string Single mime type. */ $allowed_mime_types = get_allowed_mime_types() ?? []; $mime_types = array_filter( get_allowed_mime_types(), function( $mime_type ) { return preg_match( '/image\//', $mime_type ); } ); $fileinfo = @getimagesize( $upload['file'] ); if ( ! $fileinfo || ! in_array( $fileinfo['mime'], $mime_types, true ) ) { @unlink( $upload['file'] ); /* translators: %s image url */ $this->messages[] = [ 'error', sprintf( __( 'Error getting remote image %s', 'advanced-ads' ), $image_url ) ]; return false; } // create new post. $new_post = [ 'post_title' => $file_name, 'post_mime_type' => $wp_filetype['type'], 'guid' => $upload['url'], ]; if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) { require_once ABSPATH . 'wp-admin/includes/image.php'; } $post_id = wp_insert_attachment( $new_post, $upload['file'] ); wp_update_attachment_metadata( $post_id, wp_generate_attachment_metadata( $post_id, $upload['file'] ) ); return [ 'post_id' => $post_id, 'attachment_url' => wp_get_attachment_url( $post_id ), ]; } /** * Ad content manipulations * * @param string $content Content. * * @return string $content */ private function process_ad_content( $content ) { $replacement_map = []; if ( preg_match_all( '/\(\S+?)\<\/advads_import_img\>/i', $content, $matches ) ) { foreach ( $matches[1] as $k => $url ) { if ( isset( $this->created_attachments[ $url ] ) ) { $replacement_map[ $url ] = $this->created_attachments[ $url ]['attachment_url']; } else if ( $attachment = $this->upload_image_from_url( $url ) ) { $link = ( $link = get_attachment_link( $attachment['post_id'] ) ) ? sprintf( '%s', esc_url( $link ), __( 'Edit', 'advanced-ads' ) ) : ''; /* translators: 1: Attachment ID 2: Attachment link */ $this->messages[] = [ 'success', sprintf( __( 'New attachment created %1$s %2$s', 'advanced-ads' ), $attachment['post_id'], $link ) ]; $this->created_attachments[ $url ] = $attachment; $replacement_map[ $url ] = $attachment['attachment_url']; } } } $content = str_replace( [ '', '' ], '', $content ); if ( count( $replacement_map ) ) { $content = str_replace( array_keys( $replacement_map ), array_values( $replacement_map ), $content ); } return $this->replace_placeholders( $content ); } }