← All changes
|
front-end/default-fields/upload/upload_helper_functions.php
+345
-55
3.9.6
→
4.0.3
View file →
| @@ -1,34 +1,34 @@ | ||
| 1 | 1 | <?php |
| 2 | -/* Set up upload field for frontend */ | |
| 3 | -/* overwrite the two functions for when an upload is made from the frontend so they don't check for a logged in user */ | |
| 4 | -if( strpos( wp_get_referer(), 'wp-admin' ) === false && isset( $_REQUEST['action'] ) && 'upload-attachment' == $_REQUEST['action'] ){ | |
| 5 | - if( !function_exists( 'check_ajax_referer' ) ){ | |
| 6 | - function check_ajax_referer( ) { | |
| 7 | - return true; | |
| 8 | - } | |
| 2 | +// Exit if accessed directly | |
| 3 | +if ( ! defined( 'ABSPATH' ) ) exit; | |
| 4 | + | |
| 5 | +/** Simple file input when the field is set to it, or the user cannot upload_files. */ | |
| 6 | +function wppb_use_simple_upload_field( $field ) { | |
| 7 | + if ( ! empty( $field['simple-upload'] ) && $field['simple-upload'] === 'yes' ) { | |
| 8 | + return true; | |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | - if( !function_exists( 'auth_redirect' ) ){ | |
| 12 | - function auth_redirect() { | |
| 13 | - return true; | |
| 14 | - } | |
| 11 | + return ! current_user_can( 'upload_files' ); | |
| 12 | +} | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * Whether this request includes a simple-upload for the field. | |
| 16 | + * Checkout and some payment forms post the hidden attachment ID without $_FILES. | |
| 17 | + */ | |
| 18 | +function wppb_simple_upload_was_submitted( $field, $request_data ) { | |
| 19 | + $meta = wppb_handle_meta_name( $field['meta-name'] ); | |
| 20 | + $file_key = 'simple_upload_' . $meta; | |
| 21 | + | |
| 22 | + if ( isset( $_FILES[ $file_key ] ) ) { | |
| 23 | + return true; | |
| 15 | 24 | } |
| 16 | -} | |
| 17 | 25 | |
| 18 | -/* create a fake user with the "upload_posts" capability and assign him to the global $current_user. this is used to bypass the checks for current_user_can('upload_files') in async-upload.php */ | |
| 19 | -add_action( 'current_screen', 'wppb_create_fake_user_when_uploading_and_not_logged_in' ); | |
| 20 | -if( !function_exists( 'wppb_create_fake_user_when_uploading_and_not_logged_in' ) ) { | |
| 21 | - function wppb_create_fake_user_when_uploading_and_not_logged_in() | |
| 22 | - { | |
| 23 | - if ( isset($_REQUEST['action']) && 'upload-attachment' == $_REQUEST['action'] && isset($_REQUEST['wppb_upload']) && 'true' == $_REQUEST['wppb_upload'] ) { | |
| 24 | - if (!is_user_logged_in() || !current_user_can('upload_files') || !current_user_can('edit_posts')) { | |
| 25 | - global $current_user; | |
| 26 | - $current_user = new WP_User(0, 'frontend_uploader'); | |
| 27 | - $current_user->allcaps = array("upload_files" => true, "edit_posts" => true, "edit_others_posts" => true, "edit_pages" => true, "edit_others_pages" => true); | |
| 28 | - } | |
| 29 | - } | |
| 26 | + if ( isset( $request_data['pay_gate'] ) && in_array( $request_data['pay_gate'], array( 'stripe_connect', 'paypal_connect' ), true ) ) { | |
| 27 | + return true; | |
| 30 | 28 | } |
| 29 | + | |
| 30 | + return array_key_exists( $meta, $request_data ); | |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | 33 | /* for a request of a upload from the frontend and no user is logged in don't query for attachments */ |
| 34 | 34 | add_action( 'after_setup_theme', 'wppb_modify_query_attachements_when_not_logged_in' ); |
| @@ -34,9 +34,9 @@ | ||
| 34 | 34 | add_action( 'after_setup_theme', 'wppb_modify_query_attachements_when_not_logged_in' ); |
| 35 | 35 | if( !function_exists( 'wppb_modify_query_attachements_when_not_logged_in' ) ) { |
| 36 | 36 | function wppb_modify_query_attachements_when_not_logged_in() |
| 37 | 37 | { |
| 38 | - if (strpos(wp_get_referer(), 'wp-admin') === false && !is_user_logged_in()) { | |
| 38 | + if ( strpos(wp_get_referer(), 'wp-admin') === false && !is_user_logged_in() ) { | |
| 39 | 39 | add_action('wp_ajax_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0); |
| 40 | 40 | add_action('wp_ajax_nopriv_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0); |
| 41 | 41 | function wppb_wp_ajax_not_loggedin_query_attachments() |
| 42 | 42 | { |
| @@ -50,9 +50,9 @@ | ||
| 50 | 50 | add_filter('wp_handle_upload_prefilter', 'wppb_upload_file_type'); |
| 51 | 51 | if( !function_exists( 'wppb_upload_file_type' ) ) { |
| 52 | 52 | function wppb_upload_file_type($file) |
| 53 | 53 | { |
| 54 | - if( isset( $_POST['wppb_upload'] ) && $_POST['wppb_upload'] == 'true' ) { | |
| 54 | + if( isset( $_POST['wppb_upload'] ) && $_POST['wppb_upload'] == 'true' && isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( $_POST['_wpnonce'] ), 'media-form' ) ) { | |
| 55 | 55 | |
| 56 | 56 | // file size limits. |
| 57 | 57 | $size = $file['size']; |
| 58 | 58 | $limit = apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize'))); |
| @@ -67,9 +67,21 @@ | ||
| 67 | 67 | $all_fields = apply_filters( 'wppb_form_fields', get_option('wppb_manage_fields'), array( 'context' => 'upload_helper', 'upload_meta_name' => $meta_name ) ); |
| 68 | 68 | if (!empty($all_fields)) { |
| 69 | 69 | foreach ($all_fields as $field) { |
| 70 | 70 | if ($field['meta-name'] == $meta_name) { |
| 71 | + | |
| 72 | + // per-field file size limit | |
| 73 | + if ( !empty( $field['max-file-size'] ) && is_numeric( $field['max-file-size'] ) && floatval( $field['max-file-size'] ) > 0 ) { | |
| 74 | + $field_limit = floatval( $field['max-file-size'] ) * 1024 * 1024; | |
| 75 | + $effective_limit = min( $field_limit, $limit ); | |
| 76 | + if ( $size > $effective_limit ) { | |
| 77 | + $file['error'] = __( "Files must be smaller than ", "profile-builder" ) . floatval( $field['max-file-size'] ) . 'MB'; | |
| 78 | + return $file; | |
| 79 | + } | |
| 80 | + } | |
| 81 | + | |
| 71 | 82 | $allowed_upload_extensions = ''; |
| 83 | + | |
| 72 | 84 | if ($field['field'] == 'Upload' && !empty($field['allowed-upload-extensions'])) |
| 73 | 85 | $allowed_upload_extensions = $field['allowed-upload-extensions']; |
| 74 | 86 | if ($field['field'] == 'Avatar' && !empty($field['allowed-image-extensions'])) { |
| 75 | 87 | if (trim($field['allowed-image-extensions']) == '.*') |
| @@ -93,9 +105,12 @@ | ||
| 93 | 105 | foreach (get_allowed_mime_types() as $key => $value) { |
| 94 | 106 | if (strpos($key, $ext) !== false || $key == $ext) |
| 95 | 107 | return $file; |
| 96 | 108 | } |
| 109 | + | |
| 97 | 110 | $file['error'] = __("Sorry, you cannot upload this file type for this field.", 'profile-builder'); |
| 111 | + | |
| 112 | + break; | |
| 98 | 113 | } |
| 99 | 114 | } |
| 100 | 115 | } |
| 101 | 116 | } |
| @@ -122,8 +137,13 @@ | ||
| 122 | 137 | $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) ); |
| 123 | 138 | if ( !empty( $all_fields ) ) { |
| 124 | 139 | foreach ( $all_fields as $form_field ) { |
| 125 | 140 | if ($form_field[ 'meta-name' ] == $field[ 'meta-name' ] ) { |
| 141 | + // apply per-field size limit if set | |
| 142 | + if ( !empty( $form_field['max-file-size'] ) && is_numeric( $form_field['max-file-size'] ) && floatval( $form_field['max-file-size'] ) > 0 ) { | |
| 143 | + $field_limit = floatval( $form_field['max-file-size'] ) * 1024 * 1024; | |
| 144 | + $limit = min( $field_limit, $limit ); | |
| 145 | + } | |
| 126 | 146 | $allowed_upload_extensions = ''; |
| 127 | 147 | if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) { |
| 128 | 148 | $allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ]; |
| 129 | 149 | } |
| @@ -139,23 +159,20 @@ | ||
| 139 | 159 | $allowed_upload_extensions = str_replace( '.', '', array_map( 'trim', explode( ",", strtolower( $allowed_upload_extensions ) ) ) ); |
| 140 | 160 | } else { |
| 141 | 161 | $allowed = true; |
| 142 | 162 | } |
| 143 | - $allowed_by_wordpress = false; | |
| 144 | - foreach ( $allowed_mime_types as $key => $val ){ | |
| 145 | - if ( $val == $upload[ 'type' ] ){ | |
| 146 | - $possible_extensions = explode( '|', $key ); | |
| 147 | - $allowed_by_wordpress = true; | |
| 148 | - } | |
| 163 | + if ( empty( $upload['tmp_name'] ) || empty( $upload['name'] ) ) { | |
| 164 | + return false; | |
| 149 | 165 | } |
| 150 | - if ( isset( $possible_extensions ) && $allowed_by_wordpress == true ){ | |
| 166 | + | |
| 167 | + $checked = wp_check_filetype_and_ext( $upload['tmp_name'], $upload['name'] ); | |
| 168 | + $detected_type = ! empty( $checked['type'] ) ? $checked['type'] : ''; | |
| 169 | + $detected_ext = ! empty( $checked['ext'] ) ? strtolower( $checked['ext'] ) : ''; | |
| 170 | + $allowed_by_wordpress = ( $detected_type !== '' && in_array( $detected_type, $allowed_mime_types, true ) ); | |
| 171 | + | |
| 172 | + if ( $allowed_by_wordpress && $detected_ext !== '' ) { | |
| 151 | 173 | if ( !isset( $allowed ) ){ |
| 152 | - $allowed = false; | |
| 153 | - foreach ( $allowed_upload_extensions as $extension ){ | |
| 154 | - if ( in_array( $extension, $possible_extensions ) ){ | |
| 155 | - $allowed = true; | |
| 156 | - } | |
| 157 | - } | |
| 174 | + $allowed = in_array( $detected_ext, $allowed_upload_extensions, true ); | |
| 158 | 175 | } |
| 159 | 176 | if ( $upload[ 'size' ] > $limit ){ |
| 160 | 177 | $allowed = false; |
| 161 | 178 | } |
| @@ -218,9 +235,9 @@ | ||
| 218 | 235 | } |
| 219 | 236 | else{ |
| 220 | 237 | $repeater_count = count( $repeater_group ); |
| 221 | 238 | for ( $i = 0; $i < $repeater_count; $i++ ){ |
| 222 | - if ( $repeater_group[ $i ][ 'field' ] == 'Upload' && isset( $repeater_group[ $i ][ 'simple-upload' ] ) && $repeater_group[ $i ][ 'simple-upload' ] == 'yes' && isset( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] ) ){ | |
| 239 | + if ( $repeater_group[ $i ][ 'field' ] == 'Upload' && wppb_use_simple_upload_field( $repeater_group[ $i ] ) && isset( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] ) ){ | |
| 223 | 240 | $groups = absint( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] ); |
| 224 | 241 | for ( $j = 0; $j <= $groups; $j++ ){ |
| 225 | 242 | $name = $repeater_group[ $i ][ 'meta-name' ]; |
| 226 | 243 | if ( $j != 0 ){ |
| @@ -238,14 +255,19 @@ | ||
| 238 | 255 | } |
| 239 | 256 | return false; |
| 240 | 257 | } |
| 241 | 258 | |
| 242 | -function wppb_make_upload_button( $field, $input_value, $extra_attr = '' ){ | |
| 243 | - // change the upload limit. This is not functional. | |
| 244 | - // just for display in the upload window. see upload_helper_functions.php for the actual restriction. | |
| 245 | - add_filter('upload_size_limit', function($limit, $u, $p){ | |
| 246 | - return apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize'))); | |
| 247 | - }, 10, 3); | |
| 259 | +function wppb_default_fields_make_upload_button( $field, $input_value, $extra_attr = '' ){ | |
| 260 | + // change the upload limit displayed in the upload window (per-field aware) | |
| 261 | + $per_field_max = $field; | |
| 262 | + add_filter('upload_size_limit', function($wp_limit) use ($per_field_max) { | |
| 263 | + $server_limit = apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize'))); | |
| 264 | + if ( !empty( $per_field_max['max-file-size'] ) && is_numeric( $per_field_max['max-file-size'] ) && floatval( $per_field_max['max-file-size'] ) > 0 ) { | |
| 265 | + $field_limit = floatval( $per_field_max['max-file-size'] ) * 1024 * 1024; | |
| 266 | + return min( $field_limit, $server_limit ); | |
| 267 | + } | |
| 268 | + return $server_limit; | |
| 269 | + }, 10, 1); | |
| 248 | 270 | |
| 249 | 271 | $upload_button = ''; |
| 250 | 272 | $upload_input_id = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'] ) ); |
| 251 | 273 | |
| @@ -258,16 +280,16 @@ | ||
| 258 | 280 | $thumbnail = wp_get_attachment_image($value, array(80, 80), true); |
| 259 | 281 | $file_name = get_the_title($value); |
| 260 | 282 | $file_type = get_post_mime_type($value); |
| 261 | 283 | $attachment_url = wp_get_attachment_url($value); |
| 262 | - $upload_button .= '<div id="' . esc_attr($upload_input_id) . '_info_container" class="upload-field-details" data-attachment_id="' . $value . '">'; | |
| 284 | + $upload_button .= '<div id="' . esc_attr($upload_input_id) . '_info_container" class="upload-field-details" data-attachment_id="' . esc_attr( $value ) . '">'; | |
| 263 | 285 | $upload_button .= '<div class="file-thumb">'; |
| 264 | - $upload_button .= "<a href='{$attachment_url}' target='_blank' class='wppb-attachment-link'>" . $thumbnail . "</a>"; | |
| 286 | + $upload_button .= "<a href='" . esc_url( $attachment_url ) . "' target='_blank' class='wppb-attachment-link'>" . $thumbnail . "</a>"; | |
| 265 | 287 | $upload_button .= '</div>'; |
| 266 | 288 | $upload_button .= '<p><span class="file-name">'; |
| 267 | - $upload_button .= $file_name; | |
| 289 | + $upload_button .= esc_html( $file_name ); | |
| 268 | 290 | $upload_button .= '</span><span class="file-type">'; |
| 269 | - $upload_button .= $file_type; | |
| 291 | + $upload_button .= esc_html( $file_type ); | |
| 270 | 292 | $upload_button .= '</span>'; |
| 271 | 293 | $upload_button .= '<span class="wppb-remove-upload" tabindex="0">' . apply_filters( 'wppb_upload_button_remove_label', __( 'Remove', 'profile-builder' ) ) . '</span>'; |
| 272 | 294 | $upload_button .= '</p></div>'; |
| 273 | 295 | } |
| @@ -277,11 +299,11 @@ | ||
| 277 | 299 | else{ |
| 278 | 300 | $hide_upload_button = ''; |
| 279 | 301 | } |
| 280 | 302 | |
| 281 | - if ( isset( $field[ 'simple-upload' ] ) && $field[ 'simple-upload' ] == 'yes' ){ | |
| 303 | + if ( wppb_use_simple_upload_field( $field ) ){ | |
| 282 | 304 | //If selected accordingly in form fields, generate a simple upload button |
| 283 | - $upload_button .= '<input type="file" id="upload_' . esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) . '_button" name="simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'"'; | |
| 305 | + $upload_button .= '<input type="file" id="upload_' . esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) . '_button" class="wppb_simple_upload" data-field_type="'. esc_attr( $field['field'] ) .'" name="simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'"'; | |
| 284 | 306 | $upload_button .= $hide_upload_button . '>'; |
| 285 | 307 | $upload_button .= '<p id="p_simple_upload_'. esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) .'"></p>'; |
| 286 | 308 | $limit = apply_filters( 'wppb_server_max_upload_size_byte_constant', wppb_return_bytes( ini_get( 'upload_max_filesize' ) ) ); |
| 287 | 309 | $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) ); |
| @@ -287,8 +309,13 @@ | ||
| 287 | 309 | $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) ); |
| 288 | 310 | if ( !empty( $all_fields ) ) { |
| 289 | 311 | foreach ( $all_fields as $form_field ) { |
| 290 | 312 | if ($form_field[ 'meta-name' ] == $field[ 'meta-name' ] ) { |
| 313 | + // apply per-field size limit if set | |
| 314 | + if ( !empty( $form_field['max-file-size'] ) && is_numeric( $form_field['max-file-size'] ) && floatval( $form_field['max-file-size'] ) > 0 ) { | |
| 315 | + $field_limit = floatval( $form_field['max-file-size'] ) * 1024 * 1024; | |
| 316 | + $limit = min( $field_limit, $limit ); | |
| 317 | + } | |
| 291 | 318 | $allowed_upload_extensions = ''; |
| 292 | 319 | if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) { |
| 293 | 320 | $allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ]; |
| 294 | 321 | } |
| @@ -308,9 +335,10 @@ | ||
| 308 | 335 | $allowed_extensions = ''; |
| 309 | 336 | } |
| 310 | 337 | } |
| 311 | 338 | } |
| 312 | - $upload_button .= '<input id="allowed_extensions_simple_upload_'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="allowed_extensions_simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. $allowed_extensions .'"/>'; | |
| 339 | + $upload_button .= '<input id="allowed_extensions_simple_upload_'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="allowed_extensions_simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. esc_attr( $allowed_extensions ) .'"/>'; | |
| 340 | + $upload_button .= '<input id="size_limit_simple_upload_'. esc_attr( $upload_input_id ) .'" type="hidden" name="size_limit_simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. esc_attr( $limit ) .'"/>'; | |
| 313 | 341 | $allowed_mime_types = get_allowed_mime_types(); |
| 314 | 342 | $allowed_types = ''; |
| 315 | 343 | if ( !empty( $allowed_mime_types ) ) { |
| 316 | 344 | foreach ($allowed_mime_types as $key => $val){ |
| @@ -341,9 +369,9 @@ | ||
| 341 | 369 | |
| 342 | 370 | $upload_button .= '>' . apply_filters( 'wppb_upload_button_select_label', __( 'Upload ', 'profile-builder' ) ) . '</a>'; |
| 343 | 371 | } |
| 344 | 372 | |
| 345 | - $upload_button .= '<input id="'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. $input_value .'"/>'; | |
| 373 | + $upload_button .= '<input id="'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. esc_attr( wp_unslash( $input_value ) ) .'"/>'; | |
| 346 | 374 | return $upload_button; |
| 347 | 375 | } |
| 348 | 376 | |
| 349 | 377 | /** |
| @@ -350,9 +378,9 @@ | ||
| 350 | 378 | * Function to save an attachment from the simple upload field |
| 351 | 379 | * @param $field_name |
| 352 | 380 | * @return string|WP_Error |
| 353 | 381 | */ |
| 354 | -function wppb_save_simple_upload_file ( $field_name ){ | |
| 382 | +function wppb_default_fields_save_simple_upload_file( $field_name ) { | |
| 355 | 383 | require_once(ABSPATH . 'wp-admin/includes/file.php'); |
| 356 | 384 | $upload_overrides = array('test_form' => false); |
| 357 | 385 | |
| 358 | 386 | if( isset( $_FILES[$field_name] ) ) |
| @@ -377,5 +405,267 @@ | ||
| 377 | 405 | return trim($attachment_id); |
| 378 | 406 | } else { |
| 379 | 407 | return ''; |
| 380 | 408 | } |
| 409 | +} | |
| 410 | + | |
| 411 | +/** | |
| 412 | + * Converts a legacy file URL stored in user meta (versions that predate attachment IDs) | |
| 413 | + * into an attachment owned by the user and stores the new ID in its place. | |
| 414 | + * | |
| 415 | + * The URL must resolve to an existing file inside the uploads directory with an allowed | |
| 416 | + * mime type; anything else is discarded. Only call this with a value read from user meta, | |
| 417 | + * never with request data, so that rendering a field cannot persist attacker-controlled input. | |
| 418 | + * | |
| 419 | + * @param string $file_url Legacy file URL read from user meta. | |
| 420 | + * @param array $field Field definition array (must contain 'meta-name'). | |
| 421 | + * @param int $user_id User the attachment and meta belong to. | |
| 422 | + * | |
| 423 | + * @return int|string Attachment ID, or '' when the URL could not be converted. | |
| 424 | + */ | |
| 425 | +function wppb_legacy_file_url_to_attachment( $file_url, $field, $user_id ) { | |
| 426 | + $wp_upload_dir = wp_upload_dir(); | |
| 427 | + $base_dir = realpath( $wp_upload_dir['basedir'] ); | |
| 428 | + $file_path = str_replace( $wp_upload_dir['baseurl'], $wp_upload_dir['basedir'], $file_url ); | |
| 429 | + $file_path = is_file( $file_path ) ? realpath( $file_path ) : false; | |
| 430 | + | |
| 431 | + if ( ! $base_dir || ! $file_path ) { | |
| 432 | + return ''; | |
| 433 | + } | |
| 434 | + | |
| 435 | + $base_dir = trailingslashit( wp_normalize_path( $base_dir ) ); | |
| 436 | + $file_path = wp_normalize_path( $file_path ); | |
| 437 | + | |
| 438 | + if ( strpos( $file_path, $base_dir ) !== 0 ) { | |
| 439 | + return ''; | |
| 440 | + } | |
| 441 | + | |
| 442 | + $file_type = wp_check_filetype( basename( $file_path ), null ); | |
| 443 | + if ( empty( $file_type['type'] ) ) { | |
| 444 | + return ''; | |
| 445 | + } | |
| 446 | + | |
| 447 | + $attachment_id = wp_insert_attachment( array( | |
| 448 | + 'guid' => trailingslashit( $wp_upload_dir['baseurl'] ) . substr( $file_path, strlen( $base_dir ) ), | |
| 449 | + 'post_mime_type' => $file_type['type'], | |
| 450 | + 'post_title' => sanitize_text_field( preg_replace( '/\.[^.]+$/', '', basename( $file_path ) ) ), | |
| 451 | + 'post_content' => '', | |
| 452 | + 'post_status' => 'inherit', | |
| 453 | + 'post_author' => $user_id, | |
| 454 | + ), $file_path ); | |
| 455 | + | |
| 456 | + if ( empty( $attachment_id ) || is_wp_error( $attachment_id ) ) { | |
| 457 | + return ''; | |
| 458 | + } | |
| 459 | + | |
| 460 | + // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it. | |
| 461 | + require_once ABSPATH . 'wp-admin/includes/image.php'; | |
| 462 | + wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file_path ) ); | |
| 463 | + update_user_meta( $user_id, $field['meta-name'], $attachment_id ); | |
| 464 | + | |
| 465 | + return $attachment_id; | |
| 466 | +} | |
| 467 | + | |
| 468 | +// Deferred to plugins_loaded so older Profile Builder Pro versions (which declare | |
| 469 | +// wppb_verify_attachment_id unconditionally during their own file load) win the | |
| 470 | +// declaration race and our function_exists guard then skips — avoiding a fatal. | |
| 471 | +add_action( 'plugins_loaded', 'wppb_register_attachment_ownership_helpers', 20 ); | |
| 472 | +function wppb_register_attachment_ownership_helpers() { | |
| 473 | + | |
| 474 | + /** | |
| 475 | + * Verifies if an attachment either doesn't exist or already belongs to the user. | |
| 476 | + * Used for IDOR protection on both Upload and Avatar fields. | |
| 477 | + * | |
| 478 | + * @param string|int $attachment_id The attachment post ID to verify. | |
| 479 | + * @param int|null $user_id The user ID to check ownership against. | |
| 480 | + * | |
| 481 | + * @return bool True if the attachment is valid for this user, false otherwise. | |
| 482 | + */ | |
| 483 | + if ( !function_exists( 'wppb_verify_attachment_id' ) ) { | |
| 484 | + function wppb_verify_attachment_id( $attachment_id, $user_id = null ) { | |
| 485 | + if ( $attachment_id !== '' && is_numeric( $attachment_id ) ) { | |
| 486 | + $attachment = get_post( absint( trim( $attachment_id ) ) ); | |
| 487 | + if ( $attachment && $attachment->post_type === 'attachment' ) { | |
| 488 | + | |
| 489 | + // Get current user info for admin bypass checks | |
| 490 | + $current_user_id = get_current_user_id(); | |
| 491 | + $current_user = $current_user_id ? get_userdata( $current_user_id ) : null; | |
| 492 | + $is_admin = $current_user && current_user_can( 'manage_options' ); | |
| 493 | + | |
| 494 | + if ( $user_id ) { | |
| 495 | + // Allow admins to upload files for users | |
| 496 | + if ( $is_admin ) { | |
| 497 | + return true; | |
| 498 | + } | |
| 499 | + // The attachment is claimable when it already belongs to the target | |
| 500 | + // user, or to the user performing the request. An author-less | |
| 501 | + // attachment (post_author == 0) is only claimable by an | |
| 502 | + // unauthenticated request (e.g. a visitor registering, whose upload | |
| 503 | + // has no author yet). This prevents an authenticated user from | |
| 504 | + // claiming (IDOR) an author-0 attachment created by someone else's | |
| 505 | + // anonymous/nopriv upload. | |
| 506 | + if ( $attachment->post_author == $user_id | |
| 507 | + || ( $current_user_id && $attachment->post_author == $current_user_id ) | |
| 508 | + || ( 0 === (int) $current_user_id && 0 === (int) $attachment->post_author ) ) { | |
| 509 | + return true; | |
| 510 | + } | |
| 511 | + } else { | |
| 512 | + // If no user ID is provided, check if current user is admin | |
| 513 | + if ( $is_admin ) { | |
| 514 | + return true; | |
| 515 | + } | |
| 516 | + // Without an explicit target user, an authenticated user may only | |
| 517 | + // reference an attachment they already own; an author-less | |
| 518 | + // attachment is only claimable by an unauthenticated request. | |
| 519 | + if ( ( $current_user_id && $attachment->post_author == $current_user_id ) | |
| 520 | + || ( 0 === (int) $current_user_id && 0 === (int) $attachment->post_author ) ) { | |
| 521 | + return true; | |
| 522 | + } | |
| 523 | + } | |
| 524 | + } | |
| 525 | + } | |
| 526 | + return false; | |
| 527 | + } | |
| 528 | + } | |
| 529 | + | |
| 530 | + /** | |
| 531 | + * Validates attachment ownership and updates the user meta and post author. | |
| 532 | + * Used for IDOR-safe saving on both Upload and Avatar fields. | |
| 533 | + * | |
| 534 | + * @param string|int $attachment_id The attachment post ID. | |
| 535 | + * @param array $field The field definition array (must contain 'meta-name'). | |
| 536 | + * @param int $user_id The user ID to save for. | |
| 537 | + */ | |
| 538 | + if ( !function_exists( 'wppb_save_attachment_id' ) ) { | |
| 539 | + function wppb_save_attachment_id( $attachment_id, $field, $user_id ) { | |
| 540 | + // Verify that the attachment either doesn't exist or already belongs to the user | |
| 541 | + if ( wppb_verify_attachment_id( $attachment_id, $user_id ) ) { | |
| 542 | + update_user_meta( $user_id, $field['meta-name'], absint( $attachment_id ) ); | |
| 543 | + wp_update_post( array( | |
| 544 | + 'ID' => absint( trim( $attachment_id ) ), | |
| 545 | + 'post_author' => $user_id | |
| 546 | + ) ); | |
| 547 | + } else { | |
| 548 | + update_user_meta( $user_id, $field['meta-name'], '' ); | |
| 549 | + } | |
| 550 | + } | |
| 551 | + } | |
| 552 | +} | |
| 553 | + | |
| 554 | +/** | |
| 555 | + * Resolves a simple-upload AJAX `name` parameter to a configured form field. | |
| 556 | + * | |
| 557 | + * @param string $post_name Sanitized value of $_POST['name'] from the AJAX request. | |
| 558 | + * @param string|array $field_type Expected field type(s), e.g. 'Avatar' or 'Upload'. | |
| 559 | + * | |
| 560 | + * @return array|false Field definition array, or false when not found or not simple-upload. | |
| 561 | + */ | |
| 562 | +function wppb_resolve_simple_upload_ajax_field( $post_name, $field_type ) { | |
| 563 | + if ( empty( $post_name ) ) { | |
| 564 | + return false; | |
| 565 | + } | |
| 566 | + | |
| 567 | + $field_types = is_array( $field_type ) ? $field_type : array( $field_type ); | |
| 568 | + $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'simple_upload_ajax', 'upload_post_name' => $post_name ) ); | |
| 569 | + | |
| 570 | + if ( empty( $all_fields ) ) { | |
| 571 | + return false; | |
| 572 | + } | |
| 573 | + | |
| 574 | + foreach ( $all_fields as $field ) { | |
| 575 | + if ( ! in_array( $field['field'], $field_types, true ) ) { | |
| 576 | + continue; | |
| 577 | + } | |
| 578 | + if ( ! wppb_use_simple_upload_field( $field ) ) { | |
| 579 | + continue; | |
| 580 | + } | |
| 581 | + | |
| 582 | + $field_slug = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ); | |
| 583 | + if ( $field_slug === $post_name ) { | |
| 584 | + return $field; | |
| 585 | + } | |
| 586 | + } | |
| 587 | + | |
| 588 | + // The field was not found among the top-level form fields. Repeater fields store | |
| 589 | + // their inner Upload fields in a separate option keyed by the repeater's | |
| 590 | + // meta-name, so those fields are never part of the wppb_manage_fields list scanned | |
| 591 | + // above. Scan the repeater groups as well, otherwise Simple Upload inside a | |
| 592 | + // Repeater field is silently rejected (the lookup fails and the file input clears). | |
| 593 | + return wppb_resolve_simple_upload_ajax_field_in_repeater( $post_name, $field_types, $all_fields ); | |
| 594 | +} | |
| 595 | + | |
| 596 | +/** | |
| 597 | + * Resolves a simple-upload AJAX `name` parameter to an Upload field nested inside a | |
| 598 | + * Repeater field. | |
| 599 | + * | |
| 600 | + * Repeater sub-fields are stored unindexed in an option keyed by the repeater's | |
| 601 | + * meta-name. On the front-end each group posts either "<slug>" (the first group) or | |
| 602 | + * "<slug>_N" (the Nth extra group), where <slug> is the dash-normalized wck slug of | |
| 603 | + * the inner field's meta-name. | |
| 604 | + * | |
| 605 | + * @param string $post_name Sanitized value of $_POST['name'] from the AJAX request. | |
| 606 | + * @param array $field_types Expected field type(s), e.g. array( 'Upload' ). | |
| 607 | + * @param array $all_fields The already-resolved top-level form fields. | |
| 608 | + * | |
| 609 | + * @return array|false Inner field definition array, or false when not found. | |
| 610 | + */ | |
| 611 | +function wppb_resolve_simple_upload_ajax_field_in_repeater( $post_name, $field_types, $all_fields ) { | |
| 612 | + foreach ( $all_fields as $form_field ) { | |
| 613 | + if ( empty( $form_field['field'] ) || $form_field['field'] !== 'Repeater' ) { | |
| 614 | + continue; | |
| 615 | + } | |
| 616 | + | |
| 617 | + $repeater_group = get_option( $form_field['meta-name'], 'not_set' ); | |
| 618 | + if ( $repeater_group === 'not_set' || ! is_array( $repeater_group ) ) { | |
| 619 | + continue; | |
| 620 | + } | |
| 621 | + | |
| 622 | + foreach ( $repeater_group as $inner_field ) { | |
| 623 | + if ( empty( $inner_field['field'] ) || ! in_array( $inner_field['field'], $field_types, true ) ) { | |
| 624 | + continue; | |
| 625 | + } | |
| 626 | + if ( ! wppb_use_simple_upload_field( $inner_field ) ) { | |
| 627 | + continue; | |
| 628 | + } | |
| 629 | + | |
| 630 | + $base_slug = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $inner_field['meta-name'], $inner_field ) ); | |
| 631 | + if ( $base_slug === $post_name || preg_match( '/^' . preg_quote( $base_slug, '/' ) . '_[0-9]+$/', $post_name ) ) { | |
| 632 | + return $inner_field; | |
| 633 | + } | |
| 634 | + } | |
| 635 | + } | |
| 636 | + | |
| 637 | + return false; | |
| 638 | +} | |
| 639 | + | |
| 640 | +function wppb_check_that_field_is_defined( $meta_name, $field_types = array() ){ | |
| 641 | + | |
| 642 | + if( empty( $meta_name ) ) | |
| 643 | + return false; | |
| 644 | + | |
| 645 | + $defined_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $meta_name ) ); | |
| 646 | + | |
| 647 | + if( empty( $defined_fields ) ) | |
| 648 | + return false; | |
| 649 | + else { | |
| 650 | + | |
| 651 | + if( empty( $field_types ) ){ | |
| 652 | + foreach( $defined_fields as $field ){ | |
| 653 | + | |
| 654 | + if( $field['meta-name'] == $meta_name ) | |
| 655 | + return true; | |
| 656 | + | |
| 657 | + } | |
| 658 | + } else { | |
| 659 | + foreach( $defined_fields as $field ){ | |
| 660 | + | |
| 661 | + if( in_array( $field['field'], $field_types ) && $field['meta-name'] == $meta_name ) | |
| 662 | + return true; | |
| 663 | + | |
| 664 | + } | |
| 665 | + } | |
| 666 | + | |
| 667 | + } | |
| 668 | + | |
| 669 | + return false; | |
| 670 | + | |
| 381 | 671 | } |