PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 6.0.4
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v6.0.4
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 / mlsimport.php

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

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