Plugin Directory

Changeset 3418321


Ignore:
Timestamp:
12/12/2025 01:47:34 PM (4 months ago)
Author:
PropertyHive
Message:

Update to version 2.5.36

Location:
houzez-property-feed/trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • houzez-property-feed/trunk/README.txt

    r3393603 r3418321  
    33Tags: property import, property export, houzez, houzez import property, real estate
    44Requires at least: 3.8
    5 Tested up to: 6.8
    6 Stable tag: 2.5.35
    7 Version: 2.5.35
     5Tested up to: 6.9
     6Stable tag: 2.5.36
     7Version: 2.5.36
    88Homepage: https://houzezpropertyfeed.com
    99License: GPLv3
     
    145145== Changelog ==
    146146
     147= 2.5.36 - 2025-12-12 =
     148* Added support for v6 of the propCtrl API used for agency integrations as opposed to importing from portals
     149* Added language option to Apimo imports
     150* Added support for commercial properties in VaultEA/VaultRE format
     151* Corrected XML node name of thinkSPAIN description node from 'desc' to 'description'
     152* Declared compatibility with WordPress 6.9
     153
    147154= 2.5.35 - 2025-11-11 =
    148155* Added support for exporting in the thinkSPAIN XML format
  • houzez-property-feed/trunk/assets/js/admin-import.js

    r3389696 r3418321  
    417417
    418418    jQuery('.hpf-admin-settings-import-settings .settings-panel #format').change(function()
     419    {
     420        hpf_show_format_settings();
     421    });
     422
     423    jQuery('select[name=\'propctrl_api_version\']').change(function()
    419424    {
    420425        hpf_show_format_settings();
     
    15661571            jQuery('#import_setting_tab_media').show();
    15671572            jQuery('.hpf-admin-settings-import-settings .csv-rules-available-fields').show();
     1573        }
     1574
     1575        if ( selected_format == 'propctrl' )
     1576        {
     1577            var selected_api_version = jQuery('select[name=\'propctrl_api_version\']').val();
     1578            if ( selected_api_version == 'v6' )
     1579            {
     1580                jQuery('#row_propctrl_agency_id').hide();
     1581                jQuery('#row_propctrl_branch_id').hide();
     1582            }
     1583            else
     1584            {
     1585                jQuery('#row_propctrl_agency_id').show();
     1586                jQuery('#row_propctrl_branch_id').show();
     1587            }
    15681588        }
    15691589
  • houzez-property-feed/trunk/houzez-property-feed.php

    r3393603 r3418321  
    44 * Plugin Uri: https://houzezpropertyfeed.com
    55 * Description: Automatically import properties to Houzez from estate agency CRMs and export to portals
    6  * Version: 2.5.35
     6 * Version: 2.5.36
    77 * Author: PropertyHive
    88 * Author URI: https://wp-property-hive.com
     
    2020     * @var string
    2121     */
    22     public $version = '2.5.35';
     22    public $version = '2.5.36';
    2323
    2424    /**
  • houzez-property-feed/trunk/includes/export-formats/class-houzez-property-feed-format-thinkspain.php

    r3393603 r3418321  
    246246                $description = preg_replace('/<!--\s*\/wp:.*?-->/s', '', $description);
    247247
    248                 $desc_xml = $property_xml->addChild('desc');
     248                $desc_xml = $property_xml->addChild('description');
    249249                $desc_xml->addChild('en', htmlspecialchars($description, ENT_QUOTES | ENT_XML1, 'UTF-8'));
    250250
  • houzez-property-feed/trunk/includes/format-functions.php

    r3393603 r3418321  
    391391                    'label' => __( 'Agency ID', 'houzezpropertyfeed' ),
    392392                    'type' => 'text',
    393                 )
     393                ),
     394                array(
     395                    'id' => 'language',
     396                    'label' => __( 'Language', 'houzezpropertyfeed' ),
     397                    'type' => 'text',
     398                    'default' => 'en',
     399                    'tooltip' => __( 'A two letter country code (e.g. en, es, fr)', 'houzezpropertyfeed' )
     400                ),
    394401            ),
    395402            'address_fields' => array( 'district', 'city', 'region' ),
     
    19791986            'name' => __( 'PropCtrl', 'houzezpropertyfeed' ),
    19801987            'fields' => array(
     1988                array(
     1989                    'id' => 'api_version',
     1990                    'label' => __( 'API Version', 'houzezpropertyfeed' ),
     1991                    'type' => 'select',
     1992                    'options' => array(
     1993                        '' => 'Listing Service v1 (if you\'re a portal importing from multiple agents)',
     1994                        'v6' => 'Agency Integration Service v6 (if you\'re a single agency)',
     1995                    )
     1996                ),
    19811997                array(
    19821998                    'id' => 'base_url',
  • houzez-property-feed/trunk/includes/import-formats/class-houzez-property-feed-format-apimo.php

    r3389596 r3418321  
    158158        $start_at_property = get_option( 'houzez_property_feed_property_' . $this->import_id );
    159159
     160        $language = 'en';
     161        if ( isset($import_settings['language']) && !empty($import_settings['language']) )
     162        {
     163            $language = strtolower(trim($import_settings['language']));
     164        }
     165
    160166        $property_row = 1;
    161167        foreach ( $this->properties as $property )
     
    205211                foreach ( $property['comments'] as $comment )
    206212                {
    207                     if ( isset($comment['language']) && $comment['language'] == 'en' )
     213                    if ( isset($comment['language']) && strtolower(trim($comment['language'])) == $language )
    208214                    {
    209215                        if ( isset($comment['title']) && !empty($comment['title']) ) { $display_address = $comment['title']; }
    210216                        if ( isset($comment['comment']) && !empty($comment['comment']) ) { $post_content = $comment['comment']; }
    211217                        break;
     218                    }
     219                }
     220            }
     221
     222            if ( $language != 'en' && empty($display_address) && empty($post_content) )
     223            {
     224                // No title/desc found in language specified. Fallback to en
     225                if ( isset($property['comments']) && !empty($property['comments']) )
     226                {
     227                    foreach ( $property['comments'] as $comment )
     228                    {
     229                        if ( isset($comment['language']) && strtolower(trim($comment['language'])) == 'en' )
     230                        {
     231                            if ( isset($comment['title']) && !empty($comment['title']) ) { $display_address = $comment['title']; }
     232                            if ( isset($comment['comment']) && !empty($comment['comment']) ) { $post_content = $comment['comment']; }
     233                            break;
     234                        }
    212235                    }
    213236                }
  • houzez-property-feed/trunk/includes/import-formats/class-houzez-property-feed-format-propctrl.php

    r3389596 r3418321  
    5555
    5656        // Send request back to PropCtrl containing post ID and URL etc
    57         $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/listings/' . $crm_id;
     57        if ( isset($import_settings['api_version']) && $import_settings['api_version'] == 'v6' )
     58        {
     59            $url = rtrim($import_settings['base_url'], '/') . '/agency-integration/v6/properties/' . $crm_id;
     60        }
     61        else
     62        {
     63            $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/listings/' . $crm_id;
     64        }
    5865
    5966        $headers = array(
     
    95102    public function parse()
    96103    {
     104        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
     105
     106        if ( isset($import_settings['api_version']) && $import_settings['api_version'] == 'v6' )
     107        {
     108            $parsed = $this->parse_v6();
     109
     110            if ( !$parsed )
     111            {
     112                return false;
     113            }
     114        }
     115        else
     116        {
     117            $parsed = $this->parse_v1();
     118            if ( !$parsed )
     119            {
     120                return false;
     121            }
     122        }
     123
     124        return true;
     125    }
     126
     127    public function parse_v1()
     128    {
     129        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
     130
    97131        $this->properties = array(); // Reset properties in the event we're importing multiple files
    98132
     
    100134
    101135        $this->time_at_start = time();
    102 
    103         $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
    104136
    105137        $from_date = '2020-01-01 00:00:00';
     
    387419            return false;
    388420        }
     421
     422        return true;
    389423    }
    390424
    391     private function get_agency_details( $agency_id )
     425    public function parse_v6()
    392426    {
    393         if ( isset($this->agency_cache[$agency_id]) )
    394         {
    395             return $this->agency_cache[$agency_id];
    396         }
    397 
    398427        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
    399428
    400         $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/agencies?agencyIds=' . $agency_id;
     429        $this->properties = array(); // Reset properties in the event we're importing multiple files
     430
     431        $this->log("Parsing properties", '', 0, '', false);
     432
     433        $this->time_at_start = time();
     434
     435        $from_date = '2020-01-01 00:00:00';
     436        if ( ( isset($import_settings['only_updated']) && $import_settings['only_updated'] == 'yes' ) || !isset($import_settings['only_updated']) )
     437        {
     438            // get last ran date
     439            $last_ran_date = get_option( 'houzez_property_feed_last_ran_' . $this->import_id, '' );
     440            if ( !empty($last_ran_date) )
     441            {
     442                $date = new DateTime();
     443                $date->setTimestamp($last_ran_date);
     444                $date->setTimezone(new DateTimeZone('Africa/Johannesburg'));
     445                $from_date = $date->format('Y-m-d H:i:s');
     446
     447                $this->only_updated = true;
     448            }
     449        }
     450        $url = rtrim($import_settings['base_url'], '/') . '/agency-integration/v6/properties/changes?fromDate=' . $from_date;
     451
     452        $this->log("Making request to " . $url);
    401453
    402454        $headers = array(
     
    415467        if ( is_wp_error( $response ) )
    416468        {
    417             $this->log_error( 'Agency Response: ' . $response->get_error_message() );
     469            $this->log_error( 'Response: ' . $response->get_error_message() );
    418470
    419471            return false;
    420472        }
    421473
     474        if ( wp_remote_retrieve_response_code($response) === 401 )
     475        {
     476            $this->log_error( wp_remote_retrieve_response_code($response) . ' response received when requesting properties. Error message: ' . wp_remote_retrieve_response_message($response) );
     477            return false;
     478        }
     479
    422480        $json = json_decode( $response['body'], TRUE );
    423481
    424482        if ($json !== FALSE)
    425483        {
    426             if ( is_array($json) && !empty($json) )
    427             {
    428                 $this->agency_cache[$agency_id] = $json[0];
    429                 return $json[0];
     484            if ( isset($json['items']) )
     485            {
     486                if ( !empty($json['items']) )
     487                {
     488                    $this->log("Found " . number_format(count($json['items'])) . " properties to import. Getting further details by getting properties in " . number_format(ceil( count($json['items']) / 10 )) . " batches of 10");
     489
     490                    $property_ids_in_batch = array();
     491
     492                    foreach ( $json['items'] as $property )
     493                    {
     494                        if ( count($property_ids_in_batch) == 10 )
     495                        {
     496                            $done = $this->get_properties_in_batch_v6( $property_ids_in_batch );
     497
     498                            if ( $done === false )
     499                            {
     500                                return false;
     501                            }
     502
     503                            $property_ids_in_batch = array();
     504                        }
     505
     506                        $property_ids_in_batch[] = $property['id'];
     507                    }
     508
     509                    $done = $this->get_properties_in_batch_v6( $property_ids_in_batch );
     510
     511                    if ( $done === false )
     512                    {
     513                        return false;
     514                    }
     515                }
    430516            }
    431517            else
    432518            {
    433                 $this->log_error( 'Parsed agency but it\'s empty: ' . $response['body'] );
     519                $this->log_error( 'Parsed JSON but no properties found: ' . $response['body'] );
    434520
    435521                return false;
     
    439525        {
    440526            // Failed to parse JSON
    441             $this->log_error( 'Failed to parse agency JSON: ' . $response['body'] );
     527            $this->log_error( 'Failed to parse JSON: ' . $response['body'] );
    442528
    443529            return false;
    444530        }
    445531
    446         return false;
     532        if ( empty($this->properties) )
     533        {
     534            if ( $this->only_updated === true )
     535            {
     536                update_option( 'houzez_property_feed_last_ran_' . $this->import_id, $this->time_at_start );
     537                $this->log_error( 'No properties modified since the last time an import ran.' );
     538            }
     539            else
     540            {
     541                $this->log_error( 'No properties found. We\'re not going to continue as this could likely be wrong and all properties will get removed if we continue.' );
     542            }
     543            return false;
     544        }
     545
     546        return true;
    447547    }
    448548
    449     private function get_branch_details( $branch_id )
     549    private function get_properties_in_batch_v6( $property_ids = array() )
    450550    {
    451         if ( isset($this->branch_cache[$branch_id]) )
    452         {
    453             return $this->branch_cache[$branch_id];
     551        if ( empty($property_ids) )
     552        {
     553            return false;
    454554        }
    455555
    456556        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
    457557
    458         $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/branches?branchIds=' . $branch_id;
     558        $url = rtrim($import_settings['base_url'], '/') . '/agency-integration/v6/properties?';
     559
     560        // Use array_map to prepend "listingIds=" to each ID
     561        $listing_ids = array_map(function($id) {
     562            return "propertyIds=" . $id;
     563        }, $property_ids);
     564
     565        // Use implode to concatenate them with "&"
     566        $url .= implode("&", $listing_ids);
    459567
    460568        $headers = array(
    461569            'Authorization' => 'Basic ' . base64_encode($import_settings['api_username'] . ':' . $import_settings['api_password']),
    462570        );
     571
     572        $this->log("Making request to " . $url);
    463573
    464574        $response = wp_remote_request(
     
    473583        if ( is_wp_error( $response ) )
    474584        {
    475             $this->log_error( 'Branch Response: ' . $response->get_error_message() );
     585            $this->log_error( 'Response: ' . $response->get_error_message() );
    476586
    477587            return false;
     
    480590        $json = json_decode( $response['body'], TRUE );
    481591
     592        $off_market_listing_statuses = apply_filters( 'houzez_property_feed_propctrl_off_market_statuses', array('cancelled', 'withdrawn', 'expired', 'rented', 'sold') );
     593
    482594        if ($json !== FALSE)
    483595        {
    484             if ( is_array($json) && !empty($json) )
    485             {
    486                 $this->branch_cache[$branch_id] = $json[0];
    487                 return $json[0];
     596            if ( is_array($json) )
     597            {
     598                if ( !empty($json) )
     599                {
     600                    foreach ( $json as $property )
     601                    {
     602                        if (
     603                            isset($property['listingStatus']) &&
     604                            in_array(strtolower($property['listingStatus']), $off_market_listing_statuses)
     605                        )
     606                        {
     607                            $this->remove_property( $property['propertyId'], '' );
     608                            continue;
     609                        }
     610
     611                        $property['listingId'] = $property['propertyId'];
     612
     613                        list($suburb, $city, $province, $postcode, $country) = $this->get_suburb_info($property['suburbId']);
     614
     615                        $property['suburb'] = $suburb;
     616                        $property['city'] = $city;
     617                        $property['province'] = $province;
     618                        $property['postcode'] = $postcode;
     619                        $property['country'] = $country;
     620
     621                        $property['agencyDetails'] = array();
     622                        $property['branchDetails'] = array();
     623                        $property['agentDetails'] = array();
     624
     625                        if ( isset($property['agencyId']) && !empty($property['agencyId']) )
     626                        {
     627                            $agency = $this->get_agency_details($property['agencyId']);
     628
     629                            if ( $agency !== FALSE )
     630                            {
     631                                $property['agencyDetails'] = $agency;
     632                            }
     633                        }
     634
     635                        if ( isset($property['branchId']) && !empty($property['branchId']) )
     636                        {
     637                            $branch = $this->get_branch_details($property['branchId']);
     638
     639                            if ( $branch !== FALSE )
     640                            {
     641                                $property['branchDetails'] = $branch;
     642                            }
     643                        }
     644
     645                        if ( isset($property['agents']) && !empty($property['agents']) && is_array($property['agents']) )
     646                        {
     647                            foreach ( $property['agents'] as $agent_id )
     648                            {
     649                                $agent = $this->get_agent_details($agent_id);
     650
     651                                if ( $agent !== FALSE )
     652                                {
     653                                    $property['agentDetails'][] = $agent;
     654                                }
     655                            }
     656                        }
     657
     658                        $this->properties[] = $property;
     659                       
     660                    }
     661                }
     662                else
     663                {
     664                    $this->log_error( 'Parsed property JSON but it\'s empty: ' . $response['body'] );
     665
     666                    //return false;
     667                }
    488668            }
    489669            else
    490670            {
    491                 $this->log_error( 'Parsed branch JSON but it\'s empty: ' . $response['body'] );
     671                $this->log_error( 'Parsed property JSON but it\'s not an array: ' . $response['body'] );
    492672
    493673                return false;
     
    497677        {
    498678            // Failed to parse JSON
    499             $this->log_error( 'Failed to parse branch JSON: ' . $response['body'] );
     679            $this->log_error( 'Failed to parse property JSON: ' . $response['body'] );
    500680
    501681            return false;
    502682        }
    503683
    504         return false;
     684        return true;
    505685    }
    506686
    507     private function get_agent_details( $agent_id )
     687    private function get_agency_details( $agency_id )
    508688    {
    509         if ( isset($this->agent_cache[$agent_id]) )
    510         {
    511             return $this->agent_cache[$agent_id];
     689        if ( isset($this->agency_cache[$agency_id]) )
     690        {
     691            return $this->agency_cache[$agency_id];
    512692        }
    513693
    514694        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
    515695
    516         $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/agents?agentIds=' . $agent_id;
     696        if ( isset($import_settings['api_version']) && $import_settings['api_version'] == 'v6' )
     697        {
     698            $url = rtrim($import_settings['base_url'], '/') . '/agency-integration/v6/agencies?agencyIds=' . $agency_id;
     699        }
     700        else
     701        {
     702            $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/agencies?agencyIds=' . $agency_id;
     703        }
    517704
    518705        $headers = array(
     
    531718        if ( is_wp_error( $response ) )
    532719        {
     720            $this->log_error( 'Agency Response: ' . $response->get_error_message() );
     721
     722            return false;
     723        }
     724
     725        $json = json_decode( $response['body'], TRUE );
     726
     727        if ($json !== FALSE)
     728        {
     729            if ( is_array($json) && !empty($json) )
     730            {
     731                $this->agency_cache[$agency_id] = $json[0];
     732                return $json[0];
     733            }
     734            else
     735            {
     736                $this->log_error( 'Parsed agency but it\'s empty: ' . $response['body'] );
     737
     738                return false;
     739            }
     740        }
     741        else
     742        {
     743            // Failed to parse JSON
     744            $this->log_error( 'Failed to parse agency JSON: ' . $response['body'] );
     745
     746            return false;
     747        }
     748
     749        return false;
     750    }
     751
     752    private function get_branch_details( $branch_id )
     753    {
     754        if ( isset($this->branch_cache[$branch_id]) )
     755        {
     756            return $this->branch_cache[$branch_id];
     757        }
     758
     759        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
     760
     761        if ( isset($import_settings['api_version']) && $import_settings['api_version'] == 'v6' )
     762        {
     763            $url = rtrim($import_settings['base_url'], '/') . '/agency-integration/v6/branches?branchIds=' . $branch_id;
     764        }
     765        else
     766        {
     767            $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/branches?branchIds=' . $branch_id;
     768        }
     769
     770        $headers = array(
     771            'Authorization' => 'Basic ' . base64_encode($import_settings['api_username'] . ':' . $import_settings['api_password']),
     772        );
     773
     774        $response = wp_remote_request(
     775            $url,
     776            array(
     777                'method' => 'GET',
     778                'timeout' => 120,
     779                'headers' => $headers
     780            )
     781        );
     782
     783        if ( is_wp_error( $response ) )
     784        {
     785            $this->log_error( 'Branch Response: ' . $response->get_error_message() );
     786
     787            return false;
     788        }
     789
     790        $json = json_decode( $response['body'], TRUE );
     791
     792        if ($json !== FALSE)
     793        {
     794            if ( is_array($json) && !empty($json) )
     795            {
     796                $this->branch_cache[$branch_id] = $json[0];
     797                return $json[0];
     798            }
     799            else
     800            {
     801                $this->log_error( 'Parsed branch JSON but it\'s empty: ' . $response['body'] );
     802
     803                return false;
     804            }
     805        }
     806        else
     807        {
     808            // Failed to parse JSON
     809            $this->log_error( 'Failed to parse branch JSON: ' . $response['body'] );
     810
     811            return false;
     812        }
     813
     814        return false;
     815    }
     816
     817    private function get_agent_details( $agent_id )
     818    {
     819        if ( isset($this->agent_cache[$agent_id]) )
     820        {
     821            return $this->agent_cache[$agent_id];
     822        }
     823
     824        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
     825
     826        if ( isset($import_settings['api_version']) && $import_settings['api_version'] == 'v6' )
     827        {
     828            $url = rtrim($import_settings['base_url'], '/') . '/agency-integration/v6/agents?agentIds=' . $agent_id;
     829        }
     830        else
     831        {
     832            $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/agents?agentIds=' . $agent_id;
     833        }
     834       
     835
     836        $headers = array(
     837            'Authorization' => 'Basic ' . base64_encode($import_settings['api_username'] . ':' . $import_settings['api_password']),
     838        );
     839
     840        $response = wp_remote_request(
     841            $url,
     842            array(
     843                'method' => 'GET',
     844                'timeout' => 120,
     845                'headers' => $headers
     846            )
     847        );
     848
     849        if ( is_wp_error( $response ) )
     850        {
    533851            $this->log_error( 'Agent Response: ' . $response->get_error_message() );
    534852
     
    574892        $import_settings = houzez_property_feed_get_import_settings_from_id( $this->import_id );
    575893
    576         $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/suburbs?suburbIds=' . $suburb_id;
     894        if ( isset($import_settings['api_version']) && $import_settings['api_version'] == 'v6' )
     895        {
     896            $url = rtrim($import_settings['base_url'], '/') . '/agency-integration/v6/suburbs?suburbIds=' . $suburb_id;
     897        }
     898        else
     899        {
     900            $url = rtrim($import_settings['base_url'], '/') . '/listing/v1/suburbs?suburbIds=' . $suburb_id;
     901        }
    577902
    578903        $headers = array(
  • houzez-property-feed/trunk/includes/import-formats/class-houzez-property-feed-format-vaultea.php

    r3393603 r3418321  
    8080                'department' => 'residential-lettings'
    8181            ),
    82             /*array(
     82            array(
    8383                'uri' => 'properties/commercial/sale',
    84                 'department' => 'commercial',
     84                'department' => 'residential-sales',
    8585                'portalStatus' => array( 'listing', 'conditional' )
    8686            ),
    8787            array(
    8888                'uri' => 'properties/commercial/lease',
    89                 'department' => 'commercial'
    90             ),*/
     89                'department' => 'residential-lettings'
     90            ),
    9191            array(
    9292                'uri' => 'properties/land/sale',
  • houzez-property-feed/trunk/includes/views/admin-settings-import-settings-format.php

    r3389696 r3418321  
    5151                }
    5252        ?>
    53             <tr>
     53            <tr id="row_<?php echo esc_attr($key . '_' . $field['id']); ?>">
    5454                <th><?php echo isset($field['label']) ? esc_html($field['label']) : ''; ?></th>
    5555                <td><?php
Note: See TracChangeset for help on using the changeset viewer.