PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 5.8.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v5.8.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 6.0.7 All 35 releases
mlsimport / includes / ThemeImport.php

ThemeImport.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 5.8.1, at includes/ThemeImport.php

1,049 lines 31.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly
4 }
5
6 /**
7 * Description of ThemeImport
8 *
9 * @class ThemeImport
10 */
11 class ThemeImport {
12
13
14 public $theme;
15 public $plugin_name;
16 public $enviroment;
17 public $encoded_values;
18
19
20 /**
21 * Api Request to MLSimport API using CURL
22 *
23 * @param string $method The API method to call.
24 * @param array $values_array The values to pass to the API.
25 * @param string $type The request type (default is 'GET').
26 * @return mixed The API response or error message.
27 */
28
29 public function globalApiRequestCurlSaas($method, $valuesArray, $type = 'GET') {
30 global $mlsimport;
31 $url = MLSIMPORT_API_URL . $method;
32 $headers = ['Content-Type' => 'text/plain'];
33
34 if ($method !== 'token') {
35 $token = self::getApiToken();
36 $headers = [
37 'Content-Type' => 'application/json',
38 'authorizationToken' => $token,
39 ];
40 }
41
42 $args = [
43 'method' => $type,
44 'headers' => $headers,
45 'body' => !empty($valuesArray) ? wp_json_encode($valuesArray) : null,
46 'timeout' => 120,
47 'redirection' => 10,
48 'httpversion' => '1.1',
49 'blocking' => true,
50 'user-agent' => $_SERVER['HTTP_USER_AGENT'],
51 ];
52
53 $response = $type === 'GET' ? wp_remote_get($url, $args) : wp_remote_post($url, $args);
54
55
56
57
58 if (is_wp_error($response)) {
59 return $response->get_error_message();
60 } else {
61 $body = wp_remote_retrieve_body($response);
62 $toReturn = json_decode($body, true);
63 if (json_last_error() !== JSON_ERROR_NONE) {
64 return 'JSON decode error: ' . json_last_error_msg();
65 }
66 return $toReturn;
67 }
68 }
69
70
71 /**
72 * Retrieve the API token
73 *
74 * @return string The API token.
75 */
76 private static function getApiToken() {
77 global $mlsimport;
78 return $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient();
79 }
80
81
82 /**
83 * Api Request to MLSimport API
84 *
85 * @param string $method The API method to call.
86 * @param array $valuesArray The values to pass to the API.
87 * @param string $type The request type (default is 'GET').
88 * @return array The API response data.
89 */
90
91 public static function globalApiRequestSaas($method, $valuesArray, $type = 'GET') {
92 global $mlsimport;
93 $url = MLSIMPORT_API_URL . $method;
94
95 $headers = [];
96 if ($method !== 'token' && $method !== 'mls') {
97 $token = self::getApiToken();
98 $headers = [
99 'authorizationToken' => $token,
100 'Content-Type' => 'application/json',
101 ];
102 }
103
104
105 $args = [
106 'method' => $type,
107 'timeout' => 45,
108 'redirection' => 5,
109 'httpversion' => '1.0',
110 'blocking' => true,
111 'headers' => $headers,
112 'cookies' => [],
113 'body' => !empty($valuesArray) ? wp_json_encode($valuesArray) : null,
114 ];
115 $response = wp_remote_post($url, $args);
116
117
118 if (is_wp_error($response)) {
119 return [
120 'success' => false,
121 'error_code' => $response->get_error_code(),
122 'error_message' => esc_html($response->get_error_message())
123 ];
124 }
125
126 if (isset($response['response']['code']) && $response['response']['code'] === 200) {
127 $receivedData = json_decode(wp_remote_retrieve_body($response), true);
128 return $receivedData;
129 } else {
130 return ['success' => false];
131 }
132
133 exit();
134 }
135
136
137
138
139
140
141
142
143 /**
144 * Parse Result Array
145 *
146 * @param array $readyToParseArray The array ready to be parsed.
147 * @param array $itemIdArray The item ID array.
148 * @param string $batchKey The batch key.
149 * @param array $mlsimportItemOptionData The item option data.
150 */
151
152 public function mlsimportSaasParseSearchArrayPerItem($readyToParseArray, $itemIdArray, $batchKey, $mlsimportItemOptionData) {
153 $logs = '';
154
155 wp_cache_flush();
156 gc_collect_cycles();
157 $counterProp = 0;
158
159 if (isset($readyToParseArray['data'])) {
160 foreach ($readyToParseArray['data'] as $key => $property) {
161 ++$counterProp;
162
163 $logs = $this->mlsimportMemUsage() . '=== In parse search array, listing no ' . $key . ' from batch ' . $batchKey . ' with ListingKey: ' . $property['ListingKey'] . PHP_EOL;
164 $this->writeImportLogs($logs, 'import');
165
166 wp_cache_delete('mlsimport_force_stop_' . $itemIdArray['item_id'], 'options');
167
168 $status = get_option('mlsimport_force_stop_' . $itemIdArray['item_id']);
169 $logs = $this->mlsimportMemUsage() . ' / on Batch ' . $itemIdArray['batch_counter'] . ', Item ID: ' . $itemIdArray['item_id'] . '/' . $counterProp . ' check ListingKey ' . $property['ListingKey'] . ' - stop command issued ? ' . $status . PHP_EOL;
170 $this->writeImportLogs($logs, 'import');
171
172 if ($status === 'no') {
173 $logs = 'Will proceed to import - Memory Used ' . $this->mlsimportMemUsage() . PHP_EOL;
174 $this->writeImportLogs($logs, 'import');
175 $this->mlsimportSaasPrepareToImportPerItem($property, $itemIdArray, 'normal', $mlsimportItemOptionData);
176 } else {
177 update_post_meta($itemIdArray['item_id'], 'mlsimport_spawn_status', 'completed');
178 }
179 unset($logs);
180 }
181 }
182
183 unset($readyToParseArray);
184 unset($logs);
185 }
186
187
188 /**
189 * Write logs for import process
190 *
191 * @param string $logs The log message to write.
192 * @param string $type The type of log.
193 */
194 private function writeImportLogs($logs, $type) {
195 mlsimport_saas_single_write_import_custom_logs($logs, $type);
196 }
197
198 /**
199 * Get memory usage
200 *
201 * @return string The memory usage in MB.
202 */
203 public function mlsimportMemUsage() {
204 $memUsage = memory_get_usage(true);
205 $memUsageShow = round($memUsage / 1048576, 2);
206 return $memUsageShow . 'mb ';
207 }
208
209
210
211
212
213 /**
214 * Parse Result Array in CRON
215 *
216 * @param array $readyToParseArray The array ready to be parsed.
217 * @param array $itemIdArray The item ID array.
218 * @param string $batchKey The batch key.
219 */
220 public function mlsimportSaasCronParseSearchArrayPerItem($readyToParseArray, $itemIdArray, $batchKey) {
221 $mlsimportItemOptionData = [
222 'mlsimport_item_standardstatus' => get_post_meta($itemIdArray['item_id'], 'mlsimport_item_standardstatus', true),
223 'mlsimport_item_standardstatusdelete' => get_post_meta($itemIdArray['item_id'], 'mlsimport_item_standardstatusdelete', true),
224 'mlsimport_item_property_user' => get_post_meta($itemIdArray['item_id'], 'mlsimport_item_property_user', true),
225 'mlsimport_item_agent' => get_post_meta($itemIdArray['item_id'], 'mlsimport_item_agent', true),
226 'mlsimport_item_property_status' => get_post_meta($itemIdArray['item_id'], 'mlsimport_item_property_status', true),
227 ];
228
229 foreach ($readyToParseArray['data'] as $key => $property) {
230 $logs = 'In CRON parse search array, listing no ' . $key . ' from batch ' . $batchKey . ' with ListingKey: ' . $property['ListingKey'] . PHP_EOL;
231 $this->writeImportLogs($logs, 'cron');
232 $this->mlsimportSaasPrepareToImportPerItem($property, $itemIdArray, 'cron', $mlsimportItemOptionData);
233 }
234 }
235
236
237
238
239
240
241
242
243 /**
244 * Check if property already imported
245 *
246 * @param string $key The key to search for.
247 * @param string $postType The post type to search within (default is 'estate_property').
248 * @return int The post ID if found, or 0 if not found.
249 */
250 public function mlsimportSaasRetrievePropertyById($key, $postType = 'estate_property') {
251 $args = [
252 'post_type' => $postType,
253 'post_status' => 'any',
254 'meta_query' => [
255 [
256 'key' => 'ListingKey',
257 'value' => $key,
258 'compare' => '=',
259 ],
260 ],
261 'fields' => 'ids',
262 ];
263
264 $query = new WP_Query($args);
265 if ($query->have_posts()) {
266 $query->the_post();
267 $propertyId = get_the_ID();
268 wp_reset_postdata();
269 return $propertyId;
270 } else {
271 wp_reset_postdata();
272 return 0;
273 }
274 }
275
276
277
278
279 /**
280 * Clear taxonomy
281 *
282 * @param int $propertyId The property ID.
283 * @param array $taxonomies The taxonomies to clear.
284 */
285 public function mlsimportSaasClearPropertyForTaxonomy($propertyId, $taxonomies) {
286 if (is_array($taxonomies)) {
287 foreach ($taxonomies as $taxonomy => $term) {
288 if (is_wp_error($taxonomy)) {
289 error_log('Error with taxonomy: ' . $taxonomy->get_error_message());
290 continue; // Skip this iteration
291 }
292
293 if (taxonomy_exists($taxonomy)) {
294 wp_delete_object_term_relationships($propertyId, $taxonomy);
295 } else {
296 error_log("Taxonomy does not exist: {$taxonomy}");
297 }
298 }
299 }
300 }
301
302
303
304
305
306 /**
307 * Set taxonomy for property
308 *
309 * @param string $taxonomy The taxonomy to set.
310 * @param int $propertyId The property ID.
311 * @param mixed $fieldValues The values to set.
312 */
313 public function mlsimportSaasUpdateTaxonomyForProperty($taxonomy, $propertyId, $fieldValues) {
314 global $wpdb;
315
316 // Convert comma-separated values to array if necessary
317 if (!is_array($fieldValues)) {
318 $fieldValues = strpos($fieldValues, ',') !== false ? explode(',', $fieldValues) : [$fieldValues];
319 }
320
321 // Trim values and remove empty ones
322 $fieldValues = array_filter(array_map('trim', $fieldValues));
323
324 // Start a database transaction
325 $wpdb->query('START TRANSACTION');
326 $taxLog = [];
327
328 foreach (array_chunk($fieldValues, 5) as $chunk) {
329 foreach ($chunk as $value) {
330 if (!empty($value)) {
331 // Check if the term already exists
332 $term = $wpdb->get_row($wpdb->prepare(
333 "SELECT t.*, tt.* FROM $wpdb->terms t
334 INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id
335 WHERE t.name = %s AND tt.taxonomy = %s",
336 $value, $taxonomy
337 ));
338
339 $taxLog[] = json_encode($term);
340 if (is_null($term)) {
341 // Insert the term if it doesn't exist
342 $wpdb->insert($wpdb->terms, [
343 'name' => $value,
344 'slug' => sanitize_title($value),
345 'term_group' => 0
346 ]);
347
348 $termId = $wpdb->insert_id;
349
350 if ($termId) {
351 // Insert term taxonomy
352 $wpdb->insert($wpdb->term_taxonomy, [
353 'term_id' => $termId,
354 'taxonomy' => $taxonomy,
355 'description' => '',
356 'parent' => 0,
357 'count' => 0
358 ]);
359
360 $termTaxonomyId = $wpdb->insert_id;
361 } else {
362 $taxLog[] = 'Error inserting term';
363 continue;
364 }
365 } else {
366 // Term exists, get term_id and term_taxonomy_id
367 $termId = $term->term_id;
368 $termTaxonomyId = $wpdb->get_var($wpdb->prepare(
369 "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = %d AND taxonomy = %s",
370 $termId, $taxonomy
371 ));
372 }
373
374 if (!empty($termTaxonomyId)) {
375 // Insert term relationship
376 $wpdb->replace($wpdb->term_relationships, [
377 'object_id' => $propertyId,
378 'term_taxonomy_id' => $termTaxonomyId
379 ]);
380 // Increment the term count
381 $wpdb->query($wpdb->prepare(
382 "UPDATE $wpdb->term_taxonomy SET count = count + 1 WHERE term_taxonomy_id = %d",
383 $termTaxonomyId
384 ));
385 } else {
386 $taxLog[] = 'Error: term_taxonomy_id is null';
387 }
388 }
389 }
390 // Flush the cache to free up memory
391 wp_cache_flush();
392 // Run garbage collection
393 gc_collect_cycles();
394 }
395 // Commit the transaction
396 $wpdb->query('COMMIT');
397
398 // Clear term cache selectively
399 wp_cache_delete("{$taxonomy}_terms", 'terms');
400 wp_cache_delete("{$taxonomy}_children", 'terms');
401
402 // Restore the term metadata filter
403 add_filter('get_term_metadata', [$wpdb->terms, 'cache_term_counts'], 10, 2);
404
405 // Log memory usage
406 // if (!empty($taxLog)) {
407 // $taxLogStr = implode(PHP_EOL, $taxLog);
408 // mlsimport_saas_single_write_import_custom_logs($taxLogStr, 'normal');
409 // unset($taxLogStr);
410 // }
411 }
412
413
414
415
416 /**
417 * Set Property Title
418 *
419 * @param int $propertyId The property ID.
420 * @param int $mlsImportPostId The MLS import post ID.
421 * @param array $property The property data.
422 * @return string The updated title format.
423 */
424 public function mlsimportSaasUpdatePropertyTitle($propertyId, $mlsImportPostId, $property) {
425 global $mlsimport;
426
427 $titleFormat = esc_html(get_post_meta($mlsImportPostId, 'mlsimport_item_title_format', true));
428
429 if ('' === $titleFormat) {
430 $options = get_option('mlsimport_admin_mls_sync');
431 $titleFormat = $options['title_format'];
432 }
433
434 $titleArray = $this->strBetweenAll($titleFormat, '{', '}');
435
436 $propertyExtraMetaArrayLowerCase = array_change_key_case($property['extra_meta'], CASE_LOWER);
437
438 foreach ($titleArray as $key => $value) {
439 $replace = '';
440 switch ($value) {
441 case 'Address':
442 $replace = $property['adr_title'] ?? '';
443 break;
444 case 'City':
445 $replace = $property['adr_city'] ?? '';
446 break;
447 case 'CountyOrParish':
448 $replace = $property['adr_county'] ?? '';
449 break;
450 case 'PropertyType':
451 $replace = $property['adr_type'] ?? '';
452 break;
453 case 'Bedrooms':
454 $replace = $property['adr_bedrooms'] ?? '';
455 break;
456 case 'Bathrooms':
457 $replace = $property['adr_bathrooms'] ?? '';
458 break;
459 case 'ListingKey':
460 $replace = $property['ListingKey'];
461 break;
462 case 'ListingId':
463 $replace = $property['adr_listingid'] ?? '';
464 break;
465 case 'StateOrProvince':
466 $replace = $property['extra_meta']['StateOrProvince'] ?? '';
467 break;
468 case 'PostalCode':
469 $replace = $property['meta']['property_zip'] ?? $property['meta']['fave_property_zip'] ?? '';
470 $replace = is_array($replace) ? strval($replace[0]) : strval($replace);
471 break;
472 case 'StreetNumberNumeric':
473 $replace = $propertyExtraMetaArrayLowerCase['streetnumbernumeric'] ?? '';
474 break;
475 case 'StreetName':
476 $replace = $propertyExtraMetaArrayLowerCase['streetname'] ?? '';
477 break;
478 }
479 $titleFormat = str_replace('{' . $value . '}', $replace, $titleFormat);
480 }
481
482 $post = [
483 'ID' => $propertyId,
484 'post_title' => $titleFormat,
485 'post_name' => $titleFormat,
486 ];
487
488 wp_update_post($post);
489
490 return $titleFormat;
491 }
492
493
494
495
496
497
498 /**
499 * Prepare meta data for property
500 *
501 * @param array $property The property data.
502 * @return array The property data with prepared meta.
503 */
504 public function mlsimportSaasPrepareMetaForProperty($property) {
505 if (isset($property['extra_meta']['BathroomsTotalDecimal']) && floatval($property['extra_meta']['BathroomsTotalDecimal']) > 0) {
506 $bathrooms = floatval($property['extra_meta']['BathroomsTotalDecimal']);
507 $property['meta']['property_bathrooms'] = $bathrooms;
508 $property['meta']['fave_property_bathrooms'] = $bathrooms;
509 $property['meta']['REAL_HOMES_property_bathrooms'] = $bathrooms;
510 }
511 return $property;
512 }
513
514
515
516
517
518 /**
519 * Attach media to post
520 *
521 * @param int $propertyId The property ID.
522 * @param array $media The media data.
523 * @param string $isInsert Whether the property is being inserted.
524 * @return string The media history log.
525 */
526 public function mlsimportSassAttachMediaToPost($propertyId, $media, $isInsert) {
527 $mediaHistory = [];
528 if ($isInsert === 'no') {
529 $mediaHistory[] = 'Media - We have edit - images are not replaced';
530 return implode('</br>', $mediaHistory);
531 }
532
533 global $mlsimport;
534 include_once ABSPATH . 'wp-admin/includes/image.php';
535 $hasFeatured = false;
536
537 delete_post_meta($propertyId, 'fave_property_images');
538 delete_post_meta($propertyId, 'REAL_HOMES_property_images');
539
540 add_filter('intermediate_image_sizes_advanced', [$this, 'wpcUnsetImageSizes']);
541
542 // Sorting media
543 if (isset($media[0]['Order'])) {
544 $order = array_column($media, 'Order');
545 array_multisort($order, SORT_ASC, $media);
546 }
547
548 if (is_array($media)) {
549 foreach ($media as $image) {
550 if (isset($image['MediaCategory']) && $image['MediaCategory'] !== 'Photo') {
551 continue;
552 }
553
554 $file = $image['MediaURL'];
555
556 if (isset($image['MediaURL'])) {
557 $attachment = [
558 'guid' => $image['MediaURL'],
559 'post_status' => 'inherit',
560 'post_content' => '',
561 'post_parent' => $propertyId,
562 'post_mime_type' => $image['MimeType'] ?? 'image/jpg',
563 'post_title' => $image['MediaKey'] ?? '',
564 ];
565
566 $attachId = wp_insert_attachment($attachment, $file);
567
568 $mediaHistory[] = 'Media - Added ' . $image['MediaURL'] . ' as attachment ' . $attachId;
569 $mlsimport->admin->env_data->enviroment_image_save($propertyId, $attachId);
570
571 update_post_meta($attachId, 'is_mlsimport', 1);
572 if (!$hasFeatured) {
573 set_post_thumbnail($propertyId, $attachId);
574 $hasFeatured = true;
575 }
576 }
577 }
578 } else {
579 $mediaHistory[] = 'Media data is blank - there are no images';
580 }
581
582 remove_filter('intermediate_image_sizes_advanced', [$this, 'wpcUnsetImageSizes']);
583
584 return implode('</br>', $mediaHistory);
585 }
586
587 /**
588 * Unset image sizes
589 *
590 * @param array $sizes The sizes to unset.
591 * @return array The modified sizes array.
592 */
593 public function wpcUnsetImageSizes($sizes) {
594 return [];
595 }
596
597
598
599
600
601
602
603 /**
604 * Return user option
605 *
606 * @param int $selected The selected user ID.
607 * @return string The HTML option elements for users.
608 */
609 public function mlsimportSaasThemeImportSelectUser($selected) {
610 $userOptions = '';
611 $blogusers = get_users(['blog_id' => 1, 'orderby' => 'nicename']);
612 foreach ($blogusers as $user) {
613 $userOptions .= '<option value="' . esc_attr($user->ID) . '"';
614 if ($user->ID == $selected) {
615 $userOptions .= ' selected="selected"';
616 }
617 $userOptions .= '>' . esc_html($user->user_login) . '</option>';
618 }
619 return $userOptions;
620 }
621
622
623
624
625
626
627
628 /**
629 * Return agent option
630 *
631 * @param int $selected The selected agent ID.
632 * @return string The HTML option elements for agents.
633 */
634 public function mlsimportSaasThemeImportSelectAgent($selected) {
635 global $mlsimport;
636 $args = [
637 'post_type' => $mlsimport->admin->env_data->get_agent_post_type(),
638 'post_status' => 'publish',
639 'posts_per_page' => 150,
640 ];
641
642 $agentSelection = new WP_Query($args);
643 $agentOptions = '<option value=""></option>';
644
645 while ($agentSelection->have_posts()) {
646 $agentSelection->the_post();
647 $agentId = get_the_ID();
648
649 $agentOptions .= '<option value="' . esc_attr($agentId) . '"';
650 if ($agentId == $selected) {
651 $agentOptions .= ' selected="selected"';
652 }
653 $agentOptions .= '>' . esc_html(get_the_title()) . '</option>';
654 }
655 wp_reset_postdata();
656
657 return $agentOptions;
658 }
659
660
661
662
663
664
665
666
667 /**
668 * Delete property
669 *
670 * @param int $deleteId The ID of the property to delete.
671 * @param string $ListingKey The listing key of the property.
672 */
673 public function deleteProperty($deleteId, $ListingKey) {
674 if ($deleteId > 0) {
675 $args = [
676 'numberposts' => -1,
677 'post_type' => 'attachment',
678 'post_parent' => $deleteId,
679 'post_status' => null,
680 'orderby' => 'menu_order',
681 'order' => 'ASC',
682 ];
683 $postAttachments = get_posts($args);
684
685 foreach ($postAttachments as $attachment) {
686 wp_delete_post($attachment->ID);
687 }
688
689 wp_delete_post($deleteId);
690 $logEntry = 'Property with id ' . $deleteId . ' and ' . $ListingKey . ' was deleted on ' . current_time('Y-m-d\TH:i') . PHP_EOL;
691 $this->writeImportLogs($logEntry, 'delete');
692 }
693 }
694
695
696
697
698 /**
699 * Return array with title items
700 *
701 * @param string $string The input string.
702 * @param string $start The start delimiter.
703 * @param string $end The end delimiter.
704 * @param bool $includeDelimiters Whether to include the delimiters in the result.
705 * @param int $offset The offset to start searching from.
706 * @return array The array of strings found between the delimiters.
707 */
708 public function strBetweenAll(string $string, string $start, string $end, bool $includeDelimiters = false, int &$offset = 0): array {
709 $strings = [];
710 $length = strlen($string);
711
712 while ($offset < $length) {
713 $found = $this->strBetween($string, $start, $end, $includeDelimiters, $offset);
714 if ($found === null) {
715 break;
716 }
717
718 $strings[] = $found;
719 $offset += strlen($includeDelimiters ? $found : $start . $found . $end); // move offset to the end of the newfound string
720 }
721
722 return $strings;
723 }
724
725 /**
726 * Find string between delimiters
727 *
728 * @param string $string The input string.
729 * @param string $start The start delimiter.
730 * @param string $end The end delimiter.
731 * @param bool $includeDelimiters Whether to include the delimiters in the result.
732 * @param int $offset The offset to start searching from.
733 * @return string|null The string found between the delimiters, or null if not found.
734 */
735 public function strBetween(string $string, string $start, string $end, bool $includeDelimiters = false, int &$offset = 0): ?string {
736 if ($string === '' || $start === '' || $end === '') {
737 return null;
738 }
739
740 $startLength = strlen($start);
741 $endLength = strlen($end);
742
743 $startPos = strpos($string, $start, $offset);
744 if ($startPos === false) {
745 return null;
746 }
747
748 $endPos = strpos($string, $end, $startPos + $startLength);
749 if ($endPos === false) {
750 return null;
751 }
752
753 $length = $endPos - $startPos + ($includeDelimiters ? $endLength : -$startLength);
754 if (!$length) {
755 return '';
756 }
757
758 $offset = $startPos + ($includeDelimiters ? 0 : $startLength);
759
760 return substr($string, $offset, $length);
761 }
762
763
764
765
766
767 /**
768 * Delete property via SQL
769 *
770 * @param int $deleteId The ID of the property to delete.
771 * @param string $ListingKey The listing key of the property.
772 */
773 public function mlsimportSaasDeletePropertyViaMysql($deleteId, $ListingKey) {
774 $postType = get_post_type($deleteId);
775
776 if (in_array($postType, ['estate_property', 'property'])) {
777 $termObjList = get_the_terms($deleteId, 'property_status');
778 $deleteIdStatus = join(', ', wp_list_pluck($termObjList, 'name'));
779
780 $ListingKey = get_post_meta($deleteId, 'ListingKey', true);
781 if ('' === $ListingKey) { // manually added listing
782 $logEntry = 'User added listing with id ' . $deleteId . ' (' . $postType . ') (status ' . $deleteIdStatus . ') and ' . $ListingKey . ' NOT DELETED' . PHP_EOL;
783 $this->writeImportLogs($logEntry, 'delete');
784 return;
785 }
786
787 global $wpdb;
788 $wpdb->query($wpdb->prepare("DELETE FROM $wpdb->postmeta WHERE `post_id` = %d", $deleteId));
789 $wpdb->query($wpdb->prepare("DELETE FROM $wpdb->posts WHERE `post_parent` = %d OR `ID` = %d", $deleteId, $deleteId));
790
791 $logEntry = 'MYSQL DELETE -> Property with id ' . $deleteId . ' (' . $postType . ') (status ' . $deleteIdStatus . ') and ' . $ListingKey . ' was deleted on ' . current_time('Y-m-d\TH:i') . PHP_EOL;
792 $this->writeImportLogs($logEntry, 'delete');
793 }
794 }
795
796
797
798
799
800
801
802
803 /**
804 * Prepare to import per item
805 *
806 * @param array $property The property data.
807 * @param array $itemIdArray The item ID array.
808 * @param string $tipImport The import type.
809 * @param array $mlsimportItemOptionData The item option data.
810 */
811 public function mlsimportSaasPrepareToImportPerItem($property, $itemIdArray, $tipImport, $mlsimportItemOptionData) {
812 set_time_limit(0);
813 global $mlsimport;
814
815 $mlsImportItemStatus = $mlsimportItemOptionData['mlsimport_item_standardstatus'];
816 $mlsImportItemStatusDelete = $mlsimportItemOptionData['mlsimport_item_standardstatusdelete'];
817 $newAuthor = $mlsimportItemOptionData['mlsimport_item_property_user'];
818 $newAgent = $mlsimportItemOptionData['mlsimport_item_agent'];
819 $propertyStatus = $mlsimportItemOptionData['mlsimport_item_property_status'];
820
821 if (is_array($mlsImportItemStatus)) {
822 $mlsImportItemStatus = array_map('strtolower', $mlsImportItemStatus);
823 }
824
825 if (!isset($property['ListingKey'])) {
826 $this->writeImportLogs('ERROR: No Listing Key ' . PHP_EOL, $tipImport);
827 return;
828 }
829
830 ob_start();
831
832 $ListingKey = $property['ListingKey'];
833 $listingPostType = $mlsimport->admin->env_data->get_property_post_type();
834 $propertyId = intval($this->mlsimportSaasRetrievePropertyById($ListingKey, $listingPostType));
835 $status = isset($property['StandardStatus']) ? strtolower($property['StandardStatus']) : strtolower($property['extra_meta']['MlsStatus']);
836 $isInsert = $this->shouldInsertProperty($propertyId, $status, $mlsImportItemStatus, $tipImport);
837
838 $log = $this->mlsimportMemUsage() . '==========' . wp_json_encode($mlsImportItemStatus) . '/' . $newAuthor . '/' . $newAgent . '/' . $propertyStatus . '/ We have property with $ListingKey=' . $ListingKey . ' id=' . $propertyId . ' with status ' . $status . ' is insert? ' . $isInsert . PHP_EOL;
839 $this->writeImportLogs($log, $tipImport);
840
841 $propertyHistory = [];
842 $content = $property['content'] ?? '';
843 $submitTitle = $ListingKey;
844
845 if ($isInsert === 'yes') {
846 $post = [
847 'post_title' => $submitTitle,
848 'post_content' => $content,
849 'post_status' => $propertyStatus,
850 'post_type' => $listingPostType,
851 'post_author' => $newAuthor,
852 ];
853
854 $propertyId = wp_insert_post($post);
855 if (is_wp_error($propertyId)) {
856 $this->writeImportLogs('ERROR: on inserting ' . PHP_EOL, $tipImport);
857 } else {
858 update_post_meta($propertyId, 'ListingKey', $ListingKey);
859 $keep_on_delete='delete';
860 if(!in_array ($status,$mlsImportItemStatusDelete)){
861 $keep_on_delete='keep';
862 update_post_meta($propertyId, 'mlsImportItemStatusDelete', $keep_on_delete);
863 }
864
865
866
867
868
869 $propertyHistory[] = date('F j, Y, g:i a') . ': We Inserted the property with Default title : ' . $submitTitle . ' and received id:' . $propertyId.'. The delete statuses are '.$keep_on_delete;
870 }
871
872 clean_post_cache( $propertyId );
873
874 } elseif ($propertyId !== 0) {
875
876 $keep_on_delete='delete';
877 if(!in_array ($status,$mlsImportItemStatusDelete)){
878 $keep_on_delete='keep';
879 update_post_meta($propertyId, 'mlsImportItemStatusDelete', $keep_on_delete);
880 }
881
882 $propertyHistory = $this->updateExistingProperty($propertyId,$mlsImportItemStatusDelete, $content, $listingPostType, $newAuthor, $status, $mlsImportItemStatus, $propertyHistory, $tipImport, $ListingKey);
883 }
884
885 if ($propertyId === 0) {
886 $this->writeImportLogs('ERROR property id is 0' . PHP_EOL, $tipImport);
887 return;
888 }
889
890 $newTitle = $this->processPropertyDetails($property, $propertyId, $tipImport, $propertyHistory, $newAgent, $itemIdArray,$isInsert);
891
892 $log = PHP_EOL . 'Ending on Property ' . $propertyId . ', ListingKey: ' . $ListingKey . ' , is insert? ' . $isInsert . ' with new title: ' . $newTitle . ' ' . PHP_EOL;
893 $this->writeImportLogs($log, $tipImport);
894
895 clean_post_cache( $propertyId );
896
897 ob_end_clean();
898 }
899
900
901
902
903 /**
904 * Check if the property should be inserted
905 *
906 * @param int $propertyId The property ID.
907 * @param string $status The property status.
908 * @param array $mlsImportItemStatus The MLS import item status.
909 * @param string $tipImport The import type.
910 * @return string 'yes' or 'no' indicating if the property should be inserted.
911 */
912 private function shouldInsertProperty($propertyId, $status, $mlsImportItemStatus, $tipImport) {
913 if ($propertyId === 0) {
914 return 'yes';
915 }
916 /*if ($propertyId === 0) {
917 if (in_array($status, ['active', 'active under contract', 'active with contract', 'activewithcontract', 'status', 'activeundercontract', 'comingsoon', 'coming soon', 'pending'])) {
918 if ($tipImport === 'cron' && !in_array($status, $mlsImportItemStatus)) {
919 return 'no';
920 }
921 return 'yes';
922 }
923 return 'no';
924 }
925 return 'no';
926 */
927 }
928
929 /**
930 * Update existing property
931 *
932 * @param int $propertyId The property ID.
933 * @param string $content The post content.
934 * @param string $listingPostType The listing post type.
935 * @param int $newAuthor The new author ID.
936 * @param string $status The property status.
937 * @param array $mlsImportItemStatus The MLS import item status.
938 * @param array $propertyHistory The property history.
939 * @param string $tipImport The import type.
940 * @param string $ListingKey The listing key.
941 * @return array Updated property history.
942 */
943 private function updateExistingProperty($propertyId,$mlsImportItemStatusDelete, $content, $listingPostType, $newAuthor, $status, $mlsImportItemStatus, &$propertyHistory, $tipImport, $ListingKey) {
944
945 $mlsImportItemStatusDelete = array_map('strtolower', $mlsImportItemStatusDelete);
946
947 if (in_array($status, $mlsImportItemStatusDelete)) {
948 $log = 'Property with ID ' . $propertyId . ' and with name ' . get_the_title($propertyId) . ' has a status of <strong>' . $status . '</strong> and will be deleted' . PHP_EOL;
949 $this->deleteProperty($propertyId, $ListingKey);
950 $this->writeImportLogs($log, $tipImport);
951 } else {
952 $post = [
953 'ID' => $propertyId,
954 'post_content' => $content,
955 'post_type' => $listingPostType,
956 'post_author' => $newAuthor,
957 ];
958
959 $log = 'Property with ID ' . $propertyId . ' and with name ' . get_the_title($propertyId) . ' has a status of <strong>' . $status . '</strong> and will be Edited</br>';
960 $this->writeImportLogs($log, $tipImport);
961
962 $propertyId = wp_update_post($post);
963 if (is_wp_error($propertyId)) {
964 $this->writeImportLogs('ERROR: on edit ' . PHP_EOL, $tipImport);
965 } else {
966 $submitTitle = get_the_title($propertyId);
967 $propertyHistory[] = gmdate('F j, Y, g:i a') . ': Property with title: ' . $submitTitle . ', id:' . $propertyId . ', ListingKey:' . $ListingKey . ', Status:' . $status . ' will be edited';
968 }
969 clean_post_cache( $propertyId );
970 }
971
972 return $propertyHistory;
973 }
974
975 /**
976 * Process property details
977 *
978 * @param array $property The property data.
979 * @param int $propertyId The property ID.
980 * @param string $tipImport The import type.
981 * @param array $propertyHistory The property history.
982 * @param int $newAgent The new agent ID.
983 * @param array $itemIdArray The item ID array.
984 * @param string $isInsert If is a property insert
985 */
986 private function processPropertyDetails($property, $propertyId, $tipImport, &$propertyHistory, $newAgent, $itemIdArray, $isInsert) {
987 global $mlsimport;
988 $log = PHP_EOL . $this->mlsimportMemUsage() . '====before tax======' . PHP_EOL;
989 $this->writeImportLogs($log, $tipImport);
990
991 if (isset($property['taxonomies']) && is_array($property['taxonomies'])) {
992 remove_filter('get_term_metadata', 'lazyload_term_meta', 10);
993 wp_cache_delete('get_ancestors', 'taxonomy');
994
995 $this->mlsimportSaasClearPropertyForTaxonomy($propertyId, $property['taxonomies']);
996
997 foreach ($property['taxonomies'] as $taxonomy => $term) {
998 wp_cache_delete("{$taxonomy}_term_counts", 'counts');
999 $this->mlsimportSaasUpdateTaxonomyForProperty($taxonomy, $propertyId, $term);
1000 $propertyHistory[] = 'Updated Taxonomy ' . $taxonomy . ' with terms ' . wp_json_encode($term);
1001 }
1002
1003 add_filter('get_term_metadata', 'lazyload_term_meta', 10, 2);
1004 delete_option('category_children');
1005 }
1006
1007 wp_cache_flush();
1008
1009 $property = $this->mlsimportSaasPrepareMetaForProperty($property);
1010
1011 if (isset($property['meta']) && is_array($property['meta'])) {
1012 foreach ($property['meta'] as $metaName => $metaValue) {
1013 if (is_array($metaValue)) {
1014 $metaValue = implode(',', $metaValue);
1015 }
1016 update_post_meta($propertyId, $metaName, $metaValue);
1017 $propertyHistory[] = 'Updated Meta ' . $metaName . ' with meta_value ' . $metaValue;
1018 }
1019 }
1020
1021 $extraMetaResult = $mlsimport->admin->env_data->mlsimportSaasSetExtraMeta($propertyId, $property);
1022 if (isset($extraMetaResult['property_history'])) {
1023 $propertyHistory = array_merge($propertyHistory, (array)$extraMetaResult['property_history']);
1024 }
1025
1026 $mediaHistory = $this->mlsimportSassAttachMediaToPost($propertyId, $property['Media'], $isInsert);
1027 $propertyHistory = array_merge($propertyHistory, (array)$mediaHistory);
1028
1029 $newTitle = $this->mlsimportSaasUpdatePropertyTitle($propertyId, $itemIdArray['item_id'], $property);
1030 $propertyHistory[] = 'Updated title to ' . $newTitle . '</br>';
1031
1032 $mlsimport->admin->env_data->correlationUpdateAfter($isInsert, $propertyId, [], $newAgent);
1033
1034 if (!empty($propertyHistory)) {
1035 if (intval(get_option('mlsimport-disable-history', 1)) === 1) {
1036 $propertyHistory[] = '---------------------------------------------------------------</br>';
1037 $propertyHistory = implode('</br>', $propertyHistory);
1038 update_post_meta($propertyId, 'mlsimport_property_history', $propertyHistory);
1039 }
1040 }
1041
1042 return $newTitle;
1043 }
1044
1045
1046
1047
1048 }
1049