Skip to content

Commit 777cbc0

Browse files
authored
release: fixes
- Fixed an issue where PPOM meta fields were not functioning correctly inside a popup. - Fixed an issue where reordering options in the Select field was not working, with changes not being saved. - Fixed an issue where file previews were not visible when uploading large images, displaying a folder icon instead of the image. - Fixed an issue where all images were selected by default when the Min Image Select option was set to 1. - Fixed an issue where Image Dropdown (Image Select) fields did not appear in the conditional logic settings.
2 parents 624f734 + 83d042e commit 777cbc0

File tree

6 files changed

+283
-127
lines changed

6 files changed

+283
-127
lines changed

inc/admin.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,6 @@ function ppom_admin_save_form_meta() {
275275
wp_send_json( $resp );
276276
}
277277

278-
$_REQUEST['ppom'] = is_array( $_REQUEST['ppom'] ) ? $_REQUEST['ppom'] : json_decode( wp_unslash( $_REQUEST['ppom'] ), true );
279-
280278
global $wpdb;
281279

282280
extract( $_REQUEST );
@@ -285,6 +283,12 @@ function ppom_admin_save_form_meta() {
285283
$aviary_api_key = 'NA';
286284
$show_cart_thumb = 'NA';
287285

286+
if ( is_string( $_REQUEST['ppom'] ) ) {
287+
$ppom_encoded = $_REQUEST['ppom'];
288+
parse_str( $ppom_encoded, $ppom_decoded);
289+
$_REQUEST['ppom'] = $ppom_decoded['ppom'];
290+
}
291+
288292
$ppom_meta = ( isset($_REQUEST['ppom_meta']) ? $_REQUEST['ppom_meta'] : isset($_REQUEST['ppom']) ) ? $_REQUEST['ppom'] : '';
289293

290294
if ( empty( $ppom_meta ) ) {
@@ -358,6 +362,11 @@ function( $pm ) {
358362

359363

360364
$ppom_id = $wpdb->insert_id;
365+
if ( is_string( $ppom ) ) {
366+
$ppom_encoded = $ppom;
367+
parse_str( $ppom_encoded, $ppom_decoded);
368+
$ppom = $ppom_decoded['ppom'];
369+
}
361370

362371
$product_meta = apply_filters( 'ppom_meta_data_saving', (array) $ppom, $ppom_id );
363372
$product_meta = ppom_sanitize_array_data( $product_meta );
@@ -450,10 +459,14 @@ function ppom_admin_update_form_meta() {
450459

451460
wp_send_json( $resp );
452461
}
453-
$_REQUEST['ppom'] = is_array( $_REQUEST['ppom'] ) ? $_REQUEST['ppom'] : json_decode( wp_unslash( $_REQUEST['ppom'] ), true );
454-
455462
global $wpdb;
456463

464+
if ( is_string( $_REQUEST['ppom'] ) ) {
465+
$ppom_encoded = $_REQUEST['ppom'];
466+
parse_str( $ppom_encoded, $ppom_decoded);
467+
$_REQUEST['ppom'] = $ppom_decoded['ppom'];
468+
}
469+
457470
$ppom_meta = isset( $_REQUEST['ppom_meta'] ) ? $_REQUEST['ppom_meta'] : $_REQUEST['ppom'];
458471
$product_meta = apply_filters( 'ppom_meta_data_saving', (array) $ppom_meta, $productmeta_id );
459472
$product_meta = ppom_sanitize_array_data( $product_meta );

inc/files.php

Lines changed: 107 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,66 @@ function ppom_create_thumb_for_meta( $file_name, $product_id, $cropped = false,
131131
return apply_filters( 'ppom_meta_file_thumb', $ppom_html, $file_name, $product_id );
132132
}
133133

134+
/**
135+
* Create a new file name that contains a unique string.
136+
*
137+
* @param string $file_name The file name.
138+
* @param string $file_ext The file extension.
139+
*
140+
* @return string The new file name.
141+
*/
142+
function ppom_create_unique_file_name( $file_name, $file_ext ) {
143+
return $file_name . "." . base64_encode( substr( wp_hash_password( $file_name ), 0, 8 ) ) . "." . $file_ext;
144+
}
145+
146+
final class UploadFileErrors {
147+
const OPEN_INPUT = 'open_input';
148+
const OPEN_OUTPUT = 'open_output';
149+
const MISSING_TEMP_FILE = 'missing_temp_file';
150+
const OPEN_DIR = 'open_dir';
151+
152+
static function get_message_response( $error_slug ) {
153+
$msg = array(
154+
self::OPEN_INPUT => '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}',
155+
self::OPEN_OUTPUT => '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}',
156+
self::MISSING_TEMP_FILE => '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}',
157+
self::OPEN_DIR => '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}',
158+
);
159+
160+
return isset( $msg[$error_slug] ) ? $msg[$error_slug] : false;
161+
}
162+
}
163+
164+
/**
165+
* Move the content of the file to read to the given ppom file chunk.
166+
*
167+
* @param string $file_path_to_read The file to read.
168+
* @param string $ppom_chunk_file_path The chunk file to write.
169+
* @param string $mode The writing mode for the chunk file.
170+
*
171+
* @return false|string The error.
172+
*/
173+
function ppom_create_chunk_file( $file_path_to_read, $ppom_chunk_file_path, $mode ) {
174+
$chunk_file = fopen( $ppom_chunk_file_path, $mode );
175+
if ( $chunk_file ) {
176+
// Read binary input stream and append it to temp file
177+
$temp_file = fopen( $file_path_to_read, 'rb' );
178+
179+
if ( $temp_file ) {
180+
while ( $buff = fread( $temp_file, 4096 ) ) {
181+
fwrite( $chunk_file, $buff );
182+
}
183+
} else {
184+
return UploadFileErrors::OPEN_INPUT;
185+
}
186+
fclose( $temp_file );
187+
fclose( $chunk_file );
188+
} else {
189+
return UploadFileErrors::OPEN_OUTPUT;
190+
}
191+
192+
return false;
193+
}
134194

135195
function ppom_upload_file() {
136196

@@ -140,7 +200,7 @@ function ppom_upload_file() {
140200
header( 'Cache-Control: post-check=0, pre-check=0', false );
141201
header( 'Pragma: no-cache' );
142202

143-
$ppom_nonce = $_REQUEST['ppom_nonce'];
203+
$ppom_nonce = sanitize_key( $_REQUEST['ppom_nonce'] );
144204
$file_upload_nonce_action = 'ppom_uploading_file_action';
145205
if ( ! wp_verify_nonce( $ppom_nonce, $file_upload_nonce_action ) && apply_filters( 'ppom_verify_upload_file', true ) ) {
146206
$response ['status'] = 'error';
@@ -200,14 +260,17 @@ function ppom_upload_file() {
200260
// Get parameters
201261
$chunk = isset( $_REQUEST ['chunk'] ) ? intval( $_REQUEST ['chunk'] ) : 0;
202262
$chunks = isset( $_REQUEST ['chunks'] ) ? intval( $_REQUEST ['chunks'] ) : 0;
263+
203264
// $file_name = isset ( $_REQUEST ["name"] ) ? sanitize_file_name($_REQUEST ["name"]) : '';
204265

205266
$file_path_thumb = $file_dir_path . 'thumbs';
206267
$file_name = wp_unique_filename( $file_path_thumb, $file_name );
207268
$file_name = strtolower( $file_name );
208269
$file_ext = pathinfo( $file_name, PATHINFO_EXTENSION );
209-
$unique_hash = substr( hash( 'sha256', wp_generate_password( 8, false, false ) ), 0, 8 );
210-
$file_name = str_replace( ".$file_ext", ".$unique_hash.$file_ext", $file_name );
270+
$original_name = $file_name;
271+
$original_name = str_replace(".$file_ext", "", $original_name);
272+
$file_hash = substr( hash('haval192,5', $file_name), 0, 8 ) . '-' . $ppom_nonce;
273+
$file_name = str_replace( ".$file_ext", ".$file_hash.$file_ext", $file_name );
211274
$file_path = $file_dir_path . $file_name;
212275

213276
// Make sure the fileName is unique but only if chunking is disabled
@@ -226,88 +289,72 @@ function ppom_upload_file() {
226289
}
227290

228291
// Remove old temp files
229-
if ( $cleanupTargetDir && is_dir( $file_dir_path ) && ( $dir = opendir( $file_dir_path ) ) ) {
292+
if ( is_dir( $file_dir_path ) && ( $dir = opendir( $file_dir_path ) ) ) {
230293
while ( ( $file = readdir( $dir ) ) !== false ) {
231-
$tmpfilePath = $file_dir_path . $file;
294+
$tmp_file_path = $file_dir_path . $file;
232295

233296
// Remove temp file if it is older than the max age and is not the current file
234-
if ( preg_match( '/\.part$/', $file ) && ( filemtime( $tmpfilePath ) < time() - $maxFileAge ) && ( $tmpfilePath != "{$file_path}.part" ) ) {
235-
@unlink( $tmpfilePath );
297+
if (
298+
preg_match( '/\.part$/', $file ) &&
299+
( filemtime( $tmp_file_path ) < time() - $maxFileAge ) &&
300+
( $tmp_file_path != "$file_path.part" )
301+
) {
302+
@unlink( $tmp_file_path );
236303
}
237304
}
238305

239306
closedir( $dir );
240307
} else {
241-
die( '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}' );
308+
die( UploadFileErrors::get_message_response( UploadFileErrors::MISSING_TEMP_FILE ) );
242309
}
243310

244-
311+
$http_content_type = '';
245312
// Look for the content type header
246313
if ( isset( $_SERVER ['HTTP_CONTENT_TYPE'] ) ) {
247-
$contentType = $_SERVER ['HTTP_CONTENT_TYPE'];
314+
$http_content_type = $_SERVER ['HTTP_CONTENT_TYPE'];
248315
}
249316

250317
if ( isset( $_SERVER ['CONTENT_TYPE'] ) ) {
251-
$contentType = $_SERVER ['CONTENT_TYPE'];
318+
$http_content_type = $_SERVER ['CONTENT_TYPE'];
252319
}
253320

254-
// Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
255-
if ( strpos( $contentType, 'multipart' ) !== false ) {
256-
if ( isset( $_FILES ['file'] ['tmp_name'] ) && is_uploaded_file( $_FILES ['file'] ['tmp_name'] ) ) {
257-
// Open temp file
258-
$out = fopen( "{$file_path}.part", $chunk == 0 ? 'wb' : 'ab' );
259-
if ( $out ) {
260-
// Read binary input stream and append it to temp file
261-
$in = fopen( sanitize_text_field( $_FILES ['file'] ['tmp_name'] ), 'rb' );
262-
263-
if ( $in ) {
264-
while ( $buff = fread( $in, 4096 ) ) {
265-
fwrite( $out, $buff );
266-
}
267-
} else {
268-
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
269-
}
270-
fclose( $in );
271-
fclose( $out );
272-
@unlink( sanitize_text_field( $_FILES ['file'] ['tmp_name'] ) );
273-
} else {
274-
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
275-
}
276-
} else {
277-
die( '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}' );
278-
}
279-
} else {
280-
// Open temp file
281-
$out = fopen( "{$file_path}.part", $chunk == 0 ? 'wb' : 'ab' );
282-
if ( $out ) {
283-
// Read binary input stream and append it to temp file
284-
$in = fopen( 'php://input', 'rb' );
285-
286-
if ( $in ) {
287-
while ( $buff = fread( $in, 4096 ) ) {
288-
fwrite( $out, $buff );
289-
}
290-
} else {
291-
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
292-
}
321+
$temp_file_name = isset( $_FILES['file']['tmp_name'] ) ? realpath( $_FILES['file']['tmp_name'] ) : '';
322+
$is_multipart = ! empty( $http_content_type ) && false !== strpos( $http_content_type, 'multipart' );
293323

294-
fclose( $in );
295-
fclose( $out );
296-
} else {
297-
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
298-
}
324+
if (
325+
$is_multipart &&
326+
( empty( $temp_file_name ) || ! is_uploaded_file( $temp_file_name ) )
327+
) {
328+
die( UploadFileErrors::get_message_response( UploadFileErrors::MISSING_TEMP_FILE ) );
299329
}
300330

301-
// Check if file has been uploaded
302-
if ( ! $chunks || $chunk == $chunks - 1 ) {
303-
// Strip the temp .part suffix off
304-
rename( "{$file_path}.part", $file_path );
331+
$chunk_file_path = "$file_path.part";
332+
$uploaded_file_path_to_read = $is_multipart ? $temp_file_name : 'php://input';
333+
334+
$error = ppom_create_chunk_file( $uploaded_file_path_to_read, $chunk_file_path, $chunk == 0 ? 'wb' : 'ab' );
335+
336+
if ( $is_multipart ) {
337+
@unlink( $temp_file_name );
338+
}
339+
340+
if ( $error ) {
341+
die( UploadFileErrors::get_message_response( $error ) );
342+
}
343+
344+
// Check if the file has been uploaded completely.
345+
if ( ! $chunks || $chunk === $chunks - 1 ) {
346+
347+
// Give a unique name to prevent name collisions.
348+
$file_name = ppom_create_unique_file_name( $original_name, $file_ext );
349+
$unique_file_path = $file_dir_path . $file_name;
350+
351+
rename( $chunk_file_path, $unique_file_path );
352+
$file_path = $unique_file_path;
305353

306354
$product_id = intval( $_REQUEST['product_id'] );
307355
$data_name = sanitize_key( $_REQUEST['data_name'] );
308356
$file_meta = ppom_get_field_meta_by_dataname( $product_id, $data_name );
309357

310-
311358
// making thumb if images
312359
if ( ppom_is_file_image( $file_path ) ) {
313360

js/admin/ppom-admin.js

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22
"use strict";
33

44
const FIELD_COMPATIBLE_WITH_SELECT_OPTIONS = [ 'select', 'radio', 'switcher', 'image', 'conditional_meta' ];
5+
const FIELDS_COMPATIBLE_WITH_TEXT = [ 'text', 'textarea', 'date', 'email' ]
6+
const FIELDS_COMPATIBLE_WITH_NUMBERS = [ ...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, 'number' ];
7+
58
const OPERATOR_COMPARISON_VALUE_FIELD_TYPE = {
6-
'select': [...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, 'checkbox'],
9+
'select': [...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, 'checkbox', 'imageselect'],
710
}
811
const COMPARISON_VALUE_CAN_USE_SELECT = [ 'is', 'not', 'greater than', 'less than' ];
912
const HIDE_COMPARISON_INPUT_FIELD = ['any', 'empty', 'odd-number', 'even-number'];
10-
const FIELDS_COMPATIBLE_WITH_TEXT = [ 'text', 'textarea', 'date', 'email' ]
11-
const FIELDS_COMPATIBLE_WITH_NUMBERS = [ ...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, 'number' ];
13+
1214
const OPERATORS_FIELD_COMPATIBILITY = {
13-
'is': [...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, ...FIELDS_COMPATIBLE_WITH_TEXT, ...FIELDS_COMPATIBLE_WITH_NUMBERS, 'checkbox'],
14-
'not': [...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, ...FIELDS_COMPATIBLE_WITH_TEXT, ...FIELDS_COMPATIBLE_WITH_NUMBERS, 'checkbox'],
15+
'is': [...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, ...FIELDS_COMPATIBLE_WITH_TEXT, ...FIELDS_COMPATIBLE_WITH_NUMBERS, 'checkbox', 'imageselect'],
16+
'not': [...FIELD_COMPATIBLE_WITH_SELECT_OPTIONS, ...FIELDS_COMPATIBLE_WITH_TEXT, ...FIELDS_COMPATIBLE_WITH_NUMBERS, 'checkbox', 'imageselect'],
1517
'greater than': FIELDS_COMPATIBLE_WITH_NUMBERS,
1618
'less than': FIELDS_COMPATIBLE_WITH_NUMBERS,
1719
'even-number': FIELDS_COMPATIBLE_WITH_NUMBERS,
@@ -128,38 +130,40 @@ jQuery(function($) {
128130
jQuery(".ppom-meta-save-notice").html('<img src="' + ppom_vars.loader + '">').show();
129131

130132
$('.ppom-unsave-data').remove();
131-
const data = $(this).serializeJSON();
132-
133-
const fieldsOrder = Array(...document.querySelectorAll('.ui-sortable-handle[id^="ppom_sort_id_"]'))
134-
.map( node => node.id.replace('ppom_sort_id_', '') ); // ['2', '3']
135-
data.ppom = fieldsOrder.map( fieldId => data.ppom[fieldId] );
133+
134+
const formData = new FormData();
135+
const ppomFields = new URLSearchParams();
136136

137-
data.ppom = JSON.stringify(data.ppom);
138-
139-
// Send the JSON data via POST request
140-
$.ajax({
141-
url: ajaxurl,
142-
data: data, // Send as regular object (no need to stringify)
143-
type: 'POST',
144-
success: function(resp) {
145-
146-
const bg_color = resp.status == 'success' ? '#4e694859' : '#ee8b94';
147-
jQuery(".ppom-meta-save-notice").html(resp.message).css({ 'background-color': bg_color, 'padding': '8px', 'border-left': '5px solid #008c00' });
148-
if (resp.status == 'success') {
149-
if (resp.redirect_to != '') {
150-
window.location = resp.redirect_to;
151-
}
152-
else {
153-
window.location.reload(true);
154-
}
155-
}
156-
},
157-
error: function() {
158-
// Handle error
159-
jQuery(".ppom-meta-save-notice").html("An error occurred. Please try again.").css({ 'background-color': '#ee8b94', 'padding': '8px', 'border-left': '5px solid #c00' });
137+
// NOTE: since the request is to big for small values of `max_input_vars`, we will send the PPOM fields as a single string.
138+
(new FormData(this)).forEach(( value, key) => {
139+
if ( key.startsWith('ppom[') && typeof value === 'string' ) {
140+
ppomFields.append( key, value );
141+
} else {
142+
formData.append(key, value);
160143
}
161144
});
162145

146+
formData.append('ppom', ppomFields.toString());
147+
148+
fetch(ajaxurl, {
149+
method: 'POST',
150+
body: formData
151+
})
152+
.then(response => response.json())
153+
.then(resp => {
154+
const bg_color = resp.status == 'success' ? '#4e694859' : '#ee8b94';
155+
jQuery(".ppom-meta-save-notice").html(resp.message).css({ 'background-color': bg_color, 'padding': '8px', 'border-left': '5px solid #008c00' });
156+
if (resp.status == 'success') {
157+
if (resp.redirect_to != '') {
158+
window.location = resp.redirect_to;
159+
} else {
160+
window.location.reload();
161+
}
162+
}
163+
})
164+
.catch(() => {
165+
jQuery(".ppom-meta-save-notice").html("An error occurred. Please try again.").css({ 'background-color': '#ee8b94', 'padding': '8px', 'border-left': '5px solid #c00' });
166+
});
163167
});
164168

165169

js/ppom-simple-popup.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@
151151
if (!$(e.target).closest('.ppom-enquiry-modal, .ppom-popup-product-edit-modal').length) {
152152
modal.trigger(prefix + ':close');
153153
}
154-
e.preventDefault();
155154
});
156155

157156
// disable backgroundclickevent close

0 commit comments

Comments
 (0)