PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / front-end / default-fields / upload / upload_helper_functions.php

upload_helper_functions.php in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 4.0.3, at front-end/default-fields/upload/upload_helper_functions.php

671 lines 33.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
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 }
10
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;
24 }
25
26 if ( isset( $request_data['pay_gate'] ) && in_array( $request_data['pay_gate'], array( 'stripe_connect', 'paypal_connect' ), true ) ) {
27 return true;
28 }
29
30 return array_key_exists( $meta, $request_data );
31 }
32
33 /* for a request of a upload from the frontend and no user is logged in don't query for attachments */
34 add_action( 'after_setup_theme', 'wppb_modify_query_attachements_when_not_logged_in' );
35 if( !function_exists( 'wppb_modify_query_attachements_when_not_logged_in' ) ) {
36 function wppb_modify_query_attachements_when_not_logged_in()
37 {
38 if ( strpos(wp_get_referer(), 'wp-admin') === false && !is_user_logged_in() ) {
39 add_action('wp_ajax_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0);
40 add_action('wp_ajax_nopriv_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0);
41 function wppb_wp_ajax_not_loggedin_query_attachments()
42 {
43 wp_send_json_success();
44 }
45 }
46 }
47 }
48
49 /* restrict file types of the upload field functionality */
50 add_filter('wp_handle_upload_prefilter', 'wppb_upload_file_type');
51 if( !function_exists( 'wppb_upload_file_type' ) ) {
52 function wppb_upload_file_type($file)
53 {
54 if( isset( $_POST['wppb_upload'] ) && $_POST['wppb_upload'] == 'true' && isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( $_POST['_wpnonce'] ), 'media-form' ) ) {
55
56 // file size limits.
57 $size = $file['size'];
58 $limit = apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize')));
59 if ( $size > $limit ) {
60 $limit = $limit / ( 1024 * 1024 ) ;
61 $file['error'] = __("Files must be smaller than ", "profile-builder") . $limit . 'MB';
62 }
63
64 if (isset($_POST['meta_name']) && !empty($_POST['meta_name'])) {
65 $meta_name = sanitize_text_field( $_POST['meta_name'] );
66 /*let's get the field details so we can see if we have any file restrictions */
67 $all_fields = apply_filters( 'wppb_form_fields', get_option('wppb_manage_fields'), array( 'context' => 'upload_helper', 'upload_meta_name' => $meta_name ) );
68 if (!empty($all_fields)) {
69 foreach ($all_fields as $field) {
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
82 $allowed_upload_extensions = '';
83
84 if ($field['field'] == 'Upload' && !empty($field['allowed-upload-extensions']))
85 $allowed_upload_extensions = $field['allowed-upload-extensions'];
86 if ($field['field'] == 'Avatar' && !empty($field['allowed-image-extensions'])) {
87 if (trim($field['allowed-image-extensions']) == '.*')
88 $allowed_upload_extensions = '.jpg,.jpeg,.gif,.png,.ico';
89 else
90 $allowed_upload_extensions = $field['allowed-image-extensions'];
91 }
92
93 $ext = strtolower( substr(strrchr($file['name'], '.'), 1) );
94
95 if (!empty($allowed_upload_extensions) && $allowed_upload_extensions != '.*') {
96 $allowed = str_replace('.', '', array_map('trim', explode(",", strtolower( $allowed_upload_extensions))));
97 //first check if the user uploaded the right type
98 if (!in_array($ext, (array)$allowed)) {
99 $file['error'] = __("Sorry, you cannot upload this file type for this field.", 'profile-builder');
100 return $file;
101 }
102 }
103
104 //check if the type is allowed at all by WordPress
105 foreach (get_allowed_mime_types() as $key => $value) {
106 if (strpos($key, $ext) !== false || $key == $ext)
107 return $file;
108 }
109
110 $file['error'] = __("Sorry, you cannot upload this file type for this field.", 'profile-builder');
111
112 break;
113 }
114 }
115 }
116 }
117
118 if (empty($_POST['meta_name']))
119 $file['error'] = __("An error occurred, please try again later.", 'profile-builder');
120 }
121
122 return $file;
123 }
124 }
125
126 /**
127 * Function that performs validation for the simple upload field
128 *
129 * @param $field - simple upload field
130 * @param $upload - data to be uploaded
131 *
132 * @return bool
133 */
134 function wppb_valid_simple_upload( $field, $upload ){
135 $limit = apply_filters( 'wppb_server_max_upload_size_byte_constant', wppb_return_bytes( ini_get( 'upload_max_filesize' ) ) );
136 $allowed_mime_types = get_allowed_mime_types();
137 $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
138 if ( !empty( $all_fields ) ) {
139 foreach ( $all_fields as $form_field ) {
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 }
146 $allowed_upload_extensions = '';
147 if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) {
148 $allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ];
149 }
150 if ( $form_field[ 'field' ] == 'Avatar' ) {
151 if ( trim( $field[ 'allowed-image-extensions' ] ) == '.*' || trim( $field[ 'allowed-image-extensions' ] ) == '' ) {
152 $allowed_upload_extensions = '.jpg,.jpeg,.gif,.png';
153 }
154 else {
155 $allowed_upload_extensions = $form_field[ 'allowed-image-extensions' ];
156 }
157 }
158 if ( !empty( $allowed_upload_extensions ) && $allowed_upload_extensions != '.*' ) {
159 $allowed_upload_extensions = str_replace( '.', '', array_map( 'trim', explode( ",", strtolower( $allowed_upload_extensions ) ) ) );
160 } else {
161 $allowed = true;
162 }
163 if ( empty( $upload['tmp_name'] ) || empty( $upload['name'] ) ) {
164 return false;
165 }
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 !== '' ) {
173 if ( !isset( $allowed ) ){
174 $allowed = in_array( $detected_ext, $allowed_upload_extensions, true );
175 }
176 if ( $upload[ 'size' ] > $limit ){
177 $allowed = false;
178 }
179 return $allowed;
180 }
181 else{
182 return false;
183 }
184 }
185 }
186 }
187 }
188
189 /**
190 * Function that registers intermediate avatar sizes
191 *
192 * @param $field - avatar field
193 *
194 */
195 function wppb_add_avatar_sizes( $field ){
196 if( !empty( $field['avatar-size'] ) )
197 add_image_size( 'wppb-avatar-size-'.$field['avatar-size'], $field['avatar-size'], $field['avatar-size'], true );
198 else
199 add_image_size( 'wppb-avatar-size-100', 100, 100, true );
200
201 add_image_size( 'wppb-avatar-size-64', 64, 64, true );
202 add_image_size( 'wppb-avatar-size-26', 26, 26, true );
203 }
204
205 //Function that registers avatar sizes for userlisting
206 function wppb_userlisting_avatar(){
207 $userlisting_posts = get_posts( array( 'posts_per_page' => -1, 'post_status' =>'publish', 'post_type' => 'wppb-ul-cpt', 'orderby' => 'post_date', 'order' => 'ASC' ) );
208 if( !empty( $userlisting_posts ) ){
209 foreach ( $userlisting_posts as $post ){
210 $this_form_settings = get_post_meta( $post->ID, 'wppb_ul_page_settings', true );
211 $all_userlisting_avatar_size = apply_filters( 'all_userlisting_avatar_size', ( isset( $this_form_settings[0]['avatar-size-all-userlisting'] ) ? (int)$this_form_settings[0]['avatar-size-all-userlisting'] : 100 ) );
212 $single_userlisting_avatar_size = apply_filters( 'single_userlisting_avatar_size', ( isset( $this_form_settings[0]['avatar-size-single-userlisting'] ) ? (int)$this_form_settings[0]['avatar-size-single-userlisting'] : 100 ) );
213
214 add_image_size( 'wppb-avatar-size-'.$all_userlisting_avatar_size, $all_userlisting_avatar_size, $all_userlisting_avatar_size, true );
215 add_image_size( 'wppb-avatar-size-'.$single_userlisting_avatar_size, $single_userlisting_avatar_size, $single_userlisting_avatar_size, true );
216 }
217 }
218 }
219
220 /**
221 * Function that checks if the simple upload field belongs to a repeater field with conditional logic enabled
222 *
223 * @param $field - simple upload field
224 *
225 * @return bool
226 */
227 function wppb_belongs_to_repeater_with_conditional_logic( $field ){
228 $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
229 if ( !empty( $all_fields ) ) {
230 foreach ( $all_fields as $form_field ) {
231 if ( $form_field[ 'field' ] == 'Repeater' && isset( $form_field[ 'conditional-logic-enabled' ] ) && $form_field[ 'conditional-logic-enabled' ] == 'yes' ) {
232 $repeater_group = get_option( $form_field[ 'meta-name' ], 'not_set' );
233 if ( $repeater_group == 'not_set' ) {
234 continue;
235 }
236 else{
237 $repeater_count = count( $repeater_group );
238 for ( $i = 0; $i < $repeater_count; $i++ ){
239 if ( $repeater_group[ $i ][ 'field' ] == 'Upload' && wppb_use_simple_upload_field( $repeater_group[ $i ] ) && isset( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] ) ){
240 $groups = absint( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] );
241 for ( $j = 0; $j <= $groups; $j++ ){
242 $name = $repeater_group[ $i ][ 'meta-name' ];
243 if ( $j != 0 ){
244 $name .= '_' . $j;
245 }
246 if ( $field[ 'meta-name' ] == $name ){
247 return true;
248 }
249 }
250 }
251 }
252 }
253 }
254 }
255 }
256 return false;
257 }
258
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);
270
271 $upload_button = '';
272 $upload_input_id = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'] ) );
273
274 /* container for the image preview (or file ico) and name and file type */
275 if( !empty( $input_value ) ){
276 /* it can hold multiple attachments separated by comma */
277 $values = explode( ',', $input_value );
278 foreach( $values as $value ) {
279 if( !empty( $value ) && is_numeric( $value ) ){
280 $thumbnail = wp_get_attachment_image($value, array(80, 80), true);
281 $file_name = get_the_title($value);
282 $file_type = get_post_mime_type($value);
283 $attachment_url = wp_get_attachment_url($value);
284 $upload_button .= '<div id="' . esc_attr($upload_input_id) . '_info_container" class="upload-field-details" data-attachment_id="' . esc_attr( $value ) . '">';
285 $upload_button .= '<div class="file-thumb">';
286 $upload_button .= "<a href='" . esc_url( $attachment_url ) . "' target='_blank' class='wppb-attachment-link'>" . $thumbnail . "</a>";
287 $upload_button .= '</div>';
288 $upload_button .= '<p><span class="file-name">';
289 $upload_button .= esc_html( $file_name );
290 $upload_button .= '</span><span class="file-type">';
291 $upload_button .= esc_html( $file_type );
292 $upload_button .= '</span>';
293 $upload_button .= '<span class="wppb-remove-upload" tabindex="0">' . apply_filters( 'wppb_upload_button_remove_label', __( 'Remove', 'profile-builder' ) ) . '</span>';
294 $upload_button .= '</p></div>';
295 }
296 }
297 $hide_upload_button = ' style="display:none;"';
298 }
299 else{
300 $hide_upload_button = '';
301 }
302
303 if ( wppb_use_simple_upload_field( $field ) ){
304 //If selected accordingly in form fields, generate a simple upload button
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 ) ) .'"';
306 $upload_button .= $hide_upload_button . '>';
307 $upload_button .= '<p id="p_simple_upload_'. esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) .'"></p>';
308 $limit = apply_filters( 'wppb_server_max_upload_size_byte_constant', wppb_return_bytes( ini_get( 'upload_max_filesize' ) ) );
309 $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
310 if ( !empty( $all_fields ) ) {
311 foreach ( $all_fields as $form_field ) {
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 }
318 $allowed_upload_extensions = '';
319 if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) {
320 $allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ];
321 }
322 if ( $form_field[ 'field' ] == 'Avatar' ) {
323 if ( trim( $field[ 'allowed-image-extensions' ] ) == '.*' || trim( $field[ 'allowed-image-extensions' ] ) == '' ) {
324 $allowed_upload_extensions = '.jpg,.jpeg,.gif,.png';
325 }
326 else {
327 $allowed_upload_extensions = $form_field[ 'allowed-image-extensions' ];
328 }
329 }
330 }
331 if ( !empty( $allowed_upload_extensions ) && $allowed_upload_extensions != '.*' ) {
332 $allowed_extensions = str_replace( '.', '', array_map( 'trim', explode( ",", strtolower( $allowed_upload_extensions ) ) ) );
333 $allowed_extensions = implode( ',', $allowed_extensions );
334 } else {
335 $allowed_extensions = '';
336 }
337 }
338 }
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 ) .'"/>';
341 $allowed_mime_types = get_allowed_mime_types();
342 $allowed_types = '';
343 if ( !empty( $allowed_mime_types ) ) {
344 foreach ($allowed_mime_types as $key => $val){
345 $allowed_types .= $key . '=>' . $val . ',';
346 }
347 }
348 $error_messages = array(
349 'limit_error_message' => __( 'Files must be smaller than ', 'profile-builder' ),
350 'upload_type_error_message' => __( 'Sorry, you cannot upload this file type for this field.', 'profile-builder' ),
351 );
352 $size_limit = array(
353 'size_limit' => $limit
354 );
355 $allowed_wordpress_formats = array(
356 'allowed_wordpress_formats' => $allowed_mime_types
357 );
358 wp_localize_script( 'wppb-upload-script', 'wppb_error_messages', $error_messages );
359 wp_localize_script( 'wppb-upload-script', 'wppb_limit', $size_limit );
360 wp_localize_script( 'wppb-upload-script', 'wppb_allowed_wordpress_formats', $allowed_wordpress_formats );
361 }
362 else{
363 //Otherwise, generate the WordPress upload button
364 $upload_button .= '<a href="#" class="button wppb_upload_button" id="upload_' . esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) . '_button" '.$hide_upload_button.' data-uploader_title="' . $field["field-title"] . '" data-uploader_button_text="'. __( 'Select File', 'profile-builder' ) .'" data-upload_mn="'. $field['meta-name'] .'" data-upload_input="' . esc_attr($upload_input_id) . '"';
365
366 if (is_user_logged_in())
367 $upload_button .= ' data-uploader_logged_in="true"';
368 $upload_button .= ' data-multiple_upload="false"';
369
370 $upload_button .= '>' . apply_filters( 'wppb_upload_button_select_label', __( 'Upload ', 'profile-builder' ) ) . '</a>';
371 }
372
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 ) ) .'"/>';
374 return $upload_button;
375 }
376
377 /**
378 * Function to save an attachment from the simple upload field
379 * @param $field_name
380 * @return string|WP_Error
381 */
382 function wppb_default_fields_save_simple_upload_file( $field_name ) {
383 require_once(ABSPATH . 'wp-admin/includes/file.php');
384 $upload_overrides = array('test_form' => false);
385
386 if( isset( $_FILES[$field_name] ) )
387 $file = wp_handle_upload($_FILES[$field_name], $upload_overrides);
388
389 if (isset($file['error'])) {
390 return new WP_Error('upload_error', $file['error']);
391 }
392 $filename = isset( $_FILES[$field_name]['name'] ) ? sanitize_text_field( $_FILES[$field_name]['name'] ) : '';
393 $wp_filetype = wp_check_filetype($filename, null);
394 $attachment = array(
395 'post_mime_type' => $wp_filetype['type'],
396 'post_title' => $filename,
397 'post_content' => '',
398 'post_status' => 'inherit'
399 );
400 $attachment_id = wp_insert_attachment($attachment, $file['file']);
401 if (!is_wp_error($attachment_id) && is_numeric($attachment_id)) {
402 require_once(ABSPATH . 'wp-admin/includes/image.php');
403 $attachment_data = wp_generate_attachment_metadata($attachment_id, $file['file']);
404 wp_update_attachment_metadata($attachment_id, $attachment_data);
405 return trim($attachment_id);
406 } else {
407 return '';
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
671 }