PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 6.0.7
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v6.0.7
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 6.0.7 All 35 releases
mlsimport / mlsimport.php

mlsimport.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 6.0.7, at mlsimport.php

562 lines 17.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: MlsImport
4 * Plugin URI: https://mlsimport.com/
5 * Description: "MLS Import - The MLSImport plugin facilitates the connection to your real estate MLS database, allowing you to download and synchronize real estate property data from the MLS.
6 * Version: 6.0.7
7 * Stable tag: 6.0.7
8 * Requires at least: 5.2
9 * Requires PHP: 7.2
10 * License: GPLv3
11 * License URI: https://www.gnu.org/licenses/gpl-3.0.html
12 * Author: MlsImport
13 * Text Domain: mlsimport
14 * Domain Path: /languages
15 */
16
17 // If this file is called directly, abort.
18
19 if ( ! defined( 'WPINC' ) ) {
20 die;
21 }
22
23
24 define( 'MLSIMPORT_VERSION', '6.0.7' );
25 define( 'MLSIMPORT_CLUBLINK', 'mlsimport.com' );
26 define( 'MLSIMPORT_CLUBLINKSSL', 'https' );
27 define( 'MLSIMPORT_CRON_STEP', 20 );
28 define( 'MLSIMPORT_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
29 define( 'MLSIMPORT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
30 //define( 'MLSIMPORT_API_URL', 'https://requests.mlsimport.com/' );
31 define( 'MLSIMPORT_API_URL', 'https://pyjzsilw7b.execute-api.us-east-1.amazonaws.com/dev/' );
32 if ( ! defined( 'MLSIMPORT_HIDE_SETUP_NOTICE' ) ) {
33 define( 'MLSIMPORT_HIDE_SETUP_NOTICE', false );
34 }
35
36
37
38 /**
39 * The code that runs during plugin activation.
40 * This action is documented in includes/class-mlsimport-activator.php
41 */
42 function mlsimport_activate() {
43 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mlsimport-activator.php';
44 Mlsimport_Activator::activate();
45 }
46
47
48
49 /**
50 * The code that runs during plugin deactivation.
51 * This action is documented in includes/class-mlsimport-deactivator.php
52 */
53 function mlsimport_deactivate() {
54 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mlsimport-deactivator.php';
55 wp_clear_scheduled_hook( 'event_mls_import_auto' );
56 wp_clear_scheduled_hook( 'mlsimport_reconciliation_event' );
57 Mlsimport_Deactivator::deactivate();
58 }
59
60
61
62 register_activation_hook( __FILE__, 'mlsimport_activate' );
63 register_deactivation_hook( __FILE__, 'mlsimport_deactivate' );
64
65
66
67 /**
68 * The core plugin class that is used to define internationalization,
69 * admin-specific hooks, and public-facing site hooks.
70 */
71
72 require 'vendor/autoload.php';
73 require_once plugin_dir_path( __FILE__ ) . 'includes/help_functions.php';
74 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mlsimport.php';
75 require_once plugin_dir_path( __FILE__ ) . 'includes/ThemeImport.php';
76 require_once plugin_dir_path( __FILE__ ) . 'enviroment/ResidenceClass.php';
77 require_once plugin_dir_path( __FILE__ ) . 'enviroment/EstateClass.php';
78 require_once plugin_dir_path( __FILE__ ) . 'enviroment/HouzezClass.php';
79 require_once plugin_dir_path( __FILE__ ) . 'enviroment/RealHomesClass.php';
80 require_once plugin_dir_path( __FILE__ ) . 'enviroment/ResoBase.php';
81 require_once plugin_dir_path( __FILE__ ) . 'enviroment/SparkResoClass.php';
82 require_once plugin_dir_path( __FILE__ ) . 'enviroment/BridgeResoClass.php';
83 require_once plugin_dir_path( __FILE__ ) . 'enviroment/TresleResoClass.php';
84 require_once plugin_dir_path( __FILE__ ) . 'enviroment/MlsgridResoClass.php';
85 require_once plugin_dir_path( __FILE__ ) . 'enviroment/MlsgridResoClass.php';
86 require_once plugin_dir_path( __FILE__ ) . 'includes/addons/agents_offices.php';
87 require_once plugin_dir_path( __FILE__ ) . 'includes/mlsimport-onboarding.php';
88
89 require_once plugin_dir_path( __FILE__ ) . 'includes/mlsimport-field-selector-functions.php';
90 require_once plugin_dir_path( __FILE__ ) . 'includes/mlsimport-progressive-save.php';
91
92 if ( ! wp_next_scheduled( 'event_mls_import_auto' ) ) {
93 wp_schedule_event( time(), 'hourly', 'event_mls_import_auto' );
94 }
95
96
97 /**
98 * Scheduled event: Processes MLSimport items marked for cron processing, in memory-safe batches.
99 *
100 * This function is triggered by the 'event_mls_import_auto' action.
101 * It fetches mlsimport_item post IDs in small batches (not all at once!) to minimize memory usage.
102 * Only posts with meta 'mlsimport_item_stat_cron' = 1 are processed.
103 * For each item, calls mlsimport_saas_start_cron_links_per_item().
104 *
105 * Optimizations:
106 * - Uses 'fields' => 'ids' so only post IDs are loaded (saves memory)
107 * - Batches with posts_per_page/paged, so memory does not spike for large data sets
108 * - Calls gc_collect_cycles() periodically to further reduce memory leaks
109 * - Skips processing if MLS is not connected or token is missing
110 *
111 * @return void
112 */
113 add_action('event_mls_import_auto', 'mlsimport_saas_event_mls_import_auto_function');
114 /**
115 * Scheduled event handler for MLS Import Auto (runs via WP Cron).
116 * Processes mlsimport_item posts in batches and logs memory usage.
117 */
118 function mlsimport_saas_event_mls_import_auto_function() {
119 global $mlsimport;
120
121 //error_log('[AutoCron] Start: ' . (memory_get_usage(true) / 1024 / 1024) . ' MB');
122
123 // 1. Get the API token from transient - exit if not set
124 $token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient();
125 //error_log('[AutoCron] After token fetch: ' . (memory_get_usage(true) / 1024 / 1024) . ' MB');
126 if (trim($token) === '') {
127 //error_log('[AutoCron] No token, exiting.');
128 return;
129 }
130
131 // 2. Check if MLS connection is valid - exit if not
132 $is_mls_connected = get_option('mlsimport_connection_test', '');
133 //error_log('[AutoCron] After connection check: ' . (memory_get_usage(true) / 1024 / 1024) . ' MB');
134 if ('yes' !== $is_mls_connected) {
135 //error_log('[AutoCron] No valid connection, exiting.');
136 return;
137 }
138
139 // 3. Set batch size for processing and initialize loop variables
140 $batch_size = 100;
141 $paged = 1;
142 $total_processed = 0;
143
144 // 4. Process in batches until no more items are found
145 do {
146 // Prepare query: only IDs, filter by meta key, batch, paged, no_found_rows speeds up query
147 $args = array(
148 'post_type' => 'mlsimport_item',
149 'post_status' => 'any',
150 'posts_per_page' => $batch_size,
151 'paged' => $paged,
152 'fields' => 'ids',
153 'meta_query' => array(
154 array(
155 'key' => 'mlsimport_item_stat_cron',
156 'value' => 1,
157 'compare' => '=',
158 ),
159 ),
160 'no_found_rows' => true,
161 );
162
163 // Get post IDs for this batch
164 $post_ids = get_posts($args);
165 //error_log("[AutoCron] Batch {$paged} fetched " . count($post_ids) . " items, memory: " . (memory_get_usage(true) / 1024 / 1024) . ' MB');
166
167 // If nothing is returned, break the loop
168 if (empty($post_ids)) {
169 break;
170 }
171
172 // 5. Loop through each post ID in this batch
173 foreach ($post_ids as $prop_id) {
174 $logs = 'Loop custom post: ' . $prop_id . PHP_EOL;
175 mlsimport_debuglogs_per_plugin($logs);
176
177 // Call processing function for this item
178 $mlsimport->admin->mlsimport_saas_start_cron_links_per_item($prop_id);
179
180 $total_processed++;
181
182 // Free memory every 100 processed items
183 if ($total_processed % 100 === 0) {
184 gc_collect_cycles();
185 //error_log("[AutoCron] Processed {$total_processed} total, memory: " . (memory_get_usage(true) / 1024 / 1024) . ' MB');
186 }
187 }
188
189 // 6. Prepare next batch
190 $paged++;
191 unset($post_ids); // Free memory
192 gc_collect_cycles(); // Trigger garbage collection
193 //error_log("[AutoCron] After batch {$paged}, memory: " . (memory_get_usage(true) / 1024 / 1024) . ' MB');
194
195 } while (true);
196
197 //error_log('[AutoCron] Done, total processed: ' . $total_processed . ', end memory: ' . (memory_get_usage(true) / 1024 / 1024) . ' MB');
198 }
199
200
201
202 /*
203 * Reconciliation Mechanism
204 *
205 *
206 *
207 **/
208
209 if ( ! wp_next_scheduled( 'mlsimport_reconciliation_event' ) ) {
210 wp_schedule_event( time(), 'daily', 'mlsimport_reconciliation_event' );
211 }
212
213 add_action( 'mlsimport_reconciliation_event', 'mlsimport_saas_reconciliation_event_function' );
214
215
216 /*
217 * Force use of transient
218 *
219 *
220 *
221 **/
222
223 function mlsimport_force_use_transient( $value ) {
224 return $value;
225 // return false;
226 }
227
228
229
230
231 global $mlsimport;
232 $mlsimport = new Mlsimport();
233 $mlsimport->run();
234
235
236
237
238
239 $supported_theme = array(
240 991 => 'WpResidence',
241 992 => 'Houzez',
242 993 => 'Real Homes',
243 994 => 'Wpestate',
244
245 );
246
247 define( 'MLSIMPORT_THEME', $supported_theme );
248
249 add_filter( 'action_scheduler_failure_period', 'mlsimport_saas_filter_timelimit' );
250 function mlsimport_saas_filter_timelimit( $time_limit ) {
251 return 3000;
252 }
253
254
255
256 /*
257 *
258 * Write logs
259 *
260 **/
261
262 function mlsimport_saas_single_write_import_custom_logs( $message, $tip_import = 'normal' ) {
263 // Check if logging is enabled
264 $enable_logs = intval( get_option( 'mlsimport_disable_logs' ) );
265 if ( 1 !== $enable_logs) {
266 return;
267 }
268
269 if ( is_array( $message ) ) {
270 $message = wp_json_encode( $message );
271 }
272
273 $formatted_message = gmdate( 'F j, Y, g:i a' ) . ' -> ' . $message;
274
275 // Determine the log file path based on the import type
276 $log_file_name = 'cron' === $tip_import ? 'cron_logs' :
277 ( 'delete' === $tip_import ? 'delete_logs' :
278 ( 'server_cron' === $tip_import ? 'server_cron_logs' : 'import_logs' ) );
279
280 // Construct the full path with a date suffix
281 $log_file_path = WP_PLUGIN_DIR . "/mlsimport/logs/{$log_file_name}-" . gmdate( 'Y-m-d' ) . '.log';
282
283 // Error handling for file operations
284 try {
285 // Check and create the directory for logs if it does not exist
286 $log_dir = dirname( $log_file_path );
287 if ( ! file_exists( $log_dir ) ) {
288 mkdir( $log_dir, 0755, true );
289 }
290
291 // Append the formatted message to the log file
292 file_put_contents( $log_file_path, $formatted_message, FILE_APPEND | LOCK_EX );
293 } catch ( Exception $e ) {
294 // Handle the exception, such as logging the error elsewhere or sending a notification
295 }
296 }
297
298
299
300 /*
301 *
302 *
303 * Write Status logs
304 *
305 *
306 **/
307
308
309
310 function mlsimport_debuglogs_per_plugin_old( $message ) {
311
312 if ( is_array( $message ) ) {
313 $message = wp_json_encode( $message );
314 }
315
316 global $wp_filesystem;
317 if ( empty( $wp_filesystem ) ) {
318 require_once ABSPATH . '/wp-admin/includes/file.php';
319 WP_Filesystem();
320 }
321
322 $path_status = WP_PLUGIN_DIR . '/mlsimport/logs/status_logs.log';
323 file_put_contents( $path_status, $message, LOCK_EX );
324 }
325 function mlsimport_debuglogs_per_plugin( $message ) {
326
327 if ( is_array( $message ) ) {
328 $message = wp_json_encode( $message );
329 }
330
331 if ( empty( $message ) ) {
332 return; // Exit the function if there's nothing to log
333 }
334
335 $log_file_path = WP_PLUGIN_DIR . '/mlsimport/logs/status_logs.log';
336
337 // Check and create the directory for logs if it does not exist
338 $log_dir = dirname( $log_file_path );
339 if ( ! file_exists( $log_dir ) ) {
340 mkdir( $log_dir, 0755, true );
341 }
342
343 // Error handling for file operations
344 try {
345 // Append the message to the log file with a newline and acquire an exclusive lock during writing
346 file_put_contents( $log_file_path, $message . PHP_EOL, LOCK_EX );
347 } catch ( Exception $e ) {
348 // Handle the exception, such as logging the error elsewhere or sending a notification
349 }
350 }
351
352
353
354
355
356 /*
357 * Cron job trigger
358 *
359 *
360 *
361 **/
362
363
364 // */5 * * * * wget http://example.com/check */2
365 add_action( 'init', 'mlsimport_trigger_cron_job' );
366 function mlsimport_trigger_cron_job() {
367 // ?mlsimport_cron=yes
368 if ( isset( $_REQUEST['mlsimport_cron'] ) && 'yes' === sanitize_text_field( wp_unslash( $_REQUEST['mlsimport_cron'] ) ) ) {
369 $last_run = intval( get_option( 'mlsimport_last_server_cron' ) );
370 $now = time();
371 if ( 0 === intval($last_run) ) {
372 update_option( 'mlsimport_last_server_cron', $now );
373 }
374
375 if ( $last_run < $now - ( 60 * 60 * 2 ) ) {
376 $log = 'Server Cron Job triggered on ' . date( 'l jS \of F Y h:i:s A', $last_run ) . ' vs ' . gmdate( 'l jS \of F Y h:i:s A', $now ) . PHP_EOL;
377 // mlsimport_saas_event_mls_import_auto_function();
378 update_option( 'mlsimport_last_server_cron', $now );
379 } else {
380 $log = 'Server Cron Job Called but not triggered. Last run on ' . gmdate( 'l jS \of F Y h:i:s A', $last_run ) . ' vs ' . gmdate( 'l jS \of F Y h:i:s A', $now ) . PHP_EOL;
381 }
382
383 mlsimport_saas_single_write_import_custom_logs( $log, 'server_cron' );
384 }
385 }
386
387
388
389 function mlsimport_show_signup() {
390 $affiliate_url = 'https://mlsimport.com';
391 if ( function_exists( 'wp_estate_init' ) ) {
392 $affiliate_url = 'https://mlsimport.com/ref/1/?campaign=wpresidence';
393 }
394 ?>
395 <div class="mlsimport_signup">
396 <h3><?php esc_html_e('Import MLS Listings into your Real Estate website', 'mlsimport'); ?></h3>
397 <p><?php esc_html_e('Signup now and get 30-Days Free trial, no setup fee & cancel anytime at ', 'mlsimport'); ?><a href="https://mlsimport.com/mls-import-plugin-pricing/" target="_blank">MLSImport.com</a></p>
398 <a href="https://mlsimport.com/mls-import-plugin-pricing" class="button mlsimport_button mlsimport_signup_button" target="_blank"><?php esc_html_e('Create My Account', 'mlsimport'); ?></a>
399 </div>
400 <?php
401 }
402
403
404 //add_action('admin_init', 'force_recount_all_terms');
405 function force_recount_all_terms() {
406 global $wpdb;
407
408 // Get all taxonomies
409 $taxonomies = get_taxonomies([], 'names');
410
411 foreach ($taxonomies as $taxonomy) {
412 // Get all terms for the taxonomy
413 $terms = get_terms([
414 'taxonomy' => $taxonomy,
415 'hide_empty' => false, // Include terms with 0 count
416 'fields' => 'ids', // Get only the term IDs
417 ]);
418
419 if (!is_wp_error($terms) && !empty($terms)) {
420 // Get term_taxonomy_ids for these terms
421 $term_taxonomy_ids = $wpdb->get_col($wpdb->prepare(
422 "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id IN (" . implode(',', array_map('intval', $terms)) . ")"
423 ));
424
425 // Update term counts
426 if (!empty($term_taxonomy_ids)) {
427 wp_update_term_count_now($term_taxonomy_ids, $taxonomy);
428 }
429 }
430 }
431
432 echo "Term counts have been recalculated for all taxonomies.";
433 }
434
435
436 /*
437 *
438 * create dropdown list
439 *
440 *
441 */
442 function mlsiport_mls_select_list( $key, $value, $data_array ) {
443 $select = '<select class="mlsimport-2025-select" id="' . esc_attr( $key ) . '" name="mlsimport_admin_options[' . $key . ']">';
444 if ( is_array( $data_array ) ) :
445 foreach ( $data_array as $key => $mls_item ) {
446 $select .= '<option value="' .esc_attr( $key ). '"';
447 if ( intval( $value ) === intval( $key ) ) {
448 $select .= ' selected ';
449 }
450 $select .= '>' .esc_html( $mls_item ). '</option>';
451 }
452 endif;
453 $select .= '</select>';
454 return $select;
455 }
456
457
458
459 add_action('wp_ajax_mlsimport_save_account', 'mlsimport_save_account_callback');
460 function mlsimport_save_account_callback() {
461 check_ajax_referer('mlsimport_onboarding_nonce', 'security');
462
463 $options = get_option('mlsimport_admin_options', []);
464 if ( ! empty($_POST['mlsimport_username']) && ! empty($_POST['mlsimport_password']) ) {
465 $options['mlsimport_username'] = sanitize_text_field($_POST['mlsimport_username']);
466 $options['mlsimport_password'] = sanitize_text_field($_POST['mlsimport_password']);
467 update_option('mlsimport_admin_options', $options);
468 }
469
470 global $mlsimport;
471
472 // Refresh token
473 $token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient();
474
475 if (trim($token) === '') {
476 ob_start();
477
478 ?>
479 <div class="mlsimport_warning">
480 <?php esc_html_e('You are not connected to MlsImport - Please check your Username and Password.', 'mlsimport'); ?>
481 </div>
482 <?php
483 $html = ob_get_clean();
484
485 wp_send_json_success([
486 'message' => __('You are not connected.', 'mlsimport'),
487 'html' => $html,
488 'connected' => false
489 ]);
490 } else {
491 ob_start();
492 ?>
493 <div class="mlsimport_warning mlsimport_validated">
494 <?php esc_html_e('You are connected to your MlsImport account!', 'mlsimport'); ?>
495 </div>
496 <?php
497 $html = ob_get_clean();
498
499 wp_send_json_success([
500 'message' => __('Connected successfully!', 'mlsimport'),
501 'html' => $html,
502 'connected' => true
503 ]);
504 }
505 }
506
507
508
509
510
511
512
513
514 add_action('wp_ajax_mlsimport_save_mls_data', 'mlsimport_save_mls_data_callback');
515 function mlsimport_save_mls_data_callback() {
516 check_ajax_referer('mlsimport_onboarding_nonce', 'security');
517
518 $options = get_option('mlsimport_admin_options', []);
519
520 foreach ($_POST as $key => $value) {
521 if (strpos($key, 'mlsimport_') === 0 && $key !== 'mlsimport_username' && $key !== 'mlsimport_password') {
522 $options[$key] = sanitize_text_field($value);
523 }
524 }
525
526 update_option('mlsimport_admin_options', $options);
527
528 // Run MLS connection check
529 global $mlsimport;
530 $is_mls_connected = get_option('mlsimport_connection_test', '');
531 $mlsimport->admin->mlsimport_saas_setting_up();
532
533 if ('yes' !== $is_mls_connected) {
534 $mlsimport->admin->mlsimport_saas_check_mls_connection();
535 $is_mls_connected = get_option('mlsimport_connection_test', '');
536 }
537
538 ob_start();
539 if ('yes' === $is_mls_connected) {
540 ?>
541 <div class="mlsimport_warning mlsimport_validated">
542 <?php esc_html_e('You’re now connected to your MLS.', 'mlsimport'); ?>
543 </div>
544 <?php
545 } else {
546 ?>
547 <div class="mlsimport_warning">
548 <?php esc_html_e('The connection to your MLS was NOT successful. Please check the authentication token is correct and check your MLS Data Access Application is approved.', 'mlsimport'); ?>
549 </div>
550 <?php
551 }
552 $html = ob_get_clean();
553
554 wp_send_json_success([
555 'message' => __('MLS data saved', 'mlsimport'),
556 'html' => $html,
557 'connected' => $is_mls_connected === 'yes',
558 ]);
559 }
560
561
562