Your max_execution_time setting in php is set to . Importing hundreds of listings requires extra time. Please set max_execution_time to 0 (unlimited). If that is not possible, set it to a minimum of 600 (10 minutes).
';
}
$foundItems = isset($mlsRequest['results']) ? intval($mlsRequest['results']) : 'none';
if ($foundItems === 'none') {
$mlsimport->admin->mlsimport_saas_check_mls_connection();
esc_html_e('Your Token was expired. Please refresh the page to renew it wait while we renew it.', 'mlsimport');
}
echo $this->generateMetaOptionsHtml($postId, $foundItems, $lastDate, $mlsimportItemHowMany, $mlsimportItemStatCron, $mlsimportMlsId, $fieldImport);
}
/**
* Generate Meta Options HTML
*
* @param int $postId The post ID.
* @param int $foundItems The number of found items.
* @param string $lastDate The last date checked.
* @param string $mlsimportItemHowMany How many items to import.
* @param string $mlsimportItemStatCron The status of the cron job.
* @param int $mlsimportMlsId The MLS import ID.
* @param array $fieldImport The fields to import.
* @return string The generated HTML.
*/
private function generateMetaOptionsHtml($postId, $foundItems, $lastDate, $mlsimportItemHowMany, $mlsimportItemStatCron, $mlsimportMlsId, $fieldImport) {
ob_start();
?>
1. Set the import parameters.
2. Hit Publish or Update, otherwise import will not work correctly.
3. Click the Start Import button. Most MLS limit the import number to 1000. If you need to import more create additional import items.
4. Press the Update button after you make any change in the import settings.
= 500): ?>
listings. If you decide to import all of them make sure your server database can handle the load. Please do a database backup before initial import.
"/>
plugin_name . '_admin_options');
$mlsId = '';
if (isset($options['mlsimport_mls_name'])) {
$mlsId = sanitize_text_field(trim($options['mlsimport_mls_name']));
}
if ($mlsId > 5000) {
$fieldImport['PropertyType']['multiple'] = 'no';
}
if ($mlsId >= 7000) {
// there is no such thing for realtor.ca
unset($fieldImport['PropertyType']);
}
foreach ($fieldImport as $key => $field):
$nameCheck = strtolower('mlsimport_item_' . $key . '_check');
$name = strtolower('mlsimport_item_' . $key);
$value = get_post_meta($postId, $name, true);
$valueCheck = get_post_meta($postId, $nameCheck, true);
$extraClass = '';
if ('extraCity' === $key || 'extraCounty' === $key) {
$extraClass = ' mlsimport_hidden_field_button button mlsimport_button';
}
?>
mlsimport_saas_update_last_date( $item_id );
}
return $last_date;
}
/**
*
*
* Save Last date
*/
public function mlsimport_saas_update_last_date( $item_id ) {
$unix_time = current_time( 'timestamp', 0 ) - ( 2 * 60 * 60 );
print $last_date_to_save = date( 'Y-m-d\TH:i', $unix_time );
update_post_meta( $item_id, 'mlsimport_last_date', $last_date_to_save );
return $last_date_to_save;
}
/**
* Check and process MLSimport item for modified listings in the last 2 hours.
* Optimized for memory: logs memory, unsets large arrays, and triggers garbage collection.
*
* @param int $item_id
* @return void
*/
public function mlsimport_saas_start_cron_links_per_item( $item_id ) {
// Log memory before start
//error_log("[MLSimport] Start $item_id, memory: " . (memory_get_usage(true) / 1024 / 1024) . " MB");
$last_date = $this->mlsimport_saas_get_last_date( $item_id );
print 'MLSitem id: ' . $item_id . ' - ';
esc_html_e('date to consider: ','mlsimport');
print esc_html($last_date) . '. ';
// Make request to MLS API
$mlsrequest = $this->mlsimport_make_listing_requests( $item_id, $last_date );
//error_log("[MLSimport] After mlsimport_make_listing_requests, memory: " . (memory_get_usage(true) / 1024 / 1024) . " MB");
$found_items = 0;
if ( isset( $mlsrequest['results'] ) ) {
$found_items = intval( $mlsrequest['results'] );
} else {
delete_transient( 'mlsimport_saas_token' );
}
print esc_html__('We found ','mlsimport') . esc_html( $found_items ) . ' listings.' . PHP_EOL;
// Only process if items found
if ( $found_items > 0 ) {
$item_id_array = array(
'item_id' => $item_id,
'how_many' => 0,
'max_number' => $found_items,
'batch_counter' => 1,
);
// Potentially large array, log memory before/after
$attachments_to_move = (array) $this->mlsimport_saas_generate_import_requests_per_item( $item_id_array, $last_date );
//error_log("[MLSimport] After generate_import_requests_per_item, memory: " . (memory_get_usage(true) / 1024 / 1024) . " MB");
// Store in post meta (beware if array is huge)
update_post_meta( $item_id, 'mlsimport_spawn_status_cron_job', 'started' );
update_post_meta( $item_id, 'mlsimport_cron_attach_to_move_' . $item_id, $attachments_to_move );
// Save last date for next run
$this->mlsimport_saas_update_last_date( $item_id );
// Prepare and pass only necessary arguments to background process
$attachments_to_send = array(
'args' => array(
'attachments_to_move' => $item_id,
'item_id_array' => $item_id_array,
),
);
$this->mlsimport_background_process_per_item_cron_function( $attachments_to_send['args'] );
// Unset large arrays/objects after use
unset($attachments_to_move, $attachments_to_send, $mlsrequest, $item_id_array);
gc_collect_cycles();
//error_log("[MLSimport] End processing $item_id, memory: " . (memory_get_usage(true) / 1024 / 1024) . " MB");
}
}
/**
* Reconciliation log (optimized, batched, memory logged)
*/
public function mlsimport_saas_start_doing_reconciliation() {
global $mlsimport, $wpdb;
// Get all MLS keys in memory (we assume this is necessary for lookup)
$mls_data = $this->mlsimport_saas_get_mls_reconciliation_data();
$listingKey_in_MLS = $mls_data['all_data'] ?? [];
unset($mls_data);
gc_collect_cycles();
if (empty($listingKey_in_MLS)) {
return;
}
// Flip for fast lookup
$listingKey_in_MLS = array_flip($listingKey_in_MLS);
// Batch fetch local listings
$batch = 1000;
$offset = 0;
$to_delete = 0;
$counter = 0;
$mlsimport_preload_all_mls_item_status_meta = $this->mlsimport_preload_all_mls_item_status_meta();
//print_r($mlsimport_preload_all_mls_item_status_meta);
do {
$local = $wpdb->get_results(
$wpdb->prepare(
"SELECT
p.ID,
listingkey_meta.meta_value AS listingkey,
inserted_meta.meta_value AS mlsimport_item_inserted
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} listingkey_meta
ON p.ID = listingkey_meta.post_id
AND listingkey_meta.meta_key = %s
LEFT JOIN {$wpdb->postmeta} inserted_meta
ON p.ID = inserted_meta.post_id
AND inserted_meta.meta_key = %s
WHERE p.post_status NOT IN ('draft', 'trash')
LIMIT %d OFFSET %d",
'ListingKey',
'MLSimport_item_inserted',
$batch,
$offset
),
ARRAY_A
);
$count = count($local);
foreach ($local as $item) {
$listingkey = $item['listingkey']; // not 'meta_value' anymore
$property_id = $item['ID'];
$mlsimportItemId = $item['mlsimport_item_inserted'];
++$counter;
// IN MLS
if (isset($listingKey_in_MLS[$listingkey])) {
if (!empty($mlsimportItemId) && isset($mlsimport_preload_all_mls_item_status_meta[$mlsimportItemId])) {
$mlsimport_item_standardstatus = $mlsimport_preload_all_mls_item_status_meta[$mlsimportItemId]['mlsimport_item_standardstatus'] ?? null;
} else {
$mlsimport_item_standardstatus = null;
}
$keep_when_in_mls = $mlsimport->admin->theme_importer->check_if_delete_when_status_when_in_mls($property_id,$mlsimport_item_standardstatus);
if (!$keep_when_in_mls) {
++$to_delete;
$mlsimport->admin->theme_importer->mlsimportSaasDeletePropertyViaMysql($property_id, $listingkey);
}
} else {
// NOT IN MLS
if (!empty($mlsimportItemId) && isset($mlsimport_preload_all_mls_item_status_meta[$mlsimportItemId])) {
$mlsimport_item_standardstatus = $mlsimport_preload_all_mls_item_status_meta[$mlsimportItemId]['mlsimport_item_standardstatus'] ?? null;
$mlsimport_item_standardstatusdelete = $mlsimport_preload_all_mls_item_status_meta[$mlsimportItemId]['mlsimport_item_standardstatusdelete'] ?? null;
} else {
$mlsimport_item_standardstatus = null;
$mlsimport_item_standardstatusdelete = null;
}
$keep = $mlsimport->admin->theme_importer->check_if_delete_when_status($property_id, $mlsimport_item_standardstatus, $mlsimport_item_standardstatusdelete);
if (!$keep) {
++$to_delete;
$mlsimport->admin->theme_importer->mlsimportSaasDeletePropertyViaMysql($property_id, $listingkey);
}
}
// Memory housekeeping
unset($listingkey, $property_id, $mlsimportItemId, $mlsImportItemStatus, $mlsImportItemStatusDelete, $keep_when_in_mls, $keep);
if ($counter % 250 == 0) {
gc_collect_cycles();
}
}
unset($local);
gc_collect_cycles();
$offset += $batch;
} while ($count === $batch);
print esc_html(' to delete:' . $to_delete);
// Final cleanup
unset($listingKey_in_MLS);
gc_collect_cycles();
return;
}
/**
* Preload all status meta for ALL mlsimport_item posts in ONE QUERY.
* Returns: [mlsimport_item_id => ['mlsimport_item_standardstatus' => ..., 'mlsimport_item_standardstatusdelete' => ...], ...]
*/
function mlsimport_preload_all_mls_item_status_meta() {
global $wpdb;
// Only 1 query: join posts and postmeta, grab both metas
$sql = "
SELECT p.ID as post_id, pm.meta_key, pm.meta_value
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE p.post_type = 'mlsimport_item'
AND pm.meta_key IN ('mlsimport_item_standardstatus', 'mlsimport_item_standardstatusdelete')
";
$rows = $wpdb->get_results($sql);
$meta = [];
foreach ($rows as $row) {
if (!isset($meta[$row->post_id])) {
$meta[$row->post_id] = [
'mlsimport_item_standardstatus' => null,
'mlsimport_item_standardstatusdelete' => null
];
}
$meta[$row->post_id][$row->meta_key] = maybe_unserialize($row->meta_value);
}
return $meta;
}
/**
*
*
* Requestq Reconciliation log
*/
public function mlsimport_saas_get_mls_reconciliation_data() {
$arguments = array();
$answer = $this->theme_importer->globalApiRequestCurlSaas( 'reconciliation', $arguments, 'GET' );
return $answer;
}
/**
*
*
* Reconciliation get local data
*/
public function mlsimport_saas_get_all_meta_values($key) {
global $wpdb;
$result = $wpdb->get_results(
$wpdb->prepare(
"
SELECT pm.meta_value, p.ID
FROM {$wpdb->postmeta} pm
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = %s
AND p.post_status = 'publish'
",
$key
),
ARRAY_A // Lighter than OBJECT, unless you need objects
);
return $result;
}
/*
* Do api Listing Requests
*
*
*
*
* */
public function mlsimport_make_listing_requests( $item_id, $last_date = '', $skip = '', $top = '' ) {
$options = get_option( $this->plugin_name . '_admin_options' );
$mls_id = '';
if ( isset( $options['mlsimport_mls_name'] ) ) {
$mls_id = sanitize_text_field( trim( $options['mlsimport_mls_name'] ) );
}
$arguments = $this->mlsimport_saas_make_listing_requests_arguments( $item_id, $last_date, $skip, $top );
if (
$mls_id > 5000 && $mls_id < 6000 &&
( ! isset( $arguments['property_type'] ) or
( isset( $arguments['property_type'] ) && '' === $arguments['property_type'] ) or
( isset( $arguments['property_type'][0] ) && '' === $arguments['property_type'][0] )
)
) {
return array(
'success' => false,
'type' => 'rapattoni',
'message' => esc_html__( 'This MLS requires to have one item selected from "Property Action Category" dropdown', 'mlsimport' ),
);
}
$potential_leght = strlen( wp_json_encode( $arguments ) );
if ( $potential_leght > 1750 ) {
return array(
'success' => false,
'potential_leght' => $potential_leght,
'message' => esc_html__( 'You have too many parameters selected. Split the import beween multiple MLS Import items: For ex : Import per County instead of selecting 10 cities or import listing between certain price range.', 'mlsimport' ),
);
}
//print_r($arguments);
//print '----------------------------'.PHP_EOL;
$answer = $this->theme_importer->globalApiRequestCurlSaas( 'listings', $arguments, 'POST' );
$answer['potential_leght'] = $potential_leght;
return ( $answer );
}
/*
* Create Api query arguments
*
*
*
*
*
* */
public function mlsimport_saas_make_listing_requests_arguments( $item_id, $last_date = '', $skip = '', $top = '' ) {
$options = get_option( $this->plugin_name . '_admin_options' );
if ( isset( $options['mlsimport_mls_name'] ) ) {
$mls_id = intval( $options['mlsimport_mls_name'] );
} else {
return '';
}
if ( isset( $options['mlsimport_theme_used'] ) ) {
$theme_id = intval( $options['mlsimport_theme_used'] );
} else {
return '';
}
$values = array();
$values['mls_id'] = $mls_id;
$values['theme_id'] = $theme_id;
if ( '' !== $top ) {
$values['top'] = $top;
$values['skip'] = intval( $skip );
}
// // add price
$mlsimport_item_min_price = get_post_meta( $item_id, 'mlsimport_item_min_price', true );
$mlsimport_item_max_price = get_post_meta( $item_id, 'mlsimport_item_max_price', true );
if ( '' !== $mlsimport_item_min_price && '' !== $mlsimport_item_max_price ) {
$values['list_price_min'] = floatval( $mlsimport_item_min_price );
$values['list_price_max'] = floatval( $mlsimport_item_max_price );
}
// add city
$values = $this->mls_import_return_multiple_param_value( 'city', $item_id, 'city', $values );
// add county
$values = $this->mls_import_return_multiple_param_value( 'countyorparish', $item_id, 'county_or_parish', $values );
// add MLSAreaMajor
$values = $this->mls_import_saas_add_to_parms_input( 'MLSAreaMajor', $item_id, 'mls_area_major', $values );
// add SubdivisionName
$values = $this->mls_import_saas_add_to_parms_input( 'SubdivisionName', $item_id, 'subdivision_name', $values );
// add postal code
$values = $this->mls_import_saas_add_to_parms_input( 'PostalCode', $item_id, 'postal_code', $values );
// add status
if ( 111 !== $mls_id ) { // edmonton check
$values = $this->mls_import_return_multiple_param_value( 'StandardStatus', $item_id, 'status', $values );
}
// add property_subtype
$values = $this->mls_import_return_multiple_param_value( 'PropertySubType', $item_id, 'property_subtype', $values );
// add property_type
$values = $this->mls_import_return_multiple_param_value( 'PropertyType', $item_id, 'property_type', $values );
// rapattoni exception
if ( $mls_id > 5000 &&
( isset($values['property_type']) && $values['property_type'] !='' ) ) {
$values = $this->mls_import_saas_add_to_parms_input( 'PropertyType', $item_id, 'property_type', $values );
$temp = $values['property_type'];
$temp = str_replace( ' ', '', $temp );
$values['property_type'] = array();
$values['property_type'][] = $temp;
}
// add internet_entirelisting_displayyn
$values = $this->mls_import_saas_add_to_parms_input( 'InternetEntireListingDisplayYN', $item_id, 'internet_entirelisting_displayyn', $values );
// add internet_address_displayyn
$values = $this->mls_import_saas_add_to_parms_input( 'InternetAddressDisplayYN', $item_id, 'internet_address_displayyn', $values );
// add ListAgentKey
$values = $this->mls_import_saas_add_to_parms_input( 'ListAgentKey', $item_id, 'list_agentkey', $values );
// add ListAgentKey
$values = $this->mls_import_saas_add_to_parms_input( 'ListAgentMlsId', $item_id, 'list_agentmlsid', $values );
// add ListOfficeKey
$values = $this->mls_import_saas_add_to_parms_input( 'ListOfficeKey', $item_id, 'list_officekey', $values );
// add ListOfficeMlsId
$values = $this->mls_import_saas_add_to_parms_input( 'ListOfficeMlsId', $item_id, 'list_officemlsid', $values );
// add ListingId
$values = $this->mls_import_saas_add_to_parms_input( 'ListingId', $item_id, 'listingid', $values );
//add Exclude_ListOfficeKey
$values = $this->mls_import_saas_add_to_parms_input( 'Exclude_ListOfficeKey', $item_id, 'exclude_list_officekey', $values );
// add Exclude_ListOfficeMlsId
$values = $this->mls_import_saas_add_to_parms_input( 'Exclude_ListOfficeMlsId', $item_id, 'exclude_list_officemlsid', $values );
//add Exclude_ListAgentKey
$values = $this->mls_import_saas_add_to_parms_input( 'Exclude_ListAgentKey', $item_id, 'exclude_list_agentkey', $values );
// add Exclude_ListAgentMlsId
$values = $this->mls_import_saas_add_to_parms_input( 'Exclude_ListAgentMlsId', $item_id, 'exclude_list_agentmlsid', $values );
// if we have realtorca
if ($mls_id >= 7000 && $last_date!=='') {
$dateTime_realtorca = new DateTime($last_date, new DateTimeZone('UTC'));
// Format with seconds and UTC timezone marker
$last_date = $dateTime_realtorca->format('Y-m-d\TH:i:s.000\Z');
}
if ( '' !== $last_date ) {
$values['modification_time'] = $last_date;
}
return( $values );
}
/*
*
* add input items to parameters array
*
*/
public function mls_import_saas_add_to_parms_input( $key, $post_id, $new_name, $all_values ) {
$name = strtolower( 'mlsimport_item_' . $key );
$value = get_post_meta( $post_id, $name, true );
if ( '' !== $value ) {
$all_values[ $new_name ] = $value;
}
return $all_values;
}
/*
*
* add list items to parameters array
*
*/
public function mls_import_return_multiple_param_value( $key, $post_id, $new_name, $all_values ) {
$name_check = strtolower( 'mlsimport_item_' . $key . '_check' );
$name = strtolower( 'mlsimport_item_' . $key );
$value = get_post_meta( $post_id, $name, true );
// add extra county - should be moved into function if pass tests
if ( 'countyorparish' === $key ) {
$extracounty_values = get_post_meta( $post_id, 'mlsimport_item_extracounty', true );
if ( '' !== $extracounty_values ) {
$extracounty_array = explode( ',', $extracounty_values );
if ( ! is_array( $value ) ) {
if ( '' === $value ) {
$value = array();
} else {
$value = array( $value );
}
}
foreach ( $extracounty_array as $extra ) {
$value[] = $extra;
}
}
}
// add extra city - should be moved into function if pass tests
if ( 'city' === $key ) {
$extracity_values = get_post_meta( $post_id, 'mlsimport_item_extracity', true );
if ( '' !== $extracity_values ) {
$extracity_array = explode( ',', $extracity_values );
if ( ! is_array( $value ) ) {
if ('' === $value ) {
$value = array();
} else {
$value = array( $value );
}
}
foreach ( $extracity_array as $extra ) {
$value[] = $extra;
}
}
}
$value_check = get_post_meta( $post_id, $name_check, true );
if ( 0 === intval($value_check) && '' !== $value ) {
$all_values[ $new_name ] = $value;
}
// status exception
if ( 'status' === $new_name ) {
$all_values[ $new_name ] = $value;
}
return $all_values;
}
/*
*
* All Enums fiels to be used on MLS import Item
*
*
*
*
*
*
* */
public function mlsimport_saas_return_mls_fields() {
$mlsimport_mls_metadata_mls_enums = get_option( 'mlsimport_mls_metadata_mls_enums', '' );
if ( '' === $mlsimport_mls_metadata_mls_enums ) {
?>
Please select the import fields(from MLS Import Settings) before starting a MLS import process.
array(
'label' => esc_html__( 'Select cities', 'mlsimport' ),
'description' => esc_html__( 'Select the cities from where we will import data.', 'mlsimport' ),
'type' => 'select',
'multiple' => 'yes',
'values' => $city_array,
),
'extraCity' => array(
'label' => esc_html__( 'Add extra Cities', 'mlsimport' ),
'description' => esc_html__( 'Add extra cities, separated by comma. They need to be written exactly like they are stored in MLS (for example all caps)', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
'values' => $extracity_values,
),
'CountyOrParish' => array(
'label' => esc_html__( 'Select Counties', 'mlsimport' ),
'description' => esc_html__( 'Select the counties from where we will import data.', 'mlsimport' ),
'type' => 'select',
'multiple' => 'yes',
'values' => $county_array,
'show_extra_field' => true,
),
'extraCounty' => array(
'label' => esc_html__( 'Add extra Counties', 'mlsimport' ),
'description' => esc_html__( 'Add extra counties, separated by comma. They need to be written exactly like they are stored in MLS (for example all caps)', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
'values' => $extracounty_values,
),
'MLSAreaMajor' => array(
'label' => esc_html__( 'MLS Area Major', 'mlsimport' ),
'description' => esc_html__( 'Filter listings by MLSAreaMajor.', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'SubdivisionName' => array(
'label' => esc_html__( 'Subdivision Name', 'mlsimport' ),
'description' => esc_html__( 'Filter listings by SubDivisionName.', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'PostalCode' => array(
'label' => esc_html__( 'Select Postal Code', 'mlsimport' ),
'description' => esc_html__( 'Select the PostalCode from where to import listings. Works with only one PostalCode.', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'PropertySubType' => array(
'label' => esc_html__( 'Select Property Category', 'mlsimport' ),
'description' => esc_html__( 'Property Category', 'mlsimport' ),
'type' => 'select',
'multiple' => 'yes',
'values' => $propertysubtype_array,
),
'PropertyType' => array(
'label' => esc_html__( 'Select Property Action Category', 'mlsimport' ),
'description' => esc_html__( 'Property Action Category', 'mlsimport' ),
'type' => 'select',
'multiple' => 'yes',
'values' => $propertytype_array,
),
'StandardStatus' => array(
'label' => esc_html__( 'Select Status', 'mlsimport' ),
'description' => __( 'The list is auto-populated with MLS available statuses. To select multiple statuses, use Ctrl (Windows) or Command (Mac).', 'mlsimport' ),
'type' => 'select',
'multiple' => 'yes',
'values' => $standardstatus_array,
),
'StandardStatusDelete' => array(
'label' => esc_html__( 'Delete Statuses', 'mlsimport' ),
'description' => __( 'Properties with these statuses will be deleted from your website after they are removed from MLS database. If you edit the field after importing, the changes will NOT apply to listings that have already been imported.', 'mlsimport' ),
'type' => 'select',
'multiple' => 'yes',
'values' => $standardstatus_delete_array,
),
'InternetEntireListingDisplayYN' => array(
'label' => esc_html__( 'Internet Entire Listing Display ', 'mlsimport'),
'description' => esc_html__( 'A yes/no field that states the seller has allowed the listing to be displayed on Internet sites.', 'mlsimport' ),
'type' => 'select',
'multiple' => 'no',
'values' => array(
'yes',
'no',
),
),
'InternetAddressDisplayYN' => array(
'label' => esc_html__( 'Internet Address display', 'mlsimport' ),
'description' => esc_html__( 'A yes/no field that states the seller has allowed the listing address to be displayed on Internet sites.', 'mlsimport' ),
'type' => 'select',
'multiple' => 'no',
'values' => array(
'yes',
'no',
),
),
'ListAgentKey' => array(
'label' => esc_html__( 'ListAgentKey', 'mlsimport' ),
'description' => esc_html__( 'Import listings from a specific Agent (contact your MLS for this information)', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'ListAgentMlsId' => array(
'label' => esc_html__( 'ListAgentMlsId', 'mlsimport' ),
'description' => esc_html__( 'Import listings from a specific Agent (contact your MLS for this information)', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'ListOfficeKey' => array(
'label' => esc_html__( 'ListOfficeKey', 'mlsimport' ),
'description' => esc_html__( 'Import listings from a specific Office (contact your MLS for this information)', 'mlsimport'),
'type' => 'input',
'multiple' => 'no',
),
'ListOfficeMlsId' => array(
'label' => esc_html__( 'ListOfficeMlsId', 'mlsimport' ),
'description' => esc_html__( 'Import listings from a specific Office (contact your MLS for this information)', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'ListingId' => array(
'label' => esc_html__( 'ListingId', 'mlsimport' ),
'description' => esc_html__( 'Import One Property Only via parameter ListingID. If this does not work for you, please contact us to check if the field exists in your MLS.', 'mlsimport'),
'type' => 'input',
'multiple' => 'no',
),
'Exclude_ListOfficeMlsId' => array(
'label' => esc_html__( 'Exclude listings with ListOfficeMlsId', 'mlsimport' ),
'description' => esc_html__( 'Exclude listings that belong to one or more ListOfficeMlsId.', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'Exclude_ListOfficeKey' => array(
'label' => esc_html__( 'Exclude listings with ListOfficeKey', 'mlsimport' ),
'description' => esc_html__( 'Exclude listings that belong to one or more ListOfficeKey', 'mlsimport'),
'type' => 'input',
'multiple' => 'no',
),
'Exclude_ListAgentMlsId' => array(
'label' => esc_html__( 'Exclude listings with ListAgentMlsId', 'mlsimport' ),
'description' => esc_html__( 'Exclude listings that belong to one or more ListAgentMlsId.', 'mlsimport' ),
'type' => 'input',
'multiple' => 'no',
),
'Exclude_ListAgentKey' => array(
'label' => esc_html__( 'Exclude listings with ListAgentKey ', 'mlsimport' ),
'description' => esc_html__( 'Exclude listings that belong to one or more ListAgentKey ', 'mlsimport'),
'type' => 'input',
'multiple' => 'no',
),
);
return $field_import;
}
/**
*
*
* AYsnc Test
*/
public function mlsimport_move_files_per_item() {
check_ajax_referer( 'mlsimport_item_actions', 'security' );
$post_id = 0;
$how_many = 0;
$max_number = 0;
if(isset( $_POST['post_id'] )){
$post_id = intval( $_POST['post_id'] );
}
if(isset( $_POST['how_many'] )){
$how_many = intval( $_POST['how_many'] );
}
if(isset( $_POST['post_number'] )){
$max_number = intval( $_POST['post_number'] );
}
$is_onboard=intval($_POST['is_onboard']);
update_option( 'mlsimport_force_stop_' . $post_id, 'no', false );
$item_id_array = array(
'item_id' => $post_id,
'how_many' => $how_many,
'max_number' => $max_number,
'batch_counter' => 1,
);
//error_log(json_encode($item_id_array));
//error_log("starting post id ".$post_id);
update_post_meta( $post_id, 'mlsimport_attach_to_move_' . $post_id, '' );
$attachments_to_move = (array) $this->mlsimport_saas_generate_import_requests_per_item( $item_id_array );
update_post_meta( $post_id, 'mlsimport_attach_to_move_' . $post_id, $attachments_to_move );
// net stat data
update_post_meta( $post_id, 'mlsimport_progress_properties', 0 );
update_post_meta( $post_id, 'mlsimport_progress_batches', 0 );
update_post_meta( $post_id, 'mlsimport_progress_memory', 0 );
$attachments_to_send = array(
'args' => array(
'attachments_to_move' => $post_id,
'item_id_array' => $item_id_array,
'is_onboard' =>$is_onboard,
),
);
mlsimport_saas_single_write_import_custom_logs( 'Preparing the import. Please hold on.' . PHP_EOL );
mlsimport_debuglogs_per_plugin( 'Preparing the import. Please hold on.' . PHP_EOL );
update_post_meta( $post_id, 'mlsimport_spawn_status', 'started' );
// old
as_enqueue_async_action( 'mlsimport_background_process_per_item', $attachments_to_send );
// Remove any pending async jobs for this item and enqueue a unique one
//bad ideea
// as_unschedule_all_actions( 'mlsimport_background_process_per_item', $attachments_to_send );
//as_enqueue_async_action( 'mlsimport_background_process_per_item', $attachments_to_send, '', true );
spawn_cron();
unset( $attachments_to_send ); print'before die';
die();
}
/**
* Process MLS Import attachments via background cron.
* Memory-optimized with detailed memory usage logging.
*
* @param array $input_arg
* @return void
*/
public function mlsimport_background_process_per_item_cron_function( $input_arg ) {
global $mlsimport;
$log = 'In cron processing function ->' . wp_json_encode( $input_arg['item_id_array'] ) . PHP_EOL;
mlsimport_saas_single_write_import_custom_logs( $log, 'cron' );
mlsimport_saas_single_write_import_custom_logs( '[Memory] Start: ' . (memory_get_usage(true) / 1024 / 1024) . ' MB', 'cron' );
// Load attachments to move from post meta
$attachments_to_move = get_post_meta(
$input_arg['item_id_array']['item_id'],
'mlsimport_cron_attach_to_move_' . $input_arg['item_id_array']['item_id'],
true
);
$log = '[Memory] After loading attachments: ' . (memory_get_usage(true) / 1024 / 1024) . ' MB' . PHP_EOL;
mlsimport_saas_single_write_import_custom_logs( $log, 'cron' );
if (!empty($attachments_to_move) && is_array($attachments_to_move)) {
foreach ($attachments_to_move as $key => $import_arguments) {
// Optionally clear any cache for this batch
if ( isset($GLOBALS['wp_object_cache']) ) {
$GLOBALS['wp_object_cache']->delete('mlsimport_force_stop_' . $input_arg['item_id_array']['item_id'], 'options');
}
$log = '[Memory] Before API batch ' . $key . ': ' . (memory_get_usage(true) / 1024 / 1024) . ' MB' . PHP_EOL;
mlsimport_saas_single_write_import_custom_logs( $log, 'cron' );
// API call
$api_call_array = $this->theme_importer->globalApiRequestCurlSaas('listings', $import_arguments, 'POST');
$log = '[Memory] After API batch ' . $key . ': ' . (memory_get_usage(true) / 1024 / 1024) . ' MB' . PHP_EOL;
mlsimport_saas_single_write_import_custom_logs( $log, 'cron' );
// Parse/process response
$mlsimport->admin->theme_importer->mlsimportSaasCronParseSearchArrayPerItem(
$api_call_array, $input_arg['item_id_array'], $key
);
// Free per-iteration memory
unset($api_call_array, $import_arguments);
gc_collect_cycles();
$log = '[Memory] After cleanup batch ' . $key . ': ' . (memory_get_usage(true) / 1024 / 1024) . ' MB' . PHP_EOL;
mlsimport_saas_single_write_import_custom_logs( $log, 'cron' );
}
}
mlsimport_saas_single_write_import_custom_logs('[Memory] End: ' . (memory_get_usage(true) / 1024 / 1024) . ' MB', 'cron' );
mlsimport_saas_single_write_import_custom_logs('CRON JOB Import Completed ' . PHP_EOL, 'cron');
mlsimport_debuglogs_per_plugin('CRON JOB Import Completed ' . PHP_EOL);
update_post_meta($input_arg['item_id_array']['item_id'], 'mlsimport_spawn_status', 'completed');
unset($attachments_to_move, $input_arg, $log);
gc_collect_cycles();
}
/**
*
*
* Generate import Requests per item
*/
public function mlsimport_saas_generate_import_requests_per_item( $item_id_array, $last_date = '' ) {
$import_step = 25;
$prop_id = $item_id_array['item_id'];
$max_found = $item_id_array['max_number'];
$how_many = $item_id_array['how_many'];
if ( 0=== intval($how_many) ) {
$how_many = $max_found;
}
if ( $how_many > $max_found ) {
$how_many = $max_found;
}
$search_url_step = '';
$urls_array = array();
$skip = 0;
if ( $how_many > 10000 ) {
$how_many = 10000;
}
//error_log('updating for '.$prop_id.' with'. $how_many);
update_post_meta($prop_id,'mlsimport_task_to_import', intval($how_many) );
if ( $how_many < $import_step ) {
$import_step = $how_many;
}
while ( $skip < $how_many ) {
//$batch_step = min( $import_step, $how_many - $skip );
//$search_url_step = $this->mlsimport_saas_make_listing_requests_arguments( $prop_id, $last_date, $skip, $import_step );
//$skip = $skip + $import_step;
//$urls_array[] = $search_url_step;
// Determine how many items to request for this batch.
$batch_step = min( $import_step, $how_many - $skip );
// Build the request arguments using the remaining count.
$search_url_step = $this->mlsimport_saas_make_listing_requests_arguments( $prop_id, $last_date, $skip, $batch_step );
$skip += $batch_step;
$urls_array[] = $search_url_step;
}
return $urls_array;
}
/**
* Process Async function
*/
public function mlsimport_background_process_per_item_function( $input_arg ) {
$mlsimportItemId = $input_arg['item_id_array']['item_id'];
$log_prefix = 'In processing function - Item ID: ' . $mlsimportItemId . ' -> ';
mlsimport_saas_single_write_import_custom_logs( $log_prefix . wp_json_encode( $input_arg['item_id_array'] ) . PHP_EOL );
// Get from MLS Import the big argument array only once
$attachments_to_move = get_post_meta( $mlsimportItemId, 'mlsimport_attach_to_move_' . $mlsimportItemId, true );
// Retrieve all meta data in one go to reduce database queries
$mlsimport_item_option_data = array(
'mlsimport_item_standardstatus' => get_post_meta( $mlsimportItemId, 'mlsimport_item_standardstatus', true ),
'mlsimport_item_standardstatusdelete' => get_post_meta( $mlsimportItemId, 'mlsimport_item_standardstatusdelete', true ),
'mlsimport_item_property_user' => get_post_meta( $mlsimportItemId, 'mlsimport_item_property_user', true ),
'mlsimport_item_agent' => get_post_meta( $mlsimportItemId, 'mlsimport_item_agent', true ),
'mlsimport_item_property_status' => get_post_meta( $mlsimportItemId, 'mlsimport_item_property_status', true ),
);
$total_batches = count( $attachments_to_move );
//error_log('$total_batches '. json_encode($total_batches) );
// removed because $this
global $mlsimport;
$log = 'In processing function $attachments_to_move ->' . wp_json_encode( $attachments_to_move ) . PHP_EOL;
mlsimport_saas_single_write_import_custom_logs( $log );
foreach ( $attachments_to_move as $key => $import_arguments ) {
// reconsider use
// $GLOBALS['wp_object_cache']->delete('mlsimport_force_stop_' . $input_arg['item_id_array']['item_id'], 'options');
$status = get_option( 'mlsimport_force_stop_' . $mlsimportItemId );
if ( 'no' === $status ) {
// Clear memory before processing each batch
wp_cache_flush();
gc_collect_cycles();
// wp_cache_flush();
$mem_usage = memory_get_usage( true );
$mem_usage_show = round( $mem_usage / 1048576, 2 );
update_post_meta( $mlsimportItemId, 'mlsimport_progress_batches', $key + 1 );
update_post_meta( $mlsimportItemId, 'mlsimport_progress_memory', $mem_usage_show );
mlsimport_saas_single_write_import_custom_logs( $log );
$log = 'Parsing import batch: ' . ( $key + 1 ) . ' of ' . $total_batches . '. Memory used: ' . $mem_usage_show . ' MB.' . PHP_EOL;
//error_log($log);
// Combine logs and reduce function calls
mlsimport_saas_single_write_import_custom_logs( $log );
mlsimport_debuglogs_per_plugin( $log );
print esc_html($log);
$api_call_array = $this->theme_importer->globalApiRequestCurlSaas( 'listings', $import_arguments, 'POST' );
$mlsimport->admin->theme_importer->mlsimportSaasParseSearchArrayPerItem( $api_call_array, $input_arg['item_id_array'], $key, $mlsimport_item_option_data );
// Explicitly unset large variables after each batch
unset($api_call_array);
// Force garbage collection again after processing
wp_cache_flush();
gc_collect_cycles();
// Add a small delay to allow memory to be freed
if (($key + 1) < $total_batches) {
usleep(100000); // 100ms pause between batches
}
} else {
$final_mem_usage = memory_get_usage( true );
$final_mem_usage_show = round( $final_mem_usage / 1048576, 2 );
update_post_meta( $mlsimportItemId, 'mlsimport_spawn_status', 'completed' );
delete_post_meta( $mlsimportItemId, 'mlsimport_attach_to_move_' . $mlsimportItemId );
//new stats
update_post_meta( $mlsimportItemId, 'mlsimport_progress_batches', $total_batches );
update_post_meta( $mlsimportItemId, 'mlsimport_progress_memory', $final_mem_usage_show );
mlsimport_saas_single_write_import_custom_logs( PHP_EOL . 'Parsing importing link FORCE STOP : ' );
mlsimport_debuglogs_per_plugin( 'Parsing importing link FORCE STOP : ' );
break; // Exit the loop if forced to stop
}
}
mlsimport_saas_single_write_import_custom_logs( 'Import Completed ' . PHP_EOL );
mlsimport_debuglogs_per_plugin( 'Import Completed ' . PHP_EOL );
update_post_meta( $mlsimportItemId, 'mlsimport_spawn_status', 'completed' );
delete_post_meta( $mlsimportItemId, 'mlsimport_attach_to_move_' . $mlsimportItemId );
// Final cleanup
unset($attachments_to_move);
unset($mlsimport_item_option_data);
unset($input_arg);
unset($log);
// One final garbage collection
wp_cache_flush();
gc_collect_cycles();
}
/**
*
*
*
* update log function
*/
public function mlsimport_logger_per_item() {
//check_ajax_referer( 'mlsimport_item_actions', 'security' );
$post_id=0;
if(isset($_POST['post_id'] )){
$post_id = intval( $_POST['post_id'] );
}
$status = get_post_meta( $post_id, 'mlsimport_spawn_status', true );
$path = WP_PLUGIN_DIR . '/mlsimport/logs/status_logs.log';
$logs = file_get_contents( $path );
//get neww status data
$current = intval( get_post_meta( $post_id, 'mlsimport_progress_properties', true ) );
$total = intval( get_post_meta( $post_id, 'mlsimport_progress_batches', true ) );
$memory = get_post_meta( $post_id, 'mlsimport_progress_memory', true );
$mlsimport_task_to_import = intval( get_post_meta($post_id, 'mlsimport_task_to_import',true));
$force_status = intval( get_post_meta( $post_id, 'mlsimport_force_stop', true ) );
$force_status = get_option( 'mlsimport_force_stop_' . $post_id );
if ( 'no' !== $force_status ) {
echo wp_json_encode(
array(
'is_done' => 'done',
'status' => $status,
'logs' => $logs,
)
);
die();
}
if ( '' === $status || 'completed' === $status ) {
echo wp_json_encode(
array(
'is_done' => 'done',
'status' => $status,
'logs' => $logs,
)
);
} else {
// return from log
echo wp_json_encode(
array(
'is_done' => 'wip',
'status' => $status,
'logs' => $logs,
'mlsimport_progress_properties' => $current,
'mlsimport_progress_batches' => $total,
'memory' => $memory,
'mlsimport_task_to_import'=>$mlsimport_task_to_import,
'post_id'=>$post_id
)
);
}
die();
}
/**
*
*
*
*
* Force Stop Import
*/
public function mlsimport_stop_import_per_item() {
check_ajax_referer( 'mlsimport_item_actions', 'security' );
$post_id=0;
if(isset($_POST['post_id'] )){
$post_id = intval( $_POST['post_id'] );
}
update_option( 'mlsimport_force_stop_' . $post_id, 'yes', false );
// ensure caches are cleared so running processes see the update immediately
if ( function_exists( 'wp_cache_delete' ) ) {
wp_cache_delete( 'mlsimport_force_stop_' . $post_id, 'options' );
}
mlsimport_saas_single_write_import_custom_logs( 'Stopped for ' . $post_id . PHP_EOL );
mlsimport_debuglogs_per_plugin( 'Stopped for ' . $post_id . PHP_EOL );
wp_send_json_success();
}
/*
*
* Get MLS Metadata
*
*
*
*
**/
public function mlsimport_saas_get_metadata_function() {
check_ajax_referer( 'mlsimport_saas_get_metadata', 'security' );
$theme_Start = new ThemeImport();
$values = array();
$options = get_option( $this->plugin_name . '_admin_options' );
$url = 'clients?theme_id=' . intval( $options['mlsimport_theme_used'] );
$answer = $theme_Start::globalApiRequestSaas( $url, $values, 'GET' );
update_option( 'mlsimport_mls_metadata_populated', 'yes' );
update_option( 'mlsimport_mls_metadata_theme_schema', $answer['theme_schema'] );
update_option( 'mlsimport_mls_metadata_mls_data', $answer['mls_data']['mls_meta_data'] );
update_option( 'mlsimport_mls_metadata_mls_enums', $answer['mls_data']['mls_meta_enums'] );
}
/**
*
*
* write debug logs
*/
public function mlsimport_debuglog_cron( $message ) {
if ( is_array( $message ) ) {
$message = wp_json_encode( $message );
}
$message = date( 'F j, Y, g:i a' ) . ' -> ' . $message;
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem();
}
$path = WP_PLUGIN_DIR . '/mlsimport/logs/cron_logs.log';
file_put_contents( $path, $message, FILE_APPEND | LOCK_EX );
}
}