import_from_file( $mo_file ); } $l10n['guardafoto'] = $mo; } else { // English: an empty translation set makes __() return the sources. $l10n['guardafoto'] = new MO(); } $strings = call_user_func( $builder ); if ( $had_previous ) { $l10n['guardafoto'] = $previous; } else { unset( $l10n['guardafoto'] ); } return $strings; } /** * Return the initial user-interface strings of the front-end tool. * * English is the source language; Spanish comes from the translation * files in /languages. * * @return array */ function guardafoto_ui_strings() { return array( 'label' => __( 'Paste a YouTube, Spotify, Deezer, Apple Music, TikTok or Instagram link', 'guardafoto' ), 'placeholder' => __( 'https://www.youtube.com/@channel', 'guardafoto' ), 'button' => __( 'Get the photo', 'guardafoto' ), 'hint' => __( 'Works with YouTube channels and videos, plus supported artists, albums, songs and playlists — and with public TikTok and Instagram videos.', 'guardafoto' ), 'loading' => __( 'Looking up the photo…', 'guardafoto' ), 'result' => __( 'GuardaFoto result', 'guardafoto' ), 'type' => __( 'Type', 'guardafoto' ), 'size' => __( 'Size', 'guardafoto' ), 'download' => __( 'Download', 'guardafoto' ), 'video' => __( 'Video', 'guardafoto' ), ); } /** * Return the dynamic message strings used by the front-end tool script. * * Delivered to tool.js through wp_localize_script in both languages, so * each tool instance can honour its own lang attribute. * * @return array */ function guardafoto_tool_strings() { return array( 'working' => __( 'Looking up the photo…', 'guardafoto' ), 'found' => __( 'Photo found. Choose a size to download.', 'guardafoto' ), 'foundVideo' => __( 'Found it. The files are listed below — pick a size or the video to download.', 'guardafoto' ), 'empty' => __( 'Paste a link first.', 'guardafoto' ), 'invalid' => __( 'That link is not supported. Paste a public YouTube, Spotify, Deezer, Apple Music, TikTok or Instagram link.', 'guardafoto' ), 'notFound' => __( 'That link could not be found, or it is not public.', 'guardafoto' ), 'rateLimited' => __( 'Too many lookups right now. Please wait a moment and try again.', 'guardafoto' ), 'unavailable' => __( 'The lookup service could not complete that request. Please try again in a moment.', 'guardafoto' ), 'network' => __( 'The photo service could not be reached. Check your connection and try again.', 'guardafoto' ), 'noImages' => __( 'No downloadable files were returned for that link.', 'guardafoto' ), 'profileBlocked' => __( 'Instagram profile photos cannot be downloaded: Meta shows them only to logged-in accounts.', 'guardafoto' ), 'download' => __( 'Download', 'guardafoto' ), 'type' => __( 'File', 'guardafoto' ), 'video' => __( 'Video', 'guardafoto' ), 'videoFile' => __( 'Video file', 'guardafoto' ), 'platforms' => array( 'youtube' => __( 'YouTube', 'guardafoto' ), 'spotify' => __( 'Spotify', 'guardafoto' ), 'deezer' => __( 'Deezer', 'guardafoto' ), 'apple' => __( 'Apple Music', 'guardafoto' ), 'tiktok' => __( 'TikTok', 'guardafoto' ), 'instagram' => __( 'Instagram', 'guardafoto' ), 'x' => __( 'X', 'guardafoto' ), 'twitch' => __( 'Twitch', 'guardafoto' ), ), 'kinds' => array( 'avatar' => __( 'Profile photo', 'guardafoto' ), 'banner' => __( 'Banner', 'guardafoto' ), 'thumbnail' => __( 'Thumbnail', 'guardafoto' ), 'cover' => __( 'Cover', 'guardafoto' ), 'image' => __( 'Image', 'guardafoto' ), 'video' => __( 'Video', 'guardafoto' ), ), 'types' => array( 'channel' => __( 'Channel', 'guardafoto' ), 'video' => __( 'Video', 'guardafoto' ), 'artist' => __( 'Artist', 'guardafoto' ), 'album' => __( 'Album', 'guardafoto' ), 'track' => __( 'Song', 'guardafoto' ), 'playlist' => __( 'Playlist', 'guardafoto' ), 'show' => __( 'Podcast', 'guardafoto' ), 'episode' => __( 'Episode', 'guardafoto' ), 'profile' => __( 'Profile', 'guardafoto' ), 'reel' => __( 'Reel', 'guardafoto' ), 'post' => __( 'Post', 'guardafoto' ), ), ); } /** * Enqueue the assets required by the tool. * * @param string $language Either "en" or "es". * @return void */ function guardafoto_enqueue_tool_assets( $language ) { wp_enqueue_style( 'guardafoto-tool' ); wp_enqueue_script( 'guardafoto-tool' ); wp_localize_script( 'guardafoto-tool', 'guardafotoToolConfig', array( 'endpoint' => GUARDAFOTO_API_ENDPOINT, 'defaultLang' => guardafoto_resolve_language( 'auto' ), 'language' => $language, 'strings' => array( 'en' => guardafoto_strings_for_language( 'en', 'guardafoto_tool_strings' ), 'es' => guardafoto_strings_for_language( 'es', 'guardafoto_tool_strings' ), ), ) ); } /** * Render the GuardaFoto tool. * * @param string $language Requested language: auto, en or es. * @return string */ function guardafoto_render_tool( $language = 'auto' ) { $language = guardafoto_resolve_language( $language ); $strings = guardafoto_strings_for_language( $language, 'guardafoto_ui_strings' ); $form_id = wp_unique_id( 'guardafoto-form-' ); guardafoto_enqueue_tool_assets( $language ); ob_start(); ?>

'auto', ), is_array( $atts ) ? $atts : array(), 'guardafoto' ); return guardafoto_render_tool( $atts['lang'] ); } add_shortcode( 'guardafoto', 'guardafoto_shortcode' ); /** * Render callback for the GuardaFoto block. * * @param array $attributes Block attributes. * @return string */ function guardafoto_render_block( $attributes = array() ) { $language = isset( $attributes['lang'] ) ? (string) $attributes['lang'] : 'auto'; return guardafoto_render_tool( $language ); } /** * Register the GuardaFoto Gutenberg block. * * The block is dynamic and renders exactly the same markup as the shortcode. * * @return void */ function guardafoto_register_block() { if ( ! function_exists( 'register_block_type' ) ) { return; } register_block_type( 'guardafoto/tool', array( 'api_version' => 2, 'title' => __( 'GuardaFoto', 'guardafoto' ), 'description' => __( 'Add a public profile photo, banner, thumbnail and cover downloader using the free GuardaFoto API.', 'guardafoto' ), 'category' => 'widgets', 'icon' => 'download', 'keywords' => array( 'GuardaFoto', 'YouTube', 'Spotify', 'photo', 'thumbnail' ), 'editor_script' => 'guardafoto-block-editor', 'editor_style' => 'guardafoto-tool', 'style' => 'guardafoto-tool', 'script' => 'guardafoto-tool', 'render_callback' => 'guardafoto_render_block', 'attributes' => array( 'lang' => array( 'type' => 'string', 'default' => 'auto', 'enum' => array( 'auto', 'en', 'es' ), ), ), 'supports' => array( 'html' => false, ), ) ); } add_action( 'init', 'guardafoto_register_block', 3 ); /* ------------------------------------------------------------------------- * Dashboard tool (wp-admin) * * A GuardaFoto page in the admin menu, a panel in the block editor and a * meta box in the classic editor. In all three places the lookup runs * through WordPress AJAX: the server calls the same GuardaFoto API with * only the pasted link, and an explicit "Import" click downloads the * chosen image into the Media Library. Nothing happens on page views. * ---------------------------------------------------------------------- */ /** * User-interface strings for the dashboard tool. * * English is the source language; Spanish comes from the translation * files in /languages, in the current (site or user) locale. * * @return array */ function guardafoto_admin_strings() { return array( 'intro' => __( 'Paste a public YouTube, Spotify, Deezer, Apple Music, TikTok, Instagram, X or Twitch link. You can download each photo, or import it straight into this site\'s Media Library.', 'guardafoto' ), 'editorIntro' => __( 'Paste a public link and use its photo in this post: import it to the Media Library, set it as the featured image, or insert it into the content.', 'guardafoto' ), 'panelTitle' => __( 'GuardaFoto — Extract the photo here', 'guardafoto' ), 'label' => __( 'Link', 'guardafoto' ), 'placeholder' => __( 'https://www.youtube.com/watch?v=…', 'guardafoto' ), 'button' => __( 'Look up the photo', 'guardafoto' ), 'working' => __( 'Looking up the photo…', 'guardafoto' ), 'found' => __( 'Photo found. Download it or import it to the Media Library.', 'guardafoto' ), 'foundVideo' => __( 'Found it. The photos and the video from that link are listed below.', 'guardafoto' ), 'empty' => __( 'Paste a link first.', 'guardafoto' ), 'invalid' => __( 'That link is not supported. Paste a public YouTube, Spotify, Deezer, Apple Music, TikTok, Instagram, X or Twitch link.', 'guardafoto' ), 'notFound' => __( 'That link could not be found, or it is not public.', 'guardafoto' ), 'rateLimited' => __( 'Too many lookups right now. Please wait a moment and try again.', 'guardafoto' ), 'unavailable' => __( 'The lookup service could not complete that request. Please try again in a moment.', 'guardafoto' ), 'network' => __( 'The request could not be completed. Check the connection and try again.', 'guardafoto' ), 'noImages' => __( 'No downloadable files were returned for that link.', 'guardafoto' ), 'profileBlocked' => __( 'Instagram profile photos cannot be downloaded: Meta shows them only to logged-in accounts.', 'guardafoto' ), 'download' => __( 'Download', 'guardafoto' ), 'import' => __( 'Import to Media Library', 'guardafoto' ), 'importing' => __( 'Importing…', 'guardafoto' ), 'imported' => __( 'Imported ✓', 'guardafoto' ), 'viewMedia' => __( 'View in Media Library', 'guardafoto' ), 'setFeatured' => __( 'Import & set as featured image', 'guardafoto' ), 'featuredSet' => __( 'Featured image set ✓', 'guardafoto' ), 'featuredFailed' => __( 'The photo was imported, but it could not be set as the featured image.', 'guardafoto' ), 'insertContent' => __( 'Import & insert into content', 'guardafoto' ), 'inserted' => __( 'Inserted into the content ✓', 'guardafoto' ), 'type' => __( 'Type', 'guardafoto' ), 'size' => __( 'Size', 'guardafoto' ), 'video' => __( 'Video', 'guardafoto' ), 'videoFile' => __( 'Video file', 'guardafoto' ), 'videoNote' => __( 'Videos are download-only: this version does not import them into the Media Library.', 'guardafoto' ), 'tooBig' => __( 'This video is {size} and exceeds this site\'s upload limit ({limit}): it can only be downloaded.', 'guardafoto' ), 'importFailed' => __( 'The photo could not be imported.', 'guardafoto' ), 'forbidden' => __( 'You are not allowed to do that.', 'guardafoto' ), 'platforms' => array( 'youtube' => __( 'YouTube', 'guardafoto' ), 'spotify' => __( 'Spotify', 'guardafoto' ), 'deezer' => __( 'Deezer', 'guardafoto' ), 'apple' => __( 'Apple Music', 'guardafoto' ), 'tiktok' => __( 'TikTok', 'guardafoto' ), 'instagram' => __( 'Instagram', 'guardafoto' ), 'x' => __( 'X', 'guardafoto' ), 'twitch' => __( 'Twitch', 'guardafoto' ), ), 'kinds' => array( 'avatar' => __( 'Profile photo', 'guardafoto' ), 'banner' => __( 'Banner', 'guardafoto' ), 'thumbnail' => __( 'Thumbnail', 'guardafoto' ), 'cover' => __( 'Cover', 'guardafoto' ), 'image' => __( 'Image', 'guardafoto' ), 'video' => __( 'Video', 'guardafoto' ), ), 'types' => array( 'channel' => __( 'Channel', 'guardafoto' ), 'video' => __( 'Video', 'guardafoto' ), 'artist' => __( 'Artist', 'guardafoto' ), 'album' => __( 'Album', 'guardafoto' ), 'track' => __( 'Song', 'guardafoto' ), 'playlist' => __( 'Playlist', 'guardafoto' ), 'show' => __( 'Podcast', 'guardafoto' ), 'episode' => __( 'Episode', 'guardafoto' ), 'profile' => __( 'Profile', 'guardafoto' ), 'reel' => __( 'Reel', 'guardafoto' ), 'post' => __( 'Post', 'guardafoto' ), ), ); } /** * Render the shared lookup box used by the admin page and the classic * editor meta box. The JavaScript in assets/admin.js wires it up. * * @param string $context Either "menu" or "classic". * @param int $post_id Post being edited, or 0 outside the editor. * @return string */ function guardafoto_admin_box_html( $context, $post_id = 0 ) { $strings = guardafoto_admin_strings(); $intro = 'classic' === $context ? $strings['editorIntro'] : $strings['intro']; $input_id = wp_unique_id( 'guardafoto-admin-input-' ); ob_start(); ?>

is dropped by the browser's parser — // so there the lookup box uses a plain container and a button of // type "button", and admin.js wires the click and the Enter key // instead of a form submit. if ( 'classic' === $context ) : ?>
' : '
'; ?>
'; echo '

GuardaFoto

'; echo guardafoto_admin_box_html( 'menu', 0 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Markup is escaped inside guardafoto_admin_box_html(). echo '
'; } /** * Register the classic-editor meta box on post types that do not use the * block editor (the block editor gets its own GuardaFoto panel instead). * * The box lives in the side column with high priority, right where the * featured-image box is, and carries the same "Extract the photo here" * title as the block-editor panel, so the tool is found where the author * is already looking for a photo. * * @return void */ function guardafoto_add_meta_boxes() { if ( ! current_user_can( 'upload_files' ) ) { return; } $strings = guardafoto_admin_strings(); $screens = get_post_types( array( 'public' => true ), 'names' ); foreach ( $screens as $screen ) { if ( ! post_type_supports( $screen, 'editor' ) ) { continue; } if ( function_exists( 'use_block_editor_for_post_type' ) && use_block_editor_for_post_type( $screen ) ) { continue; } add_meta_box( 'guardafoto', $strings['panelTitle'], 'guardafoto_meta_box_render', $screen, 'side', 'high' ); } } add_action( 'add_meta_boxes', 'guardafoto_add_meta_boxes' ); /** * Render the classic-editor meta box. * * @param WP_Post $post Post being edited. * @return void */ function guardafoto_meta_box_render( $post ) { echo guardafoto_admin_box_html( 'classic', (int) $post->ID ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Markup is escaped inside guardafoto_admin_box_html(). } /** * Enqueue the dashboard assets on the GuardaFoto page and on the post * editing screens (for the classic meta box), and localize the config. * * @param string $hook_suffix Current admin page hook. * @return void */ function guardafoto_admin_enqueue( $hook_suffix ) { $is_menu_page = 'toplevel_page_guardafoto' === $hook_suffix; $is_post_page = in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true ); if ( ! $is_menu_page && ! $is_post_page ) { return; } if ( ! current_user_can( 'upload_files' ) ) { return; } wp_enqueue_style( 'guardafoto-admin', plugins_url( 'assets/admin.css', __FILE__ ), array(), GUARDAFOTO_VERSION ); wp_enqueue_script( 'guardafoto-admin', plugins_url( 'assets/admin.js', __FILE__ ), array(), GUARDAFOTO_VERSION, true ); wp_localize_script( 'guardafoto-admin', 'guardafotoAdminConfig', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'guardafoto_admin' ), 'language' => guardafoto_resolve_language( 'auto' ), 'maxUploadSize' => wp_max_upload_size(), 'postId' => $is_post_page ? get_the_ID() : 0, 'strings' => guardafoto_admin_strings(), ) ); } add_action( 'admin_enqueue_scripts', 'guardafoto_admin_enqueue' ); /** * Enqueue the GuardaFoto panel script in the block editor. * * @return void */ function guardafoto_editor_enqueue() { if ( ! current_user_can( 'upload_files' ) ) { return; } wp_enqueue_style( 'guardafoto-admin', plugins_url( 'assets/admin.css', __FILE__ ), array(), GUARDAFOTO_VERSION ); wp_enqueue_script( 'guardafoto-editor', plugins_url( 'assets/editor.js', __FILE__ ), array( 'wp-plugins', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-components', 'wp-data', 'wp-blocks' ), GUARDAFOTO_VERSION, true ); wp_localize_script( 'guardafoto-editor', 'guardafotoAdminConfig', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'guardafoto_admin' ), 'language' => guardafoto_resolve_language( 'auto' ), 'maxUploadSize' => wp_max_upload_size(), 'strings' => guardafoto_admin_strings(), ) ); } add_action( 'enqueue_block_editor_assets', 'guardafoto_editor_enqueue' ); /** * Shared guard for the dashboard AJAX actions: valid nonce + capability. * * @return void Exits with a JSON error when the check fails. */ function guardafoto_ajax_guard() { check_ajax_referer( 'guardafoto_admin' ); if ( ! current_user_can( 'upload_files' ) ) { $strings = guardafoto_admin_strings(); wp_send_json_error( array( 'code' => 'forbidden', 'message' => $strings['forbidden'], ), 403 ); } } /** * AJAX: resolve a pasted link through the GuardaFoto API, server-side. * * @return void */ function guardafoto_ajax_resolve() { guardafoto_ajax_guard(); $strings = guardafoto_admin_strings(); $url = isset( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- wp_unslash + esc_url_raw applied. if ( '' === $url ) { wp_send_json_error( array( 'code' => 'bad_url', 'message' => $strings['invalid'], ) ); } $api_url = add_query_arg( 'url', $url, GUARDAFOTO_API_ENDPOINT ); $response = wp_remote_get( $api_url, array( 'timeout' => 60, 'headers' => array( 'Accept' => 'application/json', ), ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'code' => 'fetch_failed', 'message' => $strings['unavailable'], ) ); } $data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $data ) ) { wp_send_json_error( array( 'code' => 'fetch_failed', 'message' => $strings['unavailable'], ) ); } if ( empty( $data['ok'] ) ) { $code = isset( $data['error'] ) ? sanitize_text_field( (string) $data['error'] ) : 'fetch_failed'; if ( 429 === (int) wp_remote_retrieve_response_code( $response ) ) { $code = 'rate_limited'; } wp_send_json_error( array( 'code' => $code, 'message' => $strings['unavailable'], ) ); } $images = array(); if ( isset( $data['images'] ) && is_array( $data['images'] ) ) { foreach ( $data['images'] as $image ) { if ( ! is_array( $image ) || empty( $image['url'] ) ) { continue; } $images[] = array( 'kind' => isset( $image['kind'] ) ? sanitize_text_field( (string) $image['kind'] ) : 'image', 'label' => isset( $image['label'] ) ? sanitize_text_field( (string) $image['label'] ) : '', 'width' => isset( $image['width'] ) ? absint( $image['width'] ) : 0, 'height' => isset( $image['height'] ) ? absint( $image['height'] ) : 0, 'url' => esc_url_raw( (string) $image['url'] ), ); } } $videos = array(); if ( isset( $data['videos'] ) && is_array( $data['videos'] ) ) { foreach ( $data['videos'] as $video ) { if ( ! is_array( $video ) || empty( $video['url'] ) ) { continue; } $videos[] = array( 'kind' => isset( $video['kind'] ) ? sanitize_text_field( (string) $video['kind'] ) : 'video', 'label' => isset( $video['label'] ) ? sanitize_text_field( (string) $video['label'] ) : '', 'width' => isset( $video['width'] ) ? absint( $video['width'] ) : 0, 'height' => isset( $video['height'] ) ? absint( $video['height'] ) : 0, 'size_bytes' => isset( $video['size_bytes'] ) ? absint( $video['size_bytes'] ) : 0, 'url' => esc_url_raw( (string) $video['url'] ), ); } } wp_send_json_success( array( 'name' => isset( $data['name'] ) ? sanitize_text_field( (string) $data['name'] ) : '', 'platform' => isset( $data['platform'] ) ? sanitize_text_field( (string) $data['platform'] ) : '', 'type' => isset( $data['type'] ) ? sanitize_text_field( (string) $data['type'] ) : '', 'images' => $images, 'videos' => $videos, ) ); } add_action( 'wp_ajax_guardafoto_resolve', 'guardafoto_ajax_resolve' ); /** * AJAX: download one resolved image into the Media Library. * * With post_id + set_featured it also assigns the image as the featured * image of that post (requires the edit_post capability for that post) * and returns the featured-image box markup, so the classic editor can * refresh its box without a page reload. * * @return void */ function guardafoto_ajax_import() { guardafoto_ajax_guard(); $strings = guardafoto_admin_strings(); $image_url = isset( $_POST['image_url'] ) ? esc_url_raw( wp_unslash( $_POST['image_url'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- wp_unslash + esc_url_raw applied. $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- wp_unslash + sanitize_text_field applied. $kind = isset( $_POST['kind'] ) ? sanitize_text_field( wp_unslash( $_POST['kind'] ) ) : 'image'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- wp_unslash + sanitize_text_field applied. $platform = isset( $_POST['platform'] ) ? sanitize_text_field( wp_unslash( $_POST['platform'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- wp_unslash + sanitize_text_field applied. $width = isset( $_POST['width'] ) ? absint( $_POST['width'] ) : 0; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- absint applied. $height = isset( $_POST['height'] ) ? absint( $_POST['height'] ) : 0; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- absint applied. $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- absint applied. $set_featured = ! empty( $_POST['set_featured'] ); if ( '' === $image_url ) { wp_send_json_error( array( 'code' => 'bad_url', 'message' => $strings['importFailed'], ) ); } if ( $set_featured ) { if ( ! $post_id || ! get_post( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) { wp_send_json_error( array( 'code' => 'forbidden', 'message' => $strings['forbidden'], ), 403 ); } } require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; $tmp = download_url( $image_url, 120 ); if ( is_wp_error( $tmp ) ) { wp_send_json_error( array( 'code' => 'download_failed', 'message' => $strings['importFailed'] . ' ' . $tmp->get_error_message(), ) ); } $file_size = file_exists( $tmp ) ? (int) filesize( $tmp ) : 0; $max_upload = (int) wp_max_upload_size(); if ( $file_size > 0 && $max_upload > 0 && $file_size > $max_upload ) { @unlink( $tmp ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Best-effort cleanup of our own temp file. wp_send_json_error( array( 'code' => 'too_big', 'message' => $strings['importFailed'], ) ); } $image_info = @getimagesize( $tmp ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Validity probe; failure is handled below. $mime_map = array( 'image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', 'image/gif' => 'gif', ); if ( ! $image_info || ! isset( $image_info['mime'] ) || ! isset( $mime_map[ $image_info['mime'] ] ) ) { @unlink( $tmp ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Best-effort cleanup of our own temp file. wp_send_json_error( array( 'code' => 'not_image', 'message' => $strings['importFailed'], ) ); } $extension = $mime_map[ $image_info['mime'] ]; $base = sanitize_title( trim( $name . ' ' . $kind . ' ' . ( $width > 0 && $height > 0 ? $width . 'x' . $height : '' ) ) ); if ( '' === $base ) { $base = 'guardafoto-image'; } $filename = substr( $base, 0, 80 ) . '.' . $extension; $kind_label = isset( $strings['kinds'][ $kind ] ) ? $strings['kinds'][ $kind ] : $kind; $platform_label = isset( $strings['platforms'][ $platform ] ) ? $strings['platforms'][ $platform ] : $platform; $alt_parts = array_filter( array( $name, $kind_label, $platform_label ) ); $alt = implode( ' · ', $alt_parts ); if ( '' === $alt ) { $alt = 'GuardaFoto'; } $file_array = array( 'name' => $filename, 'tmp_name' => $tmp, ); $attachment_id = media_handle_sideload( $file_array, $post_id, $alt ); if ( is_wp_error( $attachment_id ) ) { if ( file_exists( $tmp ) ) { @unlink( $tmp ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Best-effort cleanup of our own temp file. } wp_send_json_error( array( 'code' => 'sideload_failed', 'message' => $strings['importFailed'] . ' ' . $attachment_id->get_error_message(), ) ); } $attachment_id = (int) $attachment_id; if ( '' !== $name ) { wp_update_post( array( 'ID' => $attachment_id, 'post_title' => $name, ) ); } update_post_meta( $attachment_id, '_wp_attachment_image_alt', $alt ); $featured_set = false; $featured_failed = false; $thumbnail_html = null; if ( $set_featured && $post_id ) { $featured_set = (bool) set_post_thumbnail( $post_id, $attachment_id ); if ( $featured_set ) { require_once ABSPATH . 'wp-admin/includes/post.php'; if ( function_exists( '_wp_post_thumbnail_html' ) ) { $thumbnail_html = _wp_post_thumbnail_html( $attachment_id, $post_id ); } } else { $featured_failed = true; } } wp_send_json_success( array( 'id' => $attachment_id, 'url' => wp_get_attachment_url( $attachment_id ), 'edit_url' => get_edit_post_link( $attachment_id, 'raw' ), 'featured' => $featured_set, 'featured_failed' => $featured_failed, 'thumbnail_html' => $thumbnail_html, ) ); } add_action( 'wp_ajax_guardafoto_import', 'guardafoto_ajax_import' );