| @@ -40,30 +40,64 @@ | ||
| 40 | 40 | |
| 41 | 41 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- nonce verified above; settings array recursively sanitized via wp_kses_post_deep(). |
| 42 | 42 | $data_source = empty($_POST['settings']) ? [] : (array) wp_kses_post_deep(wp_unslash($_POST['settings'])); |
| 43 | 43 | |
| 44 | - $base64_image = $data_source['image_data']; | |
| 44 | + $base64_image = isset($data_source['image_data']) ? (string) $data_source['image_data'] : ''; | |
| 45 | 45 | |
| 46 | + // Validate the data URI shape before parsing. | |
| 47 | + if (strpos($base64_image, ';') === false || strpos($base64_image, ',') === false) { | |
| 48 | + wp_send_json_error(__('Invalid image data.', 'adminify')); | |
| 49 | + } | |
| 50 | + | |
| 46 | 51 | // Extract the base64 data (remove the data URI scheme) |
| 47 | 52 | list($type, $data) = explode(';', $base64_image); |
| 48 | 53 | list(, $data) = explode(',', $data); |
| 49 | - $decoded_data = base64_decode($data); | |
| 54 | + $decoded_data = base64_decode($data, true); | |
| 50 | 55 | |
| 51 | - // Define the output file path | |
| 52 | - $upload_dir = wp_upload_dir(); | |
| 53 | - $upload_path = $upload_dir['path'] . '/' . $data_source['image_name']; | |
| 56 | + if ($decoded_data === false) { | |
| 57 | + wp_send_json_error(__('Invalid image data.', 'adminify')); | |
| 58 | + } | |
| 54 | 59 | |
| 55 | - // Save the image to the file | |
| 56 | - if (file_put_contents($upload_path, $decoded_data) === false) { | |
| 57 | - wp_send_json_error('Failed to save the image.'); | |
| 60 | + // Sanitize the filename: strip any path components and enforce an image extension allowlist. | |
| 61 | + $raw_name = isset($data_source['image_name']) ? (string) $data_source['image_name'] : ''; | |
| 62 | + $safe_name = sanitize_file_name(basename($raw_name)); | |
| 63 | + | |
| 64 | + $filetype = wp_check_filetype($safe_name); | |
| 65 | + $allowed = array('jpg', 'jpeg', 'png', 'gif', 'webp'); | |
| 66 | + if (empty($safe_name) || !in_array(strtolower((string) $filetype['ext']), $allowed, true)) { | |
| 67 | + wp_send_json_error(__('Invalid image file type.', 'adminify')); | |
| 58 | 68 | } |
| 59 | 69 | |
| 60 | - // Get the URL of the uploaded image | |
| 61 | - $upload_url = $upload_dir['url'] . '/' . basename($upload_path); | |
| 70 | + // Write the decoded bytes into the uploads dir (handles unique filename + path). | |
| 71 | + $uploaded = wp_upload_bits($safe_name, null, $decoded_data); | |
| 72 | + if (!empty($uploaded['error']) || empty($uploaded['file'])) { | |
| 73 | + wp_send_json_error(__('Failed to save the image.', 'adminify')); | |
| 74 | + } | |
| 62 | 75 | |
| 63 | - wp_send_json_success($upload_url); | |
| 76 | + // Register the file as a Media Library attachment so it behaves like a normal upload. | |
| 77 | + $attachment = array( | |
| 78 | + 'post_mime_type' => $filetype['type'], | |
| 79 | + 'post_title' => sanitize_text_field(pathinfo($safe_name, PATHINFO_FILENAME)), | |
| 80 | + 'post_content' => '', | |
| 81 | + 'post_status' => 'inherit', | |
| 82 | + ); | |
| 64 | 83 | |
| 65 | - wp_die(); | |
| 84 | + $attachment_id = wp_insert_attachment($attachment, $uploaded['file']); | |
| 85 | + if (is_wp_error($attachment_id) || !$attachment_id) { | |
| 86 | + wp_send_json_error(__('Failed to register the image in the media library.', 'adminify')); | |
| 87 | + } | |
| 88 | + | |
| 89 | + // Generate thumbnails/metadata for the attachment. | |
| 90 | + require_once ABSPATH . 'wp-admin/includes/image.php'; | |
| 91 | + $metadata = wp_generate_attachment_metadata($attachment_id, $uploaded['file']); | |
| 92 | + wp_update_attachment_metadata($attachment_id, $metadata); | |
| 93 | + | |
| 94 | + wp_send_json_success( | |
| 95 | + array( | |
| 96 | + 'id' => $attachment_id, | |
| 97 | + 'url' => wp_get_attachment_url($attachment_id), | |
| 98 | + ) | |
| 99 | + ); | |
| 66 | 100 | } |
| 67 | 101 | |
| 68 | 102 | public function validate_before_save($settings) { |
| 69 | 103 | foreach ($settings as $key => $setting) { |
| @@ -88,8 +122,26 @@ | ||
| 88 | 122 | public function text_validation($text) { |
| 89 | 123 | return sanitize_text_field( wp_unslash( $text ?? '' ) ); |
| 90 | 124 | } |
| 91 | 125 | |
| 126 | + // Build the full CSF media field value from an attachment id. | |
| 127 | + public function build_media_value($attachment_id) { | |
| 128 | + $attachment_id = absint($attachment_id); | |
| 129 | + $full = wp_get_attachment_image_src($attachment_id, 'full'); | |
| 130 | + $thumb = wp_get_attachment_image_src($attachment_id, 'thumbnail'); | |
| 131 | + | |
| 132 | + return array( | |
| 133 | + 'id' => $attachment_id, | |
| 134 | + 'url' => $full ? $full[0] : wp_get_attachment_url($attachment_id), | |
| 135 | + 'width' => $full ? $full[1] : '', | |
| 136 | + 'height' => $full ? $full[2] : '', | |
| 137 | + 'thumbnail' => $thumb ? $thumb[0] : '', | |
| 138 | + 'alt' => (string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true), | |
| 139 | + 'title' => get_the_title($attachment_id), | |
| 140 | + 'description' => '', | |
| 141 | + ); | |
| 142 | + } | |
| 143 | + | |
| 92 | 144 | public function pxlbsadminify_save_wizard_data() { |
| 93 | 145 | check_ajax_referer('pxlbsadminify_sw'); |
| 94 | 146 | |
| 95 | 147 | // Security check - only administrators can save wizard settings |
| @@ -116,9 +168,18 @@ | ||
| 116 | 168 | if($validate_settings['admin_ui_logo_type'] === 'text_logo') { |
| 117 | 169 | $settings['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo_text'] = $this->text_validation( $validate_settings['admin_ui_light_mode']['admin_ui_light_logo_text'] ); |
| 118 | 170 | } |
| 119 | 171 | if($validate_settings['admin_ui_logo_type'] === 'image_logo') { |
| 120 | - $settings['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo']['url'] = wp_http_validate_url($validate_settings['admin_ui_light_mode']['admin_ui_light_logo']['url']); | |
| 172 | + $logo_value = $validate_settings['admin_ui_light_mode']['admin_ui_light_logo']; | |
| 173 | + $attachment_id = isset($logo_value['id']) ? absint($logo_value['id']) : 0; | |
| 174 | + | |
| 175 | + if ($attachment_id && wp_attachment_is_image($attachment_id)) { | |
| 176 | + // Rebuild the full media value from the attachment so the CSF | |
| 177 | + // media field has every key it renders (thumbnail drives the preview). | |
| 178 | + $settings['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo'] = $this->build_media_value($attachment_id); | |
| 179 | + } else { | |
| 180 | + $settings['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo']['url'] = wp_http_validate_url($logo_value['url']); | |
| 181 | + } | |
| 121 | 182 | } |
| 122 | 183 | |
| 123 | 184 | } |
| 124 | 185 | |