| 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: 5.8.1 |
| 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', '5.8.1' ); |
| 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 |
|
| 31 |
|
| 32 |
|
| 33 |
/** |
| 34 |
* The code that runs during plugin activation. |
| 35 |
* This action is documented in includes/class-mlsimport-activator.php |
| 36 |
*/ |
| 37 |
function mlsimport_activate() { |
| 38 |
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mlsimport-activator.php'; |
| 39 |
Mlsimport_Activator::activate(); |
| 40 |
} |
| 41 |
|
| 42 |
|
| 43 |
|
| 44 |
/** |
| 45 |
* The code that runs during plugin deactivation. |
| 46 |
* This action is documented in includes/class-mlsimport-deactivator.php |
| 47 |
*/ |
| 48 |
function mlsimport_deactivate() { |
| 49 |
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mlsimport-deactivator.php'; |
| 50 |
wp_clear_scheduled_hook( 'event_mls_import_auto' ); |
| 51 |
wp_clear_scheduled_hook( 'mlsimport_reconciliation_event' ); |
| 52 |
Mlsimport_Deactivator::deactivate(); |
| 53 |
} |
| 54 |
|
| 55 |
|
| 56 |
|
| 57 |
register_activation_hook( __FILE__, 'mlsimport_activate' ); |
| 58 |
register_deactivation_hook( __FILE__, 'mlsimport_deactivate' ); |
| 59 |
|
| 60 |
|
| 61 |
|
| 62 |
/** |
| 63 |
* The core plugin class that is used to define internationalization, |
| 64 |
* admin-specific hooks, and public-facing site hooks. |
| 65 |
*/ |
| 66 |
|
| 67 |
require 'vendor/autoload.php'; |
| 68 |
require_once plugin_dir_path( __FILE__ ) . 'includes/help_functions.php'; |
| 69 |
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mlsimport.php'; |
| 70 |
require_once plugin_dir_path( __FILE__ ) . 'includes/ThemeImport.php'; |
| 71 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/ResidenceClass.php'; |
| 72 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/EstateClass.php'; |
| 73 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/HouzezClass.php'; |
| 74 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/RealHomesClass.php'; |
| 75 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/ResoBase.php'; |
| 76 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/SparkResoClass.php'; |
| 77 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/BridgeResoClass.php'; |
| 78 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/TresleResoClass.php'; |
| 79 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/MlsgridResoClass.php'; |
| 80 |
require_once plugin_dir_path( __FILE__ ) . 'enviroment/MlsgridResoClass.php'; |
| 81 |
|
| 82 |
|
| 83 |
|
| 84 |
if ( ! wp_next_scheduled( 'event_mls_import_auto' ) ) { |
| 85 |
wp_schedule_event( time(), 'hourly', 'event_mls_import_auto' ); |
| 86 |
} |
| 87 |
|
| 88 |
|
| 89 |
|
| 90 |
|
| 91 |
|
| 92 |
|
| 93 |
|
| 94 |
add_action( 'event_mls_import_auto', 'mlsimport_saas_event_mls_import_auto_function' ); |
| 95 |
function mlsimport_saas_event_mls_import_auto_function() { |
| 96 |
global $mlsimport; |
| 97 |
$token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient(); |
| 98 |
if ( trim( $token ) === '' ) { |
| 99 |
return; |
| 100 |
} |
| 101 |
|
| 102 |
$is_mls_connected = get_option( 'mlsimport_connection_test', '' ); |
| 103 |
if ( 'yes' !== $is_mls_connected ) { |
| 104 |
return; |
| 105 |
} |
| 106 |
|
| 107 |
$logs =''; |
| 108 |
mlsimport_debuglogs_per_plugin( $logs ); |
| 109 |
$args = array( |
| 110 |
'post_type' => 'mlsimport_item', |
| 111 |
'post_status' => 'any', |
| 112 |
'posts_per_page' => -1, |
| 113 |
'meta_query' => array( |
| 114 |
array( |
| 115 |
'key' => 'mlsimport_item_stat_cron', |
| 116 |
'value' => 1, |
| 117 |
'compare' => '=', |
| 118 |
), |
| 119 |
|
| 120 |
), |
| 121 |
|
| 122 |
); |
| 123 |
|
| 124 |
$prop_selection = new WP_Query( $args ); |
| 125 |
if ( $prop_selection->have_posts() ) { |
| 126 |
while ( $prop_selection->have_posts() ) : |
| 127 |
$prop_selection->the_post(); |
| 128 |
$prop_id = get_the_ID(); |
| 129 |
$logs = ' Loop custom post : ' . $prop_id . PHP_EOL; |
| 130 |
mlsimport_debuglogs_per_plugin( $logs ); |
| 131 |
$mlsimport->admin->mlsimport_saas_start_cron_links_per_item( $prop_id ); |
| 132 |
endwhile; |
| 133 |
} |
| 134 |
} |
| 135 |
|
| 136 |
|
| 137 |
|
| 138 |
/* |
| 139 |
* Reconciliation Mechanism |
| 140 |
* |
| 141 |
* |
| 142 |
* |
| 143 |
**/ |
| 144 |
|
| 145 |
if ( ! wp_next_scheduled( 'mlsimport_reconciliation_event' ) ) { |
| 146 |
wp_schedule_event( time(), 'daily', 'mlsimport_reconciliation_event' ); |
| 147 |
} |
| 148 |
|
| 149 |
add_action( 'mlsimport_reconciliation_event', 'mlsimport_saas_reconciliation_event_function' ); |
| 150 |
|
| 151 |
|
| 152 |
/* |
| 153 |
* Force use of transient |
| 154 |
* |
| 155 |
* |
| 156 |
* |
| 157 |
**/ |
| 158 |
|
| 159 |
function mlsimport_force_use_transient( $value ) { |
| 160 |
return $value; |
| 161 |
// return false; |
| 162 |
} |
| 163 |
|
| 164 |
|
| 165 |
|
| 166 |
|
| 167 |
global $mlsimport; |
| 168 |
$mlsimport = new Mlsimport(); |
| 169 |
$mlsimport->run(); |
| 170 |
|
| 171 |
|
| 172 |
|
| 173 |
|
| 174 |
|
| 175 |
$supported_theme = array( |
| 176 |
991 => 'WpResidence', |
| 177 |
992 => 'Houzez', |
| 178 |
993 => 'Real Homes', |
| 179 |
994 => 'Wpestate', |
| 180 |
|
| 181 |
); |
| 182 |
|
| 183 |
define( 'MLSIMPORT_THEME', $supported_theme ); |
| 184 |
|
| 185 |
add_filter( 'action_scheduler_failure_period', 'mlsimport_saas_filter_timelimit' ); |
| 186 |
function mlsimport_saas_filter_timelimit( $time_limit ) { |
| 187 |
return 3000; |
| 188 |
} |
| 189 |
|
| 190 |
|
| 191 |
|
| 192 |
/* |
| 193 |
* |
| 194 |
* Write logs |
| 195 |
* |
| 196 |
**/ |
| 197 |
|
| 198 |
function mlsimport_saas_single_write_import_custom_logs( $message, $tip_import = 'normal' ) { |
| 199 |
// Check if logging is enabled |
| 200 |
$enable_logs = intval( get_option( 'mlsimport_disable_logs' ) ); |
| 201 |
if ( 1 !== $enable_logs) { |
| 202 |
return; |
| 203 |
} |
| 204 |
|
| 205 |
if ( is_array( $message ) ) { |
| 206 |
$message = wp_json_encode( $message ); |
| 207 |
} |
| 208 |
|
| 209 |
$formatted_message = gmdate( 'F j, Y, g:i a' ) . ' -> ' . $message; |
| 210 |
|
| 211 |
// Determine the log file path based on the import type |
| 212 |
$log_file_name = 'cron' === $tip_import ? 'cron_logs' : |
| 213 |
( 'delete' === $tip_import ? 'delete_logs' : |
| 214 |
( 'server_cron' === $tip_import ? 'server_cron_logs' : 'import_logs' ) ); |
| 215 |
|
| 216 |
// Construct the full path with a date suffix |
| 217 |
$log_file_path = WP_PLUGIN_DIR . "/mlsimport/logs/{$log_file_name}-" . gmdate( 'Y-m-d' ) . '.log'; |
| 218 |
|
| 219 |
// Error handling for file operations |
| 220 |
try { |
| 221 |
// Check and create the directory for logs if it does not exist |
| 222 |
$log_dir = dirname( $log_file_path ); |
| 223 |
if ( ! file_exists( $log_dir ) ) { |
| 224 |
mkdir( $log_dir, 0755, true ); |
| 225 |
} |
| 226 |
|
| 227 |
// Append the formatted message to the log file |
| 228 |
file_put_contents( $log_file_path, $formatted_message, FILE_APPEND | LOCK_EX ); |
| 229 |
} catch ( Exception $e ) { |
| 230 |
// Handle the exception, such as logging the error elsewhere or sending a notification |
| 231 |
} |
| 232 |
} |
| 233 |
|
| 234 |
|
| 235 |
|
| 236 |
/* |
| 237 |
* |
| 238 |
* |
| 239 |
* Write Status logs |
| 240 |
* |
| 241 |
* |
| 242 |
**/ |
| 243 |
|
| 244 |
|
| 245 |
|
| 246 |
function mlsimport_debuglogs_per_plugin_old( $message ) { |
| 247 |
|
| 248 |
if ( is_array( $message ) ) { |
| 249 |
$message = wp_json_encode( $message ); |
| 250 |
} |
| 251 |
|
| 252 |
global $wp_filesystem; |
| 253 |
if ( empty( $wp_filesystem ) ) { |
| 254 |
require_once ABSPATH . '/wp-admin/includes/file.php'; |
| 255 |
WP_Filesystem(); |
| 256 |
} |
| 257 |
|
| 258 |
$path_status = WP_PLUGIN_DIR . '/mlsimport/logs/status_logs.log'; |
| 259 |
file_put_contents( $path_status, $message, LOCK_EX ); |
| 260 |
} |
| 261 |
function mlsimport_debuglogs_per_plugin( $message ) { |
| 262 |
|
| 263 |
if ( is_array( $message ) ) { |
| 264 |
$message = wp_json_encode( $message ); |
| 265 |
} |
| 266 |
|
| 267 |
if ( empty( $message ) ) { |
| 268 |
return; // Exit the function if there's nothing to log |
| 269 |
} |
| 270 |
|
| 271 |
$log_file_path = WP_PLUGIN_DIR . '/mlsimport/logs/status_logs.log'; |
| 272 |
|
| 273 |
// Check and create the directory for logs if it does not exist |
| 274 |
$log_dir = dirname( $log_file_path ); |
| 275 |
if ( ! file_exists( $log_dir ) ) { |
| 276 |
mkdir( $log_dir, 0755, true ); |
| 277 |
} |
| 278 |
|
| 279 |
// Error handling for file operations |
| 280 |
try { |
| 281 |
// Append the message to the log file with a newline and acquire an exclusive lock during writing |
| 282 |
file_put_contents( $log_file_path, $message . PHP_EOL, LOCK_EX ); |
| 283 |
} catch ( Exception $e ) { |
| 284 |
// Handle the exception, such as logging the error elsewhere or sending a notification |
| 285 |
} |
| 286 |
} |
| 287 |
|
| 288 |
|
| 289 |
|
| 290 |
|
| 291 |
|
| 292 |
/* |
| 293 |
* Cron job trigger |
| 294 |
* |
| 295 |
* |
| 296 |
* |
| 297 |
**/ |
| 298 |
|
| 299 |
|
| 300 |
// */5 * * * * wget http://example.com/check */2 |
| 301 |
add_action( 'init', 'mlsimport_trigger_cron_job' ); |
| 302 |
function mlsimport_trigger_cron_job() { |
| 303 |
// ?mlsimport_cron=yes |
| 304 |
if ( isset( $_REQUEST['mlsimport_cron'] ) && 'yes' === sanitize_text_field( wp_unslash( $_REQUEST['mlsimport_cron'] ) ) ) { |
| 305 |
$last_run = intval( get_option( 'mlsimport_last_server_cron' ) ); |
| 306 |
$now = time(); |
| 307 |
if ( 0 === intval($last_run) ) { |
| 308 |
update_option( 'mlsimport_last_server_cron', $now ); |
| 309 |
} |
| 310 |
|
| 311 |
if ( $last_run < $now - ( 60 * 60 * 2 ) ) { |
| 312 |
$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; |
| 313 |
// mlsimport_saas_event_mls_import_auto_function(); |
| 314 |
update_option( 'mlsimport_last_server_cron', $now ); |
| 315 |
} else { |
| 316 |
$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; |
| 317 |
} |
| 318 |
|
| 319 |
mlsimport_saas_single_write_import_custom_logs( $log, 'server_cron' ); |
| 320 |
} |
| 321 |
} |
| 322 |
|
| 323 |
|
| 324 |
|
| 325 |
function mlsimport_show_signup() { |
| 326 |
$affiliate_url = 'https://mlsimport.com'; |
| 327 |
if ( function_exists( 'wp_estate_init' ) ) { |
| 328 |
$affiliate_url = 'https://mlsimport.com/ref/1/?campaign=wpresidence'; |
| 329 |
} |
| 330 |
?> |
| 331 |
<div class="mlsimport_signup"> |
| 332 |
<h3><?php esc_html_e('Import MLS Listings into your Real Estate website', 'mlsimport'); ?></h3> |
| 333 |
<p><?php esc_html_e('Signup now and get 30-Days Free trial, no setup fee & cancel anytime at ', 'mlsimport'); ?><a href="<?php echo esc_url($affiliate_url); ?>" target="_blank">MlsImport.com</a></p> |
| 334 |
<a href="<?php echo esc_url($affiliate_url); ?>" class="button mlsimport_button mlsimport_signup_button" target="_blank"><?php esc_html_e('Create My Account', 'mlsimport'); ?></a> |
| 335 |
</div> |
| 336 |
<?php |
| 337 |
} |
| 338 |
|
| 339 |
/* |
| 340 |
|
| 341 |
// Register the hook before calling the scheduling function |
| 342 |
add_action('mlsimport_delete_empty_terms_batch_event', 'mlsimport_delete_empty_terms_batch', 10, 3); |
| 343 |
|
| 344 |
// Call this function with your taxonomy to start the deletion process |
| 345 |
mlsimport_schedule_empty_terms_deletion('property_area'); |
| 346 |
|
| 347 |
function mlsimport_schedule_empty_terms_deletion($taxonomy) { |
| 348 |
// Clear any existing scheduled events |
| 349 |
wp_clear_scheduled_hook('mlsimport_delete_empty_terms_batch_event'); |
| 350 |
|
| 351 |
// Schedule the first batch |
| 352 |
if (!wp_next_scheduled('mlsimport_delete_empty_terms_batch_event', array($taxonomy, 100, 0))) { |
| 353 |
wp_schedule_single_event(time() + 30, 'mlsimport_delete_empty_terms_batch_event', array($taxonomy, 100, 0)); |
| 354 |
echo 'Scheduled first batch event.<br>'; |
| 355 |
} else { |
| 356 |
echo 'First batch event already scheduled.<br>'; |
| 357 |
} |
| 358 |
} |
| 359 |
|
| 360 |
function mlsimport_delete_empty_terms_batch($taxonomy, $batch_size = 100, $offset = 0) { |
| 361 |
// Debugging output to confirm function execution |
| 362 |
echo 'Processing batch starting from offset: ' . $offset . '<br>'; |
| 363 |
|
| 364 |
// Get terms in batches |
| 365 |
$terms = get_terms(array( |
| 366 |
'taxonomy' => $taxonomy, |
| 367 |
'hide_empty' => false, // Include empty terms |
| 368 |
'orderby' => 'count', |
| 369 |
'order' => 'ASC', |
| 370 |
'number' => $batch_size, |
| 371 |
'offset' => $offset, |
| 372 |
)); |
| 373 |
|
| 374 |
if (!is_wp_error($terms) && !empty($terms)) { |
| 375 |
foreach ($terms as $term) { |
| 376 |
// Check if term count is zero |
| 377 |
if ($term->count == 0) { |
| 378 |
// Delete the term if it has no posts associated |
| 379 |
wp_delete_term($term->term_id, $taxonomy); |
| 380 |
echo 'Deleted term ID: ' . $term->term_id . '<br>'; |
| 381 |
} |
| 382 |
} |
| 383 |
|
| 384 |
// Schedule the next batch if more terms are found |
| 385 |
if (count($terms) == $batch_size) { |
| 386 |
$next_offset = $offset + $batch_size; |
| 387 |
wp_schedule_single_event(time() + 30, 'mlsimport_delete_empty_terms_batch_event', array($taxonomy, $batch_size, $next_offset)); |
| 388 |
echo 'Scheduled next batch event from offset: ' . $next_offset . '<br>'; |
| 389 |
} else { |
| 390 |
echo 'No more terms to process.<br>'; |
| 391 |
} |
| 392 |
} else { |
| 393 |
// Debugging output for error or completion |
| 394 |
if (is_wp_error($terms)) { |
| 395 |
echo 'Error fetching terms: ' . $terms->get_error_message() . '<br>'; |
| 396 |
} else { |
| 397 |
echo 'No more terms to process.<br>'; |
| 398 |
} |
| 399 |
} |
| 400 |
} |
| 401 |
|
| 402 |
|
| 403 |
*/ |
| 404 |
|
| 405 |
|
| 406 |
//add_action('admin_init', 'force_recount_all_terms'); |
| 407 |
function force_recount_all_terms() { |
| 408 |
global $wpdb; |
| 409 |
|
| 410 |
// Get all taxonomies |
| 411 |
$taxonomies = get_taxonomies([], 'names'); |
| 412 |
|
| 413 |
foreach ($taxonomies as $taxonomy) { |
| 414 |
// Get all terms for the taxonomy |
| 415 |
$terms = get_terms([ |
| 416 |
'taxonomy' => $taxonomy, |
| 417 |
'hide_empty' => false, // Include terms with 0 count |
| 418 |
'fields' => 'ids', // Get only the term IDs |
| 419 |
]); |
| 420 |
|
| 421 |
if (!is_wp_error($terms) && !empty($terms)) { |
| 422 |
// Get term_taxonomy_ids for these terms |
| 423 |
$term_taxonomy_ids = $wpdb->get_col($wpdb->prepare( |
| 424 |
"SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id IN (" . implode(',', array_map('intval', $terms)) . ")" |
| 425 |
)); |
| 426 |
|
| 427 |
// Update term counts |
| 428 |
if (!empty($term_taxonomy_ids)) { |
| 429 |
wp_update_term_count_now($term_taxonomy_ids, $taxonomy); |
| 430 |
} |
| 431 |
} |
| 432 |
} |
| 433 |
|
| 434 |
echo "Term counts have been recalculated for all taxonomies."; |
| 435 |
} |