Changeset 3373689
- Timestamp:
- 10/06/2025 12:42:14 PM (6 months ago)
- Location:
- propertyhive/trunk
- Files:
-
- 1 added
- 5 edited
-
README.txt (modified) (3 diffs)
-
includes/class-ph-aioseo.php (added)
-
includes/class-ph-property.php (modified) (1 diff)
-
includes/class-ph-rest-api.php (modified) (5 diffs)
-
includes/class-ph-yoast-seo.php (modified) (2 diffs)
-
propertyhive.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
propertyhive/trunk/README.txt
r3367847 r3373689 4 4 Requires at least: 5.6 5 5 Tested up to: 6.8.2 6 Stable tag: 2.1. 96 Stable tag: 2.1.10 7 7 License: GPLv3 8 8 License URI: http://www.gnu.org/licenses/gpl-3.0.html … … 39 39 * White label 40 40 * Elementor and Divi support 41 * Yoast and Rank Math compatibility41 * Yoast, AIOSEO and Rank Math compatibility 42 42 * Customisable templates 43 43 * Property Hive CRM … … 182 182 183 183 == Changelog == 184 185 = 2.1.10 - 2025-10-06 = 186 * Added RealEstateListing schema support if using Yoast SEO 187 * Added preliminary support for AIOSEO, including excluded off market properties from XML sitemaps and including RealEstateListing schema 188 * Added commercial description title as CSS class when outputting formatted version so they can be targeted using CSS/JS 189 * Added better support for commercial properties to REST API 184 190 185 191 = 2.1.9 - 2025-09-25 = -
propertyhive/trunk/includes/class-ph-property.php
r3292381 r3373689 820 820 if ( !$plain_text ) 821 821 { 822 $return .= '<p class="description-section">'; 822 $return .= '<p class="description-section'; 823 if ($this->{'_description_name_' . $i} != '') 824 { 825 $return .= ' description-section-' . esc_attr(sanitize_title($this->{'_description_name_' . $i})); 826 } 827 $return .= '">'; 823 828 if ($this->{'_description_name_' . $i} != '') 824 829 { -
propertyhive/trunk/includes/class-ph-rest-api.php
r3365891 r3373689 60 60 // Property 61 61 add_filter( 'rest_property_query', array( $this, 'modify_rest_property_query' ), 10, 2 ); 62 add_filter( 'rest_prepare_property', array( $this, 'rest_prepare_property' ), 10, 3 ); 62 63 add_action( 'rest_api_init', array( $this, 'register_rest_api_property_fields' ), 99 ); 63 64 add_filter( 'rest_property_collection_params', array( $this, 'modify_rest_order_by' ), 10, 1 ); … … 550 551 'price_actual', 551 552 'price', 553 'price_from', 554 'price_to', 555 'price_units', 556 'rent_from', 557 'rent_to', 558 'rent_units', 552 559 'price_formatted', 553 560 'rent_frequency', … … 566 573 'parking', 567 574 'outside_space', 575 'for_sale', 576 'to_rent', 577 'floor_area_from', 578 'floor_area_to', 579 'floor_area_units', 580 'site_area_from', 581 'site_area_to', 582 'site_area_units', 568 583 'on_market', 569 584 'featured', … … 602 617 { 603 618 case "price": 604 { 619 { 605 620 if ( $property->_poa != 'yes' ) 606 621 { 607 if ( $property->_department == 'residential-lettings' ) { $return = $property->_rent; }else{ $return = $property->_price; } 622 $department = $property->_department; 623 if ( ph_get_custom_department_based_on( $department ) !== false ) 624 { 625 $department = ph_get_custom_department_based_on( $department ); 626 } 627 if ( $department == 'residential-lettings' ) { $return = $property->_rent; }else{ $return = $property->_price; } 608 628 } 609 else 629 break; 630 } 631 case "price_from": 632 case "price_to": 633 { 634 if ( $property->_price_poa != 'yes' ) 610 635 { 611 $return = ''; 636 $return = $property->{'_' . $field_name}; 637 } 638 break; 639 } 640 case "rent_from": 641 case "rent_to": 642 { 643 if ( $property->_rent_poa != 'yes' ) 644 { 645 $return = $property->{'_' . $field_name}; 612 646 } 613 647 break; … … 1064 1098 } 1065 1099 1100 public function rest_prepare_property($response, $post, $request) 1101 { 1102 // Hide/show fields dynamically per item before it's returned 1103 1104 $data = $response->get_data(); 1105 1106 // Get the department 1107 $department = $data['department'] ?? get_post_meta($post->ID, '_department', true); 1108 if ( ph_get_custom_department_based_on( $department ) !== false ) 1109 { 1110 $department = ph_get_custom_department_based_on( $department ); 1111 } 1112 1113 // Define the "not applicable" fields per department 1114 $remove_for = [ 1115 'residential-sales' => [ 1116 // specify non-sales fields 1117 'available_date', 'deposit', 'furnished', 'rent_frequency', 1118 'commercial_tenure', 'commercial_property_type', 'for_sale', 'to_rent', 'price_from', 'price_to', 'price_units', 'rent_from', 'rent_to', 'rent_units', 'floor_area_from', 'floor_area_to', 'floor_area_units', 'site_area_from', 'site_area_to', 'site_area_units' 1119 ], 1120 'residential-lettings' => [ 1121 // specify non-lettings fields 1122 'sale_by', 'tenure', 1123 'commercial_tenure', 'commercial_property_type', 'for_sale', 'to_rent', 'price_from', 'price_to', 'price_units', 'rent_from', 'rent_to', 'rent_units', 'floor_area_from', 'floor_area_to', 'floor_area_units', 'site_area_from', 'site_area_to', 'site_area_units', 1124 ], 1125 'commercial' => [ 1126 // specify non-commercial fields 1127 'price', 'price_actual', 'bedrooms', 'bathrooms', 'reception_rooms', 'outside_space', 'parking', 1128 'available_date', 'deposit', 'furnished', 'rent_frequency', 1129 ], 1130 ]; 1131 1132 if ( !empty($remove_for[$department]) ) 1133 { 1134 foreach ( $remove_for[$department] as $k ) 1135 { 1136 unset($data[$k]); 1137 } 1138 } 1139 1140 $response->set_data($data); 1141 1142 return $response; 1143 } 1144 1066 1145 public function register_rest_api_office_fields() 1067 1146 { -
propertyhive/trunk/includes/class-ph-yoast-seo.php
r2973852 r3373689 27 27 add_filter( 'manage_edit-property_columns', array( __CLASS__, 'yoast_remove_columns') ); 28 28 add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', array( __CLASS__, 'sitemap_exclude_off_market') ); 29 29 30 add_filter( 'wpseo_schema_webpage_type', array( __CLASS__, 'yoast_schema_webpage_type') ); 31 add_filter( 'wpseo_schema_webpage', array( __CLASS__, 'yoast_schema_webpage') ); 32 add_filter( 'wpseo_schema_graph', array( __CLASS__, 'yoast_schema_graph'), 20 ); 33 30 34 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) ); 31 35 } … … 56 60 57 61 return $type; 62 } 63 64 public static function yoast_schema_webpage( $data ) 65 { 66 // Enrich the WebPage piece with RealEstateListing props (datePosted, offers, mainEntity) 67 68 if ( ! is_singular( 'property' ) ) { 69 return $data; 70 } 71 72 $post_id = get_the_ID(); 73 74 $property = new PH_Property($post_id); 75 76 $url = get_permalink( $post_id ); 77 78 // IDs so everything links together nicely in the graph 79 $residence_id = $url . '#/residence/' . $post_id; 80 $offer_id = $url . '#/offer/' . $post_id; 81 82 // Core listing bits 83 $department = $property->_department; 84 85 $data['datePosted'] = date("Y-m-d\TH:i:s", strtotime($property->_on_market_change_date)) . "+00:00"; 86 87 // Attach an Offer (price, currency, availability) that points at the Residence 88 if ( $department == 'residential-sales' || ph_get_custom_department_based_on($department) == 'residential-sales' ) 89 { 90 $data['offers'] = [ 91 '@type' => 'Offer', 92 '@id' => $offer_id, 93 'price' => $property->_poa != 'yes' ? (float)$property->_price : '', 94 'priceCurrency' => $property->_currency, 95 // Use a schema.org ItemAvailability URL if you have one; default to InStock 96 //'availability' => $availability ?: 'https://schema.org/InStock', 97 'businessFunction' => 'https://purl.org/goodrelations/v1#Sell', 98 'itemOffered' => [ '@id' => $residence_id ], 99 'url' => $url 100 ]; 101 } 102 elseif ( $department == 'residential-lettings' || ph_get_custom_department_based_on($department) == 'residential-lettings' ) 103 { 104 $data['offers'] = [ 105 '@type' => 'Offer', 106 '@id' => $offer_id, 107 'price' => $property->_poa != 'yes' ? (float)$property->_rent : '', 108 'priceCurrency' => $property->_currency, 109 'priceSpecification' => [ 110 '@type' => 'UnitPriceSpecification', 111 'price' => $property->_poa != 'yes' ? (float)$property->_rent : '', 112 'priceCurrency' => $property->_currency, 113 'unitText' => $property->_rent_frequency, 114 ], 115 'businessFunction' => 'https://purl.org/goodrelations/v1#LeaseOut', 116 // Use a schema.org ItemAvailability URL if you have one; default to InStock 117 //'availability' => $availability ?: 'https://schema.org/InStock', 118 'itemOffered' => [ '@id' => $residence_id ], 119 'url' => $url 120 ]; 121 } 122 123 // Point the WebPage at the main entity (the actual property) 124 $data['mainEntity'] = [ '@id' => $residence_id ]; 125 126 return $data; 127 } 128 129 public static function yoast_schema_graph( $graph ) 130 { 131 // Append a Residence/House node with all the property specifics 132 133 if ( ! is_singular( 'property' ) ) { 134 return $graph; 135 } 136 137 $post_id = get_the_ID(); 138 139 $property = new PH_Property($post_id); 140 141 $url = get_permalink( $post_id ); 142 143 $department = $property->_department; 144 145 $residence_id = $url . '#/residence/' . $post_id; 146 147 // Collect property meta 148 $bedrooms = (int)$property->_bedrooms; 149 $bathrooms = (int)$property->_bathrooms; 150 $street = $property->_address_street; 151 $locality = $property->_address_two; 152 $region = $property->_address_three; 153 $postcode = $property->_address_postcode; 154 $country = $property->_address_country; 155 $lat = $property->_latitude; 156 $lng = $property->_longitude; 157 158 $images = array(); 159 if ( get_option('propertyhive_images_stored_as', '') == 'urls' ) 160 { 161 $photo_urls = $property->_photo_urls; 162 if ( !is_array($photo_urls) ) { $photo_urls = array(); } 163 164 if ( !empty($num_images) ) 165 { 166 $photo_urls = array_slice($photo_urls, 0, $num_images); 167 } 168 169 foreach ( $photo_urls as $photo ) 170 { 171 $images[] = isset($photo['url']) ? $photo['url'] : ''; 172 } 173 } 174 else 175 { 176 $gallery_attachments = $property->get_gallery_attachment_ids(); 177 178 if ( !empty($gallery_attachments) ) 179 { 180 if ( !empty($num_images) ) 181 { 182 $gallery_attachments = array_slice($gallery_attachments, 0, $num_images); 183 } 184 185 foreach ($gallery_attachments as $gallery_attachment) 186 { 187 $images[] = wp_get_attachment_url( $gallery_attachment ); 188 } 189 } 190 } 191 $images = array_values( array_unique( array_filter( $images ) ) ); 192 193 $types = array( 'Residence' ); 194 if ( $department != 'commercial' && ph_get_custom_department_based_on($department) != 'commercial' ) 195 { 196 $types[] = 'SingleFamilyResidence'; 197 } 198 $residence = [ 199 '@type' => $types, 200 '@id' => $residence_id, 201 'url' => $url, 202 'name' => get_the_title( $post_id ), 203 ]; 204 205 // Address 206 if ( $street || $locality || $region || $postcode || $country ) { 207 $residence['address'] = array_filter( [ 208 '@type' => 'PostalAddress', 209 'streetAddress' => $street, 210 'addressLocality' => $locality, 211 'addressRegion' => $region, 212 'postalCode' => $postcode, 213 'addressCountry' => $country, 214 ] ); 215 } 216 217 // Geo 218 if ( $lat && $lng ) { 219 $residence['geo'] = [ 220 '@type' => 'GeoCoordinates', 221 'latitude' => (float)$lat, 222 'longitude' => (float)$lng, 223 ]; 224 } 225 226 // Rooms 227 if ( $bedrooms && $department != 'commercial' && ph_get_custom_department_based_on($department) != 'commercial' ) { $residence['numberOfBedrooms'] = $bedrooms; } 228 if ( $bathrooms && $department != 'commercial' && ph_get_custom_department_based_on($department) != 'commercial' ) { $residence['numberOfBathroomsTotal'] = $bathrooms; } 229 230 // Photos / primary image 231 if ( $images ) { 232 $residence['image'] = $images; 233 } 234 235 $graph[] = $residence; 236 return $graph; 58 237 } 59 238 -
propertyhive/trunk/propertyhive.php
r3367847 r3373689 4 4 * Plugin URI: https://wordpress.org/plugins/propertyhive/ 5 5 * Description: Property Hive has everything you need to build estate agency websites 6 * Version: 2.1. 96 * Version: 2.1.10 7 7 * Author: PropertyHive 8 8 * Author URI: https://wp-property-hive.com … … 28 28 * 29 29 * @class PropertyHive 30 * @version 2.1. 930 * @version 2.1.10 31 31 */ 32 32 final class PropertyHive { … … 35 35 * @var string 36 36 */ 37 public $version = '2.1. 9';37 public $version = '2.1.10'; 38 38 39 39 /** … … 305 305 include_once( 'includes/class-ph-yoast-seo.php' ); // Yoast SEO 306 306 include_once( 'includes/class-ph-rank-math.php' ); // Rank Math 307 include_once( 'includes/class-ph-aioseo.php' ); // All In One SEO 307 308 include_once( 'includes/class-ph-duplicate-post.php' ); // Duplicate Post 308 309
Note: See TracChangeset
for help on using the changeset viewer.