PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 6.3.6
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v6.3.6
7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 6.0.5 All 36 releases
mlsimport / includes / mlsimport-progressive-save.php

mlsimport-progressive-save.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 6.3.6, at includes/mlsimport-progressive-save.php

667 lines 22.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MLS Import Progressive Save Handlers
4 *
5 * Server-side handlers for the progressive save system:
6 * - Initial save detection and chunked saving
7 * - Individual field option saving
8 * - Optimized field order saving
9 *
10 * @package MLSImport
11 * @subpackage MLSImport/includes
12 */
13
14 // Exit if accessed directly
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19
20 /**
21 * AJAX handler for saving field order
22 * This stores the order directly in the mlsimport_admin_fields_select option
23 */
24 function mlsimport_ajax_save_field_order() {
25 // Add detailed logging for debugging
26
27 // Check nonce
28 if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'mlsimport_field_selector_nonce')) {
29 //error_log('Invalid security token in field order save');
30 wp_send_json_error('Invalid security token');
31 return;
32 }
33
34 // Check for both parameter formats
35 $field_order = array();
36
37 // Check for standard array format (field_order[])
38 if (isset($_POST['field_order']) && is_array($_POST['field_order'])) {
39 $field_order = $_POST['field_order'];
40 //error_log('Using field_order parameter: ' . count($field_order) . ' items');
41 }
42 // Check for older format (fields[])
43 else if (isset($_POST['fields']) && is_array($_POST['fields'])) {
44 $field_order = $_POST['fields'];
45 //error_log('Using fields parameter: ' . count($field_order) . ' items');
46 }
47 // Otherwise, try to get values directly by key pattern
48 else {
49 //error_log('No direct array found, checking for field_order[] pattern');
50 // Check if we need to manually extract from the POST data (for field_order[])
51 foreach ($_POST as $key => $value) {
52 if (preg_match('/^field_order(\[.*\])?$/', $key)) {
53 if (is_array($value)) {
54 $field_order = $value;
55 //error_log('Found field_order as array with ' . count($field_order) . ' items');
56 break;
57 }
58 }
59 }
60
61 // If we still don't have field order data, try to extract it directly from POST
62 if (empty($field_order)) {
63 //error_log('Trying to manually build array from field_order[]');
64 foreach ($_POST as $key => $value) {
65 if (strpos($key, 'field_order[') === 0) {
66 $field_order[] = $value;
67 }
68 }
69 //error_log('Manually built array with ' . count($field_order) . ' items');
70 }
71 }
72
73 // Check if we have any field order data
74 if (empty($field_order)) {
75 //error_log('No field order data found in request');
76 wp_send_json_error('No field order provided');
77 return;
78 }
79
80 // Log field count for debugging
81 //error_log('Saving ' . count($field_order) . ' fields in order');
82
83 // Get current options
84 $options = get_option('mlsimport_admin_fields_select', array());
85
86 // Make sure $options is an array
87 if (!is_array($options)) {
88 $options = array();
89 }
90
91 // Store the field order directly in the options
92 $options['field_order'] = array_map('sanitize_text_field', $field_order);
93
94 // Save the updated options
95 $result = update_option('mlsimport_admin_fields_select', $options);
96
97 if ($result) {
98 global $mlsimport;
99 if ( isset( $mlsimport->admin->env_data ) && method_exists( $mlsimport->admin->env_data, 'enviroment_custom_fields' ) ) {
100 $mlsimport->admin->env_data->enviroment_custom_fields( $mlsimport->get_plugin_name() );
101 }
102 //error_log('Field order saved successfully');
103 wp_send_json_success('Field order saved successfully');
104 } else {
105 //error_log('Failed to save field order - update_option returned false');
106 wp_send_json_error('Failed to save field order - update_option returned false');
107 }
108 }
109
110
111
112 /**
113 * Add this function to your mlsimport-admin-fields-select.php file
114 * Outputs the nonce field needed for field ordering
115 */
116 function mlsimport_add_field_selector_nonce() {
117 wp_nonce_field('mlsimport_field_selector_nonce', 'mlsimport_field_selector_nonce');
118 }
119
120
121
122
123
124
125 /**
126 * Register AJAX handlers for progressive saving
127 */
128 function mlsimport_register_progressive_save_handlers() {
129 // Check if initial save is needed
130 add_action('wp_ajax_mlsimport_check_initial_save_needed', 'mlsimport_ajax_check_initial_save_needed');
131
132 // Save a chunk of fields
133 add_action('wp_ajax_mlsimport_save_field_chunk', 'mlsimport_ajax_save_field_chunk');
134
135 // Save an individual field option
136 add_action('wp_ajax_mlsimport_save_field_option', 'mlsimport_ajax_save_field_option');
137
138 // Bulk save import selections
139 add_action('wp_ajax_mlsimport_save_bulk_import', 'mlsimport_ajax_save_bulk_import');
140
141 // Bulk save admin visibility selections
142 add_action('wp_ajax_mlsimport_save_bulk_admin', 'mlsimport_ajax_save_bulk_admin');
143 }
144 add_action('init', 'mlsimport_register_progressive_save_handlers');
145
146
147
148
149
150
151 function mlsimport_ajax_save_field_chunk() {
152 // Check nonce
153 if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'mlsimport_field_selector_nonce')) {
154 wp_send_json_error('Invalid security token');
155 return;
156 }
157
158 // Capability check — the settings page is gated by 'administrator'; a nonce
159 // is not authorization.
160 if ( ! current_user_can( 'administrator' ) ) {
161 wp_send_json_error( 'Insufficient permissions' );
162 return;
163 }
164
165 // Check if fields were provided
166 if (!isset($_POST['fields']) || !is_array($_POST['fields'])) {
167 wp_send_json_error('No field data provided');
168 return;
169 }
170
171 // Get chunk info
172 $chunk_index = isset($_POST['chunk_index']) ? intval($_POST['chunk_index']) : 0;
173 $total_chunks = isset($_POST['total_chunks']) ? intval($_POST['total_chunks']) : 1;
174 $is_last_chunk = ($chunk_index + 1) == $total_chunks;
175
176 // Get current options
177 $options = get_option('mlsimport_admin_fields_select', array());
178
179 // Initialize arrays if they don't exist
180 if (!is_array($options)) {
181 $options = array();
182 }
183
184 if (!isset($options['mls-fields'])) {
185 $options['mls-fields'] = array();
186 }
187
188 if (!isset($options['mls-fields-admin'])) {
189 $options['mls-fields-admin'] = array();
190 }
191
192 if (!isset($options['mls-fields-label'])) {
193 $options['mls-fields-label'] = array();
194 }
195
196 if (!isset($options['mls-fields-map-postmeta'])) {
197 $options['mls-fields-map-postmeta'] = array();
198 }
199
200 if (!isset($options['mls-fields-map-taxonomy'])) {
201 $options['mls-fields-map-taxonomy'] = array();
202 }
203
204 if (!isset($options['field_order'])) {
205 $options['field_order'] = array();
206 $i = 0;
207 }else{
208 $i = count($options['field_order'] );
209 }
210
211
212
213 // Process fields in the chunk
214 foreach ($_POST['fields'] as $field_key => $field_data) {
215
216
217 // Process field data as before
218 if (isset($field_data['import'])) {
219 $options['mls-fields'][$field_key] = intval($field_data['import']);
220 }
221
222 if (isset($field_data['admin'])) {
223 $options['mls-fields-admin'][$field_key] = intval($field_data['admin']);
224 }
225
226 if (isset($field_data['label'])) {
227 $options['mls-fields-label'][$field_key] = $field_data['label'];
228 }
229
230 if (isset($field_data['postmeta'])) {
231 $options['mls-fields-map-postmeta'][$field_key] = $field_data['postmeta'];
232 }
233
234 if (isset($field_data['taxonomy'])) {
235 $options['mls-fields-map-taxonomy'][$field_key] = $field_data['taxonomy'];
236 }
237
238
239 $options['field_order'][$field_key] = $i++;
240
241 }
242
243
244
245
246 // Save the options
247 $saved = update_option('mlsimport_admin_fields_select', $options);
248 if ( $saved ) {
249 global $mlsimport;
250 if ( isset( $mlsimport->admin->env_data ) && method_exists( $mlsimport->admin->env_data, 'enviroment_custom_fields' ) ) {
251 $mlsimport->admin->env_data->enviroment_custom_fields( $mlsimport->get_plugin_name() );
252 }
253 }
254
255 wp_send_json_success(array(
256
257 'field_order'=> $options['field_order'],
258 'message' => 'Field chunk ' . ($chunk_index + 1) . ' of ' . $total_chunks . ' saved successfully',
259 'chunkIndex' => $chunk_index,
260 'totalChunks' => $total_chunks,
261 'isLastChunk' => $is_last_chunk,
262
263 ));
264 }
265
266 /**
267 * AJAX handler to save import selections in bulk
268 */
269 function mlsimport_ajax_save_bulk_import() {
270 if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'mlsimport_field_selector_nonce')) {
271 wp_send_json_error('Invalid security token');
272 }
273
274 // Capability check — the settings page is gated by 'administrator'; a nonce
275 // is not authorization.
276 if ( ! current_user_can( 'administrator' ) ) {
277 wp_send_json_error( 'Insufficient permissions' );
278 }
279
280 if (!isset($_POST['fields']) || !is_array($_POST['fields'])) {
281 wp_send_json_error('No field data provided');
282 }
283
284 $options = get_option('mlsimport_admin_fields_select', array());
285 if (!isset($options['mls-fields']) || !is_array($options['mls-fields'])) {
286 $options['mls-fields'] = array();
287 }
288
289 foreach ($_POST['fields'] as $key => $value) {
290 $options['mls-fields'][sanitize_text_field($key)] = intval($value);
291 }
292
293 $result = update_option('mlsimport_admin_fields_select', $options);
294 if ($result) {
295 global $mlsimport;
296 if ( isset( $mlsimport->admin->env_data ) && method_exists( $mlsimport->admin->env_data, 'enviroment_custom_fields' ) ) {
297 $mlsimport->admin->env_data->enviroment_custom_fields( $mlsimport->get_plugin_name() );
298 }
299 wp_send_json_success('Bulk import selections saved');
300 } else {
301 wp_send_json_error('Failed to save selections');
302 }
303 }
304
305 /**
306 * AJAX handler to save admin visibility selections in bulk
307 */
308 function mlsimport_ajax_save_bulk_admin() {
309 if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'mlsimport_field_selector_nonce')) {
310 wp_send_json_error('Invalid security token');
311 }
312
313 // Capability check — the settings page is gated by 'administrator'; a nonce
314 // is not authorization.
315 if ( ! current_user_can( 'administrator' ) ) {
316 wp_send_json_error( 'Insufficient permissions' );
317 }
318
319 if (!isset($_POST['fields']) || !is_array($_POST['fields'])) {
320 wp_send_json_error('No field data provided');
321 }
322
323 $options = get_option('mlsimport_admin_fields_select', array());
324 if (!isset($options['mls-fields-admin']) || !is_array($options['mls-fields-admin'])) {
325 $options['mls-fields-admin'] = array();
326 }
327
328 foreach ($_POST['fields'] as $key => $value) {
329 $options['mls-fields-admin'][sanitize_text_field($key)] = intval($value);
330 }
331
332 $result = update_option('mlsimport_admin_fields_select', $options);
333 if ($result) {
334 global $mlsimport;
335 if ( isset( $mlsimport->admin->env_data ) && method_exists( $mlsimport->admin->env_data, 'enviroment_custom_fields' ) ) {
336 $mlsimport->admin->env_data->enviroment_custom_fields( $mlsimport->get_plugin_name() );
337 }
338 wp_send_json_success('Bulk admin selections saved');
339 } else {
340 wp_send_json_error('Failed to save selections');
341 }
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366 /**
367 * AJAX handler to check if initial save is needed
368 */
369 function mlsimport_ajax_check_initial_save_needed() {
370 // Check nonce
371 if ( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'mlsimport_field_selector_nonce' ) ) {
372 wp_send_json_error( 'Invalid security token' );
373 }
374
375 // Capability check — this returns the full options blob; restrict it to
376 // administrators (the capability gating the settings page).
377 if ( ! current_user_can( 'administrator' ) ) {
378 wp_send_json_error( 'Insufficient permissions' );
379 }
380
381 // Check if option exists and has content
382 $options = get_option( 'mlsimport_admin_fields_select' );
383 $mlsimport_mls_metadata_mls_data = get_option( 'mlsimport_mls_metadata_mls_data', '' );
384
385 // Decode MLS data
386 $mls_data = json_decode( $mlsimport_mls_metadata_mls_data, true );
387
388 // Check if we need an initial save
389 $initial_save_needed = false;
390
391 // If MLS data exists but option is empty/incomplete
392 // had !empty( $mls_data ) &&
393 if ( (
394 empty( $options ) ||
395 empty( $options['mls-fields'] )
396 ) ) {
397 $initial_save_needed = true;
398 }
399
400 wp_send_json_success( array(
401 'initialSaveNeeded' => $initial_save_needed,
402
403 'options'=>$options,
404 'fieldsInOption' => is_array( $options ) && isset( $options['mls-fields'] ) ? count( $options['mls-fields'] ) : 0,
405 'fieldsInMlsData' => is_array( $mls_data ) ? count( $mls_data ) : 0
406 ) );
407 }
408
409 /**
410 * AJAX handler to save an individual field option
411 */
412 function mlsimport_ajax_save_field_ssswsd2option() {
413 // Check nonce
414 if ( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'mlsimport_field_selector_nonce' ) ) {
415 wp_send_json_error( 'Invalid security token' );
416 }
417
418 // Check if field key and option type were provided
419 if ( ! isset( $_POST['field_key'] ) || ! isset( $_POST['option_type'] ) ) {
420 wp_send_json_error( 'Missing field key or option type' );
421 }
422
423 // Sanitize inputs
424 $field_key = sanitize_text_field( $_POST['field_key'] );
425 $option_type = sanitize_text_field( $_POST['option_type'] );
426 $value = isset( $_POST['value'] ) ? $_POST['value'] : '';
427
428 // Validate option type
429 $valid_option_types = array( 'import', 'admin', 'label', 'postmeta', 'taxonomy' );
430
431 if ( ! in_array( $option_type, $valid_option_types ) ) {
432 wp_send_json_error( 'Invalid option type' );
433 }
434
435 // Get current options
436 $options = get_option( 'mlsimport_admin_fields_select', array() );
437 // Keep a copy of the original options to detect changes
438 $original_options = $options;
439
440 // Map option type to options array key
441 $option_map = array(
442 'import' => 'mls-fields',
443 'admin' => 'mls-fields-admin',
444 'label' => 'mls-fields-label',
445 'postmeta' => 'mls-fields-map-postmeta',
446 'taxonomy' => 'mls-fields-map-taxonomy'
447 );
448
449 $option_key = $option_map[ $option_type ];
450
451 // Initialize array if it doesn't exist
452 if ( ! isset( $options[ $option_key ] ) ) {
453 $options[ $option_key ] = array();
454 }
455
456 // Sanitize and update value based on option type
457 switch ( $option_type ) {
458 case 'import':
459 case 'admin':
460 $options[ $option_key ][ $field_key ] = intval( $value );
461 break;
462
463 case 'label':
464 case 'postmeta':
465 case 'taxonomy':
466 $options[ $option_key ][ $field_key ] = sanitize_text_field( $value );
467 break;
468 }
469
470 // If nothing changed, treat as success without calling update_option
471 if ( $options === $original_options ) {
472 wp_send_json_success( array(
473 'message' => 'Field option saved successfully',
474 'fieldKey' => $field_key,
475 'optionType' => $option_type,
476 'value' => $value,
477 ) );
478 }
479
480 // Save updated options
481 $result = update_option( 'mlsimport_admin_fields_select', $options );
482
483 if ( false !== $result ) {
484 wp_send_json_success( array(
485 'message' => 'Field option saved successfully',
486 'fieldKey' => $field_key,
487 'optionType' => $option_type,
488 'value' => $value,
489 ) );
490 } else {
491 wp_send_json_error( 'Failed to save field option' );
492 }
493 }
494 function mlsimport_ajax_save_field_option() {
495 // Check nonce
496 if ( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'mlsimport_field_selector_nonce' ) ) {
497 //error_log( '[mlsimport] Invalid security token' );
498 wp_send_json_error( 'Invalid security token' );
499 }
500
501 // Capability check — the settings page is gated by 'administrator'; a nonce
502 // is not authorization.
503 if ( ! current_user_can( 'administrator' ) ) {
504 wp_send_json_error( 'Insufficient permissions' );
505 }
506
507 // Check if field key and option type were provided
508 if ( ! isset( $_POST['field_key'] ) || ! isset( $_POST['option_type'] ) ) {
509 //error_log( '[mlsimport] Missing field key or option type' );
510 wp_send_json_error( 'Missing field key or option type' );
511 }
512
513 // Sanitize inputs
514 $field_key = sanitize_text_field( $_POST['field_key'] );
515 $option_type = sanitize_text_field( $_POST['option_type'] );
516 $value = isset( $_POST['value'] ) ? $_POST['value'] : '';
517
518 // Error log for debugging
519 //error_log( '[mlsimport] Field Key: ' . $field_key );
520 //error_log( '[mlsimport] Option Type: ' . $option_type );
521 //error_log( '[mlsimport] Raw Value: ' . print_r( $value, true ) );
522
523 // Validate option type
524 $valid_option_types = array( 'import', 'admin', 'label', 'postmeta', 'taxonomy' );
525 if ( ! in_array( $option_type, $valid_option_types ) ) {
526 //error_log( '[mlsimport] Invalid option type: ' . $option_type );
527 wp_send_json_error( 'Invalid option type' );
528 }
529
530 // Get current options
531 $options = get_option( 'mlsimport_admin_fields_select', array() );
532 $original_options = $options;
533
534 // Map option type to options array key
535 $option_map = array(
536 'import' => 'mls-fields',
537 'admin' => 'mls-fields-admin',
538 'label' => 'mls-fields-label',
539 'postmeta' => 'mls-fields-map-postmeta',
540 'taxonomy' => 'mls-fields-map-taxonomy'
541 );
542
543 $option_key = $option_map[ $option_type ];
544
545 if ( ! isset( $options[ $option_key ] ) ) {
546 $options[ $option_key ] = array();
547 //error_log( '[mlsimport] Initialized option key: ' . $option_key );
548 }
549
550 // Sanitize and update value
551 switch ( $option_type ) {
552 case 'import':
553 case 'admin':
554 $options[ $option_key ][ $field_key ] = intval( $value );
555 break;
556
557 case 'label':
558 case 'postmeta':
559 case 'taxonomy':
560 $options[ $option_key ][ $field_key ] = sanitize_text_field( $value );
561 break;
562 }
563
564 // //error_log( '[mlsimport] Updated Options: ' . print_r( $options, true ) );
565
566 // No changes
567 if ( $options === $original_options ) {
568 //error_log( '[mlsimport] No changes detected for option update.' );
569 wp_send_json_success( array(
570 'message' => 'Field option saved successfully',
571 'fieldKey' => $field_key,
572 'optionType' => $option_type,
573 'value' => $value,
574 ) );
575 }
576 //error_log('saving mls-fields '.count($options['mls-fields']));
577 //error_log('saving mls-fields-admin '.count($options['mls-fields-admin']));
578 //error_log('saving mls-fields-label '.count($options['mls-fields-label']));
579 //error_log('saving mls-fields-map-postmeta '.count($options['mls-fields-map-postmeta']));
580 //error_log('saving mls-fields-map-taxonomy '.count($options['mls-fields-map-taxonomy']));
581 //error_log('saving field_order '.count($options['field_order']));
582
583 // Save updated options
584 $result = update_option( 'mlsimport_admin_fields_select', $options );
585
586 if ( false !== $result ) {
587 //error_log( '[mlsimport] Options updated successfully.' );
588 wp_send_json_success( array(
589 'message' => 'Field option saved successfully',
590 'fieldKey' => $field_key,
591 'optionType' => $option_type,
592 'value' => $value,
593 ) );
594 } else {
595 //error_log( '[mlsimport] Failed to update options.' );
596 wp_send_json_error( 'Failed to save field option' );
597 }
598 }
599
600
601
602 function mlsimport_ajax_save_field_position() {
603 // Verify nonce
604 if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'mlsimport_field_selector_nonce')) {
605 wp_send_json_error('Invalid security token');
606 }
607
608 // Capability check — the settings page is gated by 'administrator'; a nonce
609 // is not authorization.
610 if ( ! current_user_can( 'administrator' ) ) {
611 wp_send_json_error( 'Insufficient permissions' );
612 }
613
614 // Validate required fields
615 if (!isset($_POST['moving_index'], $_POST['target_index'], $_POST['position'])) {
616 wp_send_json_error('Missing parameters');
617 }
618
619 $moving_index = intval($_POST['moving_index']);
620 $target_index = intval($_POST['target_index']);
621 $position = sanitize_text_field($_POST['position']); // 'before' or 'after'
622
623 // Load current options
624 $options = get_option('mlsimport_admin_fields_select', []);
625
626 if (empty($options['field_order']) || !is_array($options['field_order'])) {
627 wp_send_json_error('Field order not found');
628 }
629
630
631
632
633 // Validate the requested indexes against the current order.
634 $order_map = $options['field_order'];
635 asort($order_map); // Sort by index.
636 $ordered_keys = array_keys($order_map);
637 if (!isset($ordered_keys[$moving_index]) || !isset($ordered_keys[$target_index])) {
638 wp_send_json_error('Invalid indexes');
639 }
640
641 // Compute the new field order, then reorder EVERY parallel field array via
642 // the shared helper. The previous inline loop named non-existent target
643 // keys ('mls-fields-map-admin' / 'mls-fields-map-label'), so the admin and
644 // label arrays were left in stale order. mlsimport_sort_all_fields_by_order()
645 // uses the correct key list.
646 $options['field_order'] = mlsimport_compute_field_order_after_move(
647 $options['field_order'],
648 $moving_index,
649 $target_index,
650 $position
651 );
652 $options = mlsimport_sort_all_fields_by_order($options);
653
654 // Save the updated options
655 $saved = update_option('mlsimport_admin_fields_select', $options);
656 if ( $saved ) {
657 global $mlsimport;
658 if ( isset( $mlsimport->admin->env_data ) && method_exists( $mlsimport->admin->env_data, 'enviroment_custom_fields' ) ) {
659 $mlsimport->admin->env_data->enviroment_custom_fields( $mlsimport->get_plugin_name() );
660 }
661 }
662
663 wp_send_json_success('Field order updated');
664 }
665
666 // Register the AJAX handler
667 add_action('wp_ajax_mlsimport_save_field_position', 'mlsimport_ajax_save_field_position');