PluginProbe
Property Hive / 2.3.1
Property Hive v2.3.1
2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 All 261 releases
← All changes | includes/class-ph-rest-api.php +1174 -21 1.4.62.3.1 View file →
@@ -12,8 +12,9 @@
12 12 * @package PropertyHive/Classes/
13 13 * @category Class
14 14 * @author PropertyHive
15 15 */
16 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Rest_Api; preserving the existing PH_* class name is required for plugin and extension compatibility.
16 17 class PH_Rest_Api {
17 18
18 19 /** @var PH_Rest_Api The single instance of the class */
19 20 protected static $_instance = null;
@@ -18,8 +19,37 @@
18 19 /** @var PH_Rest_Api The single instance of the class */
19 20 protected static $_instance = null;
20 21
21 22 /**
23 + * Download REST media within the site's upload size limit.
24 + *
25 + * @param string $url Remote media URL.
26 + * @return string|WP_Error Temporary filename or an error.
27 + */
28 + private static function download_media( $url ) {
29 + $temporary_file = wp_tempnam( $url );
30 + if ( ! $temporary_file ) {
31 + return new WP_Error( 'propertyhive_media_temp_file', __( 'Unable to create a temporary file.', 'propertyhive' ) );
32 + }
33 + $maximum_size = wp_max_upload_size();
34 + $response = wp_safe_remote_get( $url, array(
35 + 'timeout' => 30,
36 + 'stream' => true,
37 + 'filename' => $temporary_file,
38 + 'limit_response_size' => $maximum_size + 1,
39 + ) );
40 + if ( is_wp_error( $response ) ) {
41 + wp_delete_file( $temporary_file );
42 + return $response;
43 + }
44 + if ( 200 !== wp_remote_retrieve_response_code( $response ) || filesize( $temporary_file ) > $maximum_size ) {
45 + wp_delete_file( $temporary_file );
46 + return new WP_Error( 'propertyhive_media_download', __( 'The media could not be downloaded or exceeds the upload size limit.', 'propertyhive' ) );
47 + }
48 + return $temporary_file;
49 + }
50 +
51 + /**
22 52 * Main PH_Rest_Api Instance.
23 53 *
24 54 * Ensures only one instance of PH_Rest_Api is loaded or can be loaded.
25 55 *
@@ -39,9 +69,9 @@
39 69 *
40 70 * @since 1.0.0
41 71 */
42 72 public function __clone() {
43 - _doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
73 + _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
44 74 }
45 75
46 76 /**
47 77 * Unserializing instances of this class is forbidden.
@@ -48,9 +78,9 @@
48 78 *
49 79 * @since 1.0.0
50 80 */
51 81 public function __wakeup() {
52 - _doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
82 + _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
53 83 }
54 84
55 85 /**
56 86 * Constructor for the licenses class
@@ -56,25 +86,496 @@
56 86 * Constructor for the licenses class
57 87 *
58 88 */
59 89 public function __construct() {
90 + // Property
60 91 add_filter( 'rest_property_query', array( $this, 'modify_rest_property_query' ), 10, 2 );
92 + add_filter( 'rest_prepare_property', array( $this, 'rest_prepare_property' ), 10, 3 );
61 93 add_action( 'rest_api_init', array( $this, 'register_rest_api_property_fields' ), 99 );
94 + add_filter( 'rest_property_collection_params', array( $this, 'modify_rest_order_by' ), 10, 1 );
95 + add_action( "rest_after_insert_property", array( $this, 'ensure_key_property_fields_set' ), 10, 3 );
96 +
97 + // Enquiry - Create
98 + add_action( 'rest_api_init', array( $this, 'register_enquiry_rest_route' ), 11 );
99 + add_action( "rest_after_insert_enquiry", array( $this, 'ensure_key_enquiry_fields_set' ), 10, 3 );
100 +
101 + // Enquiry - Read
102 + add_filter( 'pre_get_posts', array( $this, 'restrict_enquiry_rest_listing' ));
103 + add_filter( 'rest_pre_dispatch', array( $this, 'block_enquiry_rest_listing' ), 10, 3);
104 + add_action( 'rest_api_init', array( $this, 'register_rest_api_enquiry_fields' ), 99 );
105 +
106 + // Office
107 + add_action( 'rest_api_init', array( $this, 'register_rest_api_office_fields' ), 99 );
62 108 }
63 109
110 + // Restrict listing of enquiries via REST API
111 + public function restrict_enquiry_rest_listing(WP_Query $query)
112 + {
113 + if ( !is_admin() && defined('REST_REQUEST') && REST_REQUEST )
114 + {
115 + $post_type = $query->get('post_type');
116 +
117 + // Check if it's the 'enquiry' CPT
118 + if ( $post_type === 'enquiry' )
119 + {
120 + if ( !current_user_can('manage_propertyhive') )
121 + {
122 + // Force an empty result
123 + $query->set('post__in', [0]);
124 + }
125 + }
126 + }
127 + }
128 +
129 + public function block_enquiry_rest_listing($response, $server, $request)
130 + {
131 + if ( preg_match( '#^/wp/v2/enquiry(?:/|$)#', $request->get_route() ) )
132 + {
133 + $current_user = wp_get_current_user();
134 +
135 + if ( !current_user_can('manage_propertyhive') )
136 + {
137 + return new WP_Error(
138 + 'rest_forbidden',
139 + __( 'You are not allowed to access enquiries.', 'propertyhive' ),
140 + ['status' => 403]
141 + );
142 + }
143 +
144 + if ( get_option( 'propertyhive_module_disabled_enquiries', '' ) == 'yes' )
145 + {
146 + return new WP_Error(
147 + 'rest_forbidden',
148 + __('The enquiries module is currently disabled.', 'propertyhive'),
149 + ['status' => 403]
150 + );
151 + }
152 +
153 + if ( get_option( 'propertyhive_store_property_enquiries', 'yes' ) != 'yes' )
154 + {
155 + return new WP_Error(
156 + 'rest_forbidden',
157 + __('Storing of property enquiries has been disabled in the GDPR settings.', 'propertyhive'),
158 + ['status' => 403]
159 + );
160 + }
161 +
162 + }
163 + return $response;
164 + }
165 +
166 + public function register_rest_api_enquiry_fields()
167 + {
168 + $field_array = array(
169 + 'office',
170 + 'negotiator',
171 + 'status',
172 + 'source',
173 + 'properties',
174 + 'details',
175 + );
176 +
177 + $field_array = apply_filters( 'propertyhive_rest_api_enquiry_fields', $field_array );
178 +
179 + foreach ( $field_array as $field )
180 + {
181 + register_rest_field( 'enquiry',
182 + $field,
183 + array(
184 + 'get_callback' => function( $object, $field_name, $request )
185 + {
186 + $enquiry = new PH_Enquiry($object['id']);
187 +
188 + $return = '';
189 +
190 + switch ($field_name)
191 + {
192 + case "office":
193 + {
194 + $return = array(
195 + 'name' => get_the_title($enquiry->_office_id),
196 + );
197 + break;
198 + }
199 + case "negotiator":
200 + {
201 + $user = get_userdata( $enquiry->_negotiator_id );
202 +
203 + $return = array(
204 + 'name' => ( $user !== false ) ? $user->display_name : '',
205 + );
206 + break;
207 + }
208 + case "properties":
209 + {
210 + $return = array();
211 +
212 + $property_ids = $enquiry->get_properties();
213 +
214 + if ( !empty($property_ids) )
215 + {
216 + foreach ( $property_ids as $property_id )
217 + {
218 + $property = new PH_Property((int)$property_id);
219 +
220 + $return[] = array(
221 + 'id' => (int)$property_id,
222 + 'address' => $property->get_formatted_full_address()
223 + );
224 + }
225 + }
226 + break;
227 + }
228 + case "details":
229 + {
230 + $ignore_keys = array(
231 + '_status',
232 + '_source',
233 + '_negotiator_id',
234 + '_office_id',
235 + '_action',
236 + '_contact_id',
237 + '_property_id',
238 + 'property_id',
239 + );
240 +
241 + $enquiry_meta = get_metadata( 'post', $object['id'] );
242 +
243 + $return = array();
244 +
245 + foreach ($enquiry_meta as $key => $value)
246 + {
247 + if ( !in_array( $key, $ignore_keys ) && substr( $key, 0, 1 ) != '_' && strpos($key, 'captcha') === FALSE )
248 + {
249 + $return[trim($key, "_")] = $value;
250 + }
251 + }
252 + break;
253 + }
254 + default:
255 + {
256 + $return = $enquiry->{$field_name};
257 + }
258 + }
259 +
260 + $return = apply_filters( 'propertyhive_rest_api_enquiry_field_callback', $return, $field_name, $enquiry );
261 + return $return;
262 + },
263 + 'schema' => null,
264 + )
265 + );
266 + }
267 + }
268 +
269 + public function register_enquiry_rest_route()
270 + {
271 + register_rest_route('wp/v2', '/enquiry', array(
272 + array(
273 + 'methods' => WP_REST_Server::CREATABLE,
274 + 'callback' => array( $this, 'handle_enquiry_post' ),
275 + 'permission_callback' => array( $this, 'enquiry_permission_check' )
276 + ),
277 + ));
278 + }
279 +
280 + public function enquiry_permission_check() {
281 + if ( current_user_can( 'manage_propertyhive' ) ) {
282 + return true;
283 + }
284 + return new WP_Error( 'rest_forbidden', __( 'You do not have permission to create enquiries.', 'propertyhive' ), array( 'status' => 403 ) );
285 + }
286 +
287 + public function handle_enquiry_post(WP_REST_Request $request)
288 + {
289 + // Handle the creation of the enquiry post
290 + $params = $request->get_params();
291 +
292 + // validate office ID
293 + if ( isset($params['office_id']) && !empty($params['office_id']) )
294 + {
295 + if ( get_post_type((int)$params['office_id']) != 'office' )
296 + {
297 + return new WP_Error('rest_enquiry_error', __( 'Invalid office ID passed', 'propertyhive' ) . ' ('. (int)$params['office_id'] . ')', array('status' => 400));
298 + }
299 + }
300 +
301 + // validate negotiator ID
302 + if ( isset($params['negotiator_id']) && !empty($params['negotiator_id']) )
303 + {
304 + if ( get_userdata((int)$params['negotiator_id']) === false )
305 + {
306 + return new WP_Error('rest_enquiry_error', __( 'Invalid negotiator ID passed', 'propertyhive' ) . ' ('. (int)$params['negotiator_id'] . ')', array('status' => 400));
307 + }
308 + }
309 +
310 + // validate source
311 + $valid_sources = array(
312 + 'office' => __( 'Office', 'propertyhive' ),
313 + 'website' => __( 'Website', 'propertyhive' )
314 + );
315 +
316 + $valid_sources = apply_filters( 'propertyhive_enquiry_sources', $valid_sources );
317 +
318 + if ( isset($params['source']) && !empty($params['source']) )
319 + {
320 + if ( !array_key_exists($params['source'], $valid_sources) )
321 + {
322 + return new WP_Error('rest_enquiry_error', __( 'Invalid source passed. Should be one of', 'propertyhive' ) . ': ' . implode(", ", array_keys($valid_sources)), array('status' => 400));
323 + }
324 + }
325 +
326 + if ( isset($params['property_id']) && !empty($params['property_id']) )
327 + {
328 + if ( !is_array($params['property_id']) )
329 + {
330 + $property_ids = array($params['property_id']);
331 + }
332 + else
333 + {
334 + $property_ids = $params['property_id'];
335 + }
336 +
337 + foreach ( $property_ids as $property_id )
338 + {
339 + if ( get_post_type((int)$property_id) != 'property' )
340 + {
341 + return new WP_Error('rest_enquiry_error', __( 'Invalid property ID passed', 'propertyhive' ) . ' ('. (int)$property_id . ')', array('status' => 400));
342 + }
343 + }
344 + }
345 +
346 + $title = ( isset($params['title']) && !empty($params['title']) ? $params['title'] : '' );
347 + if (empty($title))
348 + {
349 + $title = 'Enquiry';
350 + if ( isset($params['name']) && !empty($params['name']) )
351 + {
352 + $title .= ' from ' . $params['name'];
353 + }
354 + if ( isset($params['property_id']) && !empty($params['property_id']) )
355 + {
356 + if ( is_array($params['property_id']) )
357 + {
358 + $title .= ' about ' . count($params['property_id']) . ' properties';
359 + }
360 + else
361 + {
362 + $title .= ' about ' . get_the_title($params['property_id']);
363 + }
364 + }
365 + }
366 + $data = array(
367 + 'post_type' => 'enquiry',
368 + 'post_status' => 'publish',
369 + 'post_title' => sanitize_text_field($title),
370 + 'post_content' => '',
371 + 'comment_status' => 'closed',
372 + 'ping_status' => 'closed',
373 + );
374 +
375 + $post_id = wp_insert_post($data, true);
376 +
377 + if ( is_wp_error($post_id) )
378 + {
379 + return new WP_Error('rest_enquiry_error', $post_id->get_error_message(), array('status' => 500));
380 + }
381 +
382 + // Status
383 + if ( isset($params['status']) && !empty($params['status']) && in_array($params['status'], array('open', 'closed')) )
384 + {
385 + update_post_meta( $post_id, '_status', sanitize_text_field($params['status']) );
386 + }
387 + else
388 + {
389 + update_post_meta( $post_id, '_status', 'open' );
390 + }
391 +
392 + if ( isset($params['office_id']) && !empty($params['office_id']) && is_numeric($params['office_id']) )
393 + {
394 + // Should be a valid office ID as this has already been validated by this point
395 + update_post_meta( $post_id, '_office_id', (int)$params['office_id'] );
396 + }
397 + else
398 + {
399 + $primary_office_id = '';
400 + $args = array(
401 + 'post_type' => 'office',
402 + 'nopaging' => true
403 + );
404 + $office_query = new WP_Query($args);
405 +
406 + if ($office_query->have_posts())
407 + {
408 + while ($office_query->have_posts())
409 + {
410 + $office_query->the_post();
411 +
412 + if (get_post_meta(get_the_ID(), 'primary', TRUE) == '1')
413 + {
414 + $primary_office_id = get_the_ID();
415 + }
416 + }
417 + }
418 + $office_query->reset_postdata();
419 +
420 + update_post_meta( $post_id, '_office_id', $primary_office_id );
421 + }
422 +
423 + if ( isset($params['negotiator_id']) && !empty($params['negotiator_id']) && is_numeric($params['negotiator_id']) )
424 + {
425 + // Should be a valid neg ID as this has already been validated by this point
426 + update_post_meta( $post_id, '_negotiator_id', (int)$params['negotiator_id'] );
427 + }
428 +
429 + // Source
430 + if ( isset($params['source']) && !empty($params['source']) && array_key_exists($params['source'], $valid_sources) )
431 + {
432 + update_post_meta( $post_id, '_source', sanitize_text_field($params['source']) );
433 + }
434 + else
435 + {
436 + update_post_meta( $post_id, '_source', 'website' );
437 + }
438 +
439 + if ( isset($params['name']) && !empty($params['name']) )
440 + {
441 + update_post_meta( $post_id, 'name', sanitize_text_field($params['name']) );
442 + }
443 +
444 + if ( isset($params['email_address']) && !empty($params['email_address']) )
445 + {
446 + update_post_meta( $post_id, 'email_address', sanitize_email($params['email_address']) );
447 + }
448 +
449 + if ( isset($params['telephone_number']) && !empty($params['telephone_number']) )
450 + {
451 + update_post_meta( $post_id, 'telephone_number', sanitize_text_field($params['telephone_number']) );
452 + }
453 +
454 + if ( isset($params['message']) && !empty($params['message']) )
455 + {
456 + update_post_meta( $post_id, 'message', sanitize_textarea_field($params['message']) );
457 + }
458 +
459 + if ( isset($params['property_id']) && !empty($params['property_id']) && ( is_numeric($params['property_id']) || is_array($params['property_id']) ) )
460 + {
461 + // Should be a valid property ID as this has already been validated by this point
462 + $property_ids = $params['property_id'];
463 + if ( !is_array($property_ids) ) { $property_ids = array($property_ids); }
464 +
465 + foreach ( $property_ids as $property_id )
466 + {
467 + add_post_meta( $post_id, 'property_id', (int)$property_id );
468 + }
469 + }
470 +
471 + return new WP_REST_Response(array('ID' => $post_id), 201);
472 + }
473 +
474 + public function ensure_key_property_fields_set( $post, $request, $creating )
475 + {
476 + // Country / price actual
477 + $country = get_post_meta( $post->ID, '_address_country', true );
478 + if ( $country == '' )
479 + {
480 + $default_country = get_option( 'propertyhive_default_country', 'GB' );
481 + update_post_meta( $post->ID, '_address_country', $default_country );
482 + }
483 +
484 + $ph_countries = new PH_Countries();
485 + $ph_countries->update_property_price_actual( $post->ID );
486 +
487 + // Department
488 + $department = get_post_meta( $post->ID, '_department', true );
489 + if ( $department == '' )
490 + {
491 + $primary_department = get_option( 'propertyhive_primary_department', 'residential-sales' );
492 + update_post_meta( $post->ID, '_department', $primary_department );
493 + }
494 +
495 + // On Market
496 + $on_market = get_post_meta( $post->ID, '_on_market', true );
497 + if ( $on_market != 'yes' )
498 + {
499 + update_post_meta( $post->ID, '_on_market', '' );
500 + }
501 +
502 + // Featured
503 + $featured = get_post_meta( $post->ID, '_featured', true );
504 + if ( $featured != 'yes' )
505 + {
506 + update_post_meta( $post->ID, '_featured', '' );
507 + }
508 +
509 + // Office
510 + $office_id = get_post_meta( $post->ID, '_office_id', true );
511 + if ( $office_id == '' )
512 + {
513 + $primary_office_id = '';
514 + $args = array(
515 + 'post_type' => 'office',
516 + 'nopaging' => true
517 + );
518 + $office_query = new WP_Query($args);
519 +
520 + if ($office_query->have_posts())
521 + {
522 + while ($office_query->have_posts())
523 + {
524 + $office_query->the_post();
525 +
526 + if (get_post_meta(get_the_ID(), 'primary', TRUE) == '1')
527 + {
528 + $primary_office_id = get_the_ID();
529 + }
530 + }
531 + }
532 + $office_query->reset_postdata();
533 +
534 + update_post_meta( $post->ID, '_office_id', $primary_office_id );
535 + }
536 + }
537 +
538 + public function modify_rest_order_by($params)
539 + {
540 + $fields = ["price-asc", "price-desc"];
541 +
542 + foreach ( $fields as $key => $value )
543 + {
544 + $params['orderby']['enum'][] = $value;
545 + }
546 +
547 + return $params;
548 + }
549 +
64 550 public function modify_rest_property_query($args, $request)
65 551 {
66 - if ( !isset( $args['meta_query'] ) )
552 + /*if ( !isset( $args['meta_query'] ) )
67 553 {
68 554 $args['meta_query'] = array();
69 - }
555 + }*/
70 556
71 - $args['meta_query'][] = array(
72 - 'key' => '_on_market',
73 - 'value' => 'yes'
74 - );
557 + $PH_Query = new PH_Query();
75 558
76 - apply_filters( 'propertyhive_rest_api_query_args', $args );
559 + // Meta query
560 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- REST property collection arguments are built from PH_Query filters and are consumed by WordPress REST collection pagination. Meta/tax/date filters and fixed ordering are feature behavior; the REST controller bounds per_page and the source preserves request filters.
561 + $args['meta_query'] = $PH_Query->get_meta_query();
562 +
563 + // Tax query
564 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- REST property collection arguments are built from PH_Query filters and are consumed by WordPress REST collection pagination. Meta/tax/date filters and fixed ordering are feature behavior; the REST controller bounds per_page and the source preserves request filters.
565 + $args['tax_query'] = $PH_Query->get_tax_query();
566 +
567 + // Date query
568 + $args['date_query'] = $PH_Query->get_date_query();
569 +
570 + $ordering = $PH_Query->get_search_results_ordering_args();
571 + $args['orderby'] = $ordering['orderby'] . ' post_title';
572 + $args['order'] = $ordering['order'];
573 + if ( isset( $ordering['meta_key'] ) )
574 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- REST property collection arguments are built from PH_Query filters and are consumed by WordPress REST collection pagination. Meta/tax/date filters and fixed ordering are feature behavior; the REST controller bounds per_page and the source preserves request filters.
575 + $args['meta_key'] = $ordering['meta_key'];
576 +
577 + $args = apply_filters( 'propertyhive_rest_api_query_args', $args );
77 578
78 579 return $args;
79 580 }
80 581
@@ -81,16 +582,32 @@
81 582 public function register_rest_api_property_fields()
82 583 {
83 584 $field_array = array(
84 585 'department',
586 + 'reference_number',
587 + 'address_street',
588 + 'address_two',
589 + 'address_three',
590 + 'address_four',
591 + 'address_postcode',
592 + 'address_country',
85 593 'latitude',
86 594 'longitude',
595 + 'price_actual',
87 596 'price',
597 + 'price_from',
598 + 'price_to',
599 + 'price_units',
600 + 'rent_from',
601 + 'rent_to',
602 + 'rent_units',
88 603 'price_formatted',
604 + 'rent_frequency',
89 605 'currency',
90 606 'price_qualifier',
91 607 'sale_by',
92 608 'tenure',
609 + 'council_tax_band',
93 610 'deposit',
94 611 'furnished',
95 612 'available_date',
96 613 'bedrooms',
@@ -98,17 +615,40 @@
98 615 'reception_rooms',
99 616 'property_type',
100 617 'parking',
101 618 'outside_space',
619 + 'for_sale',
620 + 'to_rent',
621 + 'floor_area_from',
622 + 'floor_area_to',
623 + 'floor_area_units',
624 + 'site_area_from',
625 + 'site_area_to',
626 + 'site_area_units',
102 627 'on_market',
103 628 'featured',
629 + 'location',
104 630 'availability',
105 - 'marketing_flags',
631 + 'marketing_flag',
106 632 'features',
107 633 'description',
108 634 'office',
635 + 'negotiator',
636 + 'images',
637 + 'floorplans',
638 + 'brochures',
639 + 'epcs',
640 + 'virtual_tours',
641 + 'views_total',
642 + 'views_today',
643 + 'views_yesterday',
644 + 'views_last_7_days',
645 + 'views_last_14_days',
646 + 'views_last_30_days',
109 647 );
110 648
649 + $field_array = apply_filters( 'propertyhive_rest_api_property_fields', $field_array );
650 +
111 651 foreach ( $field_array as $field )
112 652 {
113 653 register_rest_field( 'property',
114 654 $field,
@@ -116,25 +656,49 @@
116 656 'get_callback' => function( $object, $field_name, $request )
117 657 {
118 658 $property = new PH_Property($object[ 'id' ]);
119 659
660 + $return = '';
661 +
120 662 switch ($field_name)
121 663 {
122 664 case "price":
123 - {
124 - if ( $property->poa != 'yes' )
665 + {
666 + if ( $property->_poa != 'yes' )
125 667 {
126 - if ( $property->department == 'residential-lettings' ) { return $property->rent; }else{ return $property->price; }
668 + $department = $property->_department;
669 + if ( ph_get_custom_department_based_on( $department ) !== false )
670 + {
671 + $department = ph_get_custom_department_based_on( $department );
672 + }
673 + if ( $department == 'residential-lettings' ) { $return = $property->_rent; }else{ $return = $property->_price; }
127 674 }
128 -
129 - return '';
675 + break;
130 676 }
131 - case "price_formatted": { return $property->get_formatted_price(); break; }
132 - case "features": { return $property->get_features(); break; }
133 - case "description": { return $property->get_formatted_description(); break; }
677 + case "price_from":
678 + case "price_to":
679 + {
680 + if ( $property->_price_poa != 'yes' )
681 + {
682 + $return = $property->{'_' . $field_name};
683 + }
684 + break;
685 + }
686 + case "rent_from":
687 + case "rent_to":
688 + {
689 + if ( $property->_rent_poa != 'yes' )
690 + {
691 + $return = $property->{'_' . $field_name};
692 + }
693 + break;
694 + }
695 + case "price_formatted": { $return = $property->get_formatted_price(); break; }
696 + case "features": { $return = $property->get_features(); break; }
697 + case "description": { $return = $property->get_formatted_description(); break; }
134 698 case "office":
135 699 {
136 - return array(
700 + $return = array(
137 701 'name' => $property->office_name,
138 702 'address' => $property->office_address,
139 703 'telephone_number' => $property->office_telephone_number,
140 704 'email_address' => $property->office_email_address,
@@ -139,15 +703,605 @@
139 703 'telephone_number' => $property->office_telephone_number,
140 704 'email_address' => $property->office_email_address,
141 705 );
142 706 break;
707 + }
708 + case "negotiator":
709 + {
710 + $return = array(
711 + 'name' => $property->negotiator_name,
712 + 'telephone_number' => $property->negotiator_telephone_number,
713 + 'email_address' => $property->negotiator_email_address,
714 + );
715 + break;
716 + }
717 + case "images":
718 + {
719 + $images_array = array();
720 + if ( get_option('propertyhive_images_stored_as', '') == 'urls' )
721 + {
722 + $image_urls = $property->_photo_urls;
723 + if ( !is_array($image_urls) ) { $image_urls = array(); }
724 +
725 + foreach ( $image_urls as $image_url )
726 + {
727 + if ( isset($image_url['url']) )
728 + {
729 + $images_array[] = array(
730 + 'url' => $image_url['url'],
731 + 'large' => $image_url['url'],
732 + 'medium' => $image_url['url'],
733 + 'thumbnail' => $image_url['url'],
734 + );
735 + }
736 + }
737 + }
738 + else
739 + {
740 + $image_ids = $property->get_gallery_attachment_ids();
741 + foreach ( $image_ids as $image_id )
742 + {
743 + $image_url = wp_get_attachment_url($image_id);
744 + if ($image_url !== false)
745 + {
746 + $large_image = wp_get_attachment_image_src( $image_id, 'large' );
747 + $medium_image = wp_get_attachment_image_src( $image_id, 'medium' );
748 + $thumbnail_image = wp_get_attachment_image_src( $image_id, 'thumbnail' );
749 +
750 + $images_array[] = array(
751 + 'url' => $image_url,
752 + 'large' => ( $large_image !== FALSE ? $large_image[0] : $image_url ),
753 + 'medium' => ( $medium_image !== FALSE ? $medium_image[0] : $image_url ),
754 + 'thumbnail' => ( $thumbnail_image !== FALSE ? $thumbnail_image[0] : $image_url ),
755 + );
756 + }
757 + }
758 + }
759 + $return = $images_array;
760 + break;
761 + }
762 + case "floorplans":
763 + {
764 + $floorplans_array = array();
765 + if ( get_option('propertyhive_floorplans_stored_as', '') == 'urls' )
766 + {
767 + $floorplan_urls = $property->_floorplan_urls;
768 + if ( !is_array($floorplan_urls) ) { $floorplan_urls = array(); }
769 +
770 + foreach ( $floorplan_urls as $floorplan_url )
771 + {
772 + if ( isset($floorplan_url['url']) )
773 + {
774 + $floorplans_array[] = array(
775 + 'url' => $floorplan_url['url'],
776 + 'large' => $floorplan_url['url'],
777 + 'medium' => $floorplan_url['url'],
778 + 'thumbnail' => $floorplan_url['url'],
779 + );
780 + }
781 + }
782 + }
783 + else
784 + {
785 + $floorplan_ids = $property->get_floorplan_attachment_ids();
786 + foreach ( $floorplan_ids as $floorplan_id )
787 + {
788 + $floorplan_url = wp_get_attachment_url($floorplan_id);
789 + if ($floorplan_url !== false)
790 + {
791 + $large_image = wp_get_attachment_image_src( $floorplan_id, 'large' );
792 + $medium_image = wp_get_attachment_image_src( $floorplan_id, 'medium' );
793 + $thumbnail_image = wp_get_attachment_image_src( $floorplan_id, 'thumbnail' );
794 +
795 + $floorplans_array[] = array(
796 + 'url' => $floorplan_url,
797 + 'large' => ( $large_image !== FALSE ? $large_image[0] : $floorplan_url ),
798 + 'medium' => ( $medium_image !== FALSE ? $medium_image[0] : $floorplan_url ),
799 + 'thumbnail' => ( $thumbnail_image !== FALSE ? $thumbnail_image[0] : $floorplan_url ),
800 + );
801 + }
802 + }
803 + }
804 + $return = $floorplans_array;
805 + break;
806 + }
807 + case "brochures":
808 + {
809 + $brochures_array = array();
810 + if ( get_option('propertyhive_brochures_stored_as', '') == 'urls' )
811 + {
812 + $brochure_urls = $property->_brochure_urls;
813 + if ( !is_array($brochure_urls) ) { $brochure_urls = array(); }
814 +
815 + foreach ( $brochure_urls as $brochure_url )
816 + {
817 + if ( isset($brochure_url['url']) )
818 + {
819 + $brochures_array[] = array(
820 + 'url' => $brochure_url['url'],
821 + );
822 + }
823 + }
824 + }
825 + else
826 + {
827 + $brochure_ids = $property->get_brochure_attachment_ids();
828 + foreach ( $brochure_ids as $brochure_id )
829 + {
830 + $brochure_url = wp_get_attachment_url($brochure_id);
831 + if ($brochure_url !== false)
832 + {
833 + $brochures_array[] = array(
834 + 'url' => $brochure_url,
835 + );
836 + }
837 + }
838 + }
839 + $return = $brochures_array;
840 + break;
841 + }
842 + case "epcs":
843 + {
844 + $epcs_array = array();
845 + if ( get_option('propertyhive_epcs_stored_as', '') == 'urls' )
846 + {
847 + $epc_urls = $property->_epc_urls;
848 + if ( !is_array($epc_urls) ) { $epc_urls = array(); }
849 +
850 + foreach ( $epc_urls as $epc_url )
851 + {
852 + if ( isset($epc_url['url']) )
853 + {
854 + $epcs_array[] = array(
855 + 'url' => $epc_url['url'],
856 + );
857 + }
858 + }
859 + }
860 + else
861 + {
862 + $epc_ids = $property->get_epc_attachment_ids();
863 + foreach ( $epc_ids as $epc_id )
864 + {
865 + $epc_url = wp_get_attachment_url($epc_id);
866 + if ($epc_url !== false)
867 + {
868 + $epcs_array[] = array(
869 + 'url' => $epc_url,
870 + );
871 + }
872 + }
873 + }
874 + $return = $epcs_array;
875 + break;
876 + }
877 + case "virtual_tours":
878 + {
879 + $return = $property->get_virtual_tours();
880 + break;
143 881 }
882 + case "views_today":
883 + case "views_yesterday":
884 + case "views_last_7_days":
885 + case "views_last_14_days":
886 + case "views_last_30_days":
887 + case "views_total":
888 + {
889 + $view_statistics = $property->_view_statistics;
890 + if ( !is_array($view_statistics) )
891 + {
892 + $view_statistics = array();
893 + }
894 +
895 + $views = 0;
896 +
897 + $today = current_datetime()->setTime(0, 0, 0);
898 +
899 + $date_from = new DateTimeImmutable('2001-01-01', wp_timezone());
900 + $date_to = $today;
901 +
902 + switch ($field_name)
903 + {
904 + case 'views_last_7_days':
905 + {
906 + $date_from = $today->modify('-7 days');
907 + $date_to = $today->modify('-1 day');
908 + break;
909 + }
910 +
911 + case 'views_last_14_days':
912 + {
913 + $date_from = $today->modify('-14 days');
914 + $date_to = $today->modify('-1 day');
915 + break;
916 + }
917 +
918 + case 'views_last_30_days':
919 + {
920 + $date_from = $today->modify('-30 days');
921 + $date_to = $today->modify('-1 day');
922 + break;
923 + }
924 +
925 + case 'views_today':
926 + {
927 + $date_from = $today;
928 + $date_to = $today;
929 + break;
930 + }
931 +
932 + case 'views_yesterday':
933 + {
934 + $date_from = $today->modify('-1 day');
935 + $date_to = $date_from;
936 + break;
937 + }
938 + }
939 +
940 + for ( $date = $date_from; $date <= $date_to; $date = $date->modify('+1 day') )
941 + {
942 + $date_key = $date->format('Y-m-d');
943 +
944 + if ( isset($view_statistics[$date_key]) )
945 + {
946 + $views += (int)$view_statistics[$date_key];
947 + }
948 + }
949 +
950 + $return = $views;
951 + break;
952 + }
144 953 default:
145 954 {
146 - return $property->{$field_name};
955 + $return = $property->{$field_name};
147 956 }
148 957 }
958 +
959 + $return = apply_filters( 'propertyhive_rest_api_property_field_callback', $return, $field_name, $property );
960 + return $return;
149 961 },
962 + 'update_callback' => function ( $value, $object, $field_name )
963 + {
964 + if ( taxonomy_exists($field_name) )
965 + {
966 + wp_set_post_terms( $object->ID, $value, $field_name );
967 + }
968 + else
969 + {
970 + switch ( $field_name )
971 + {
972 + case "price":
973 + {
974 + $property = new PH_Property($object->ID);
975 +
976 + $price = '';
977 + if ( $value != '' )
978 + {
979 + $price = preg_replace("/[^0-9.]/", '', $value);
980 + }
981 +
982 + if ( isset($property->_department) && $property->_department == 'residential-lettings' )
983 + {
984 + update_post_meta( $object->ID, '_rent', $price );
985 + }
986 + else
987 + {
988 + update_post_meta( $object->ID, '_price', $price );
989 + }
990 +
991 + if ( $price != '' )
992 + {
993 + // Store price in common currency (GBP) used for ordering
994 + $ph_countries = new PH_Countries();
995 + $ph_countries->update_property_price_actual( $object->ID );
996 + }
997 +
998 + break;
999 + }
1000 + case "features":
1001 + {
1002 + if ( is_array($value) && !empty($value) )
1003 + {
1004 + update_post_meta( $object->ID, '_features', count($value) );
1005 +
1006 + $i = 0;
1007 + foreach ( $value as $feature )
1008 + {
1009 + update_post_meta( $object->ID, '_feature_' . $i, $feature );
1010 +
1011 + ++$i;
1012 + }
1013 + }
1014 + break;
1015 + }
1016 + case "description":
1017 + {
1018 + if ( ! is_string( $value ) ) {
1019 + return new WP_Error( 'propertyhive_rest_description_invalid', __( 'The description must be text.', 'propertyhive' ), array( 'status' => 400 ) );
1020 + }
1021 + $value = propertyhive_sanitize_description( $value );
1022 + $property = new PH_Property($object->ID);
1023 +
1024 + if ( isset($property->_department) && $property->_department == 'commercial' )
1025 + {
1026 + update_post_meta( $object->ID, '_descriptions', '1' );
1027 + update_post_meta( $object->ID, '_description_name_0', '' );
1028 + update_post_meta( $object->ID, '_description_0', wp_slash( $value ) );
1029 + }
1030 + else
1031 + {
1032 + update_post_meta( $object->ID, '_rooms', '1' );
1033 + update_post_meta( $object->ID, '_room_name_0', '' );
1034 + update_post_meta( $object->ID, '_room_dimensions_0', '' );
1035 + update_post_meta( $object->ID, '_room_description_0', wp_slash( $value ) );
1036 + }
1037 + break;
1038 + }
1039 + case "images":
1040 + case "floorplans":
1041 + case "brochures":
1042 + case "epcs":
1043 + {
1044 + if ( ! current_user_can( 'upload_files' ) || ! current_user_can( 'edit_post', $object->ID ) ) {
1045 + return new WP_Error( 'propertyhive_rest_media_forbidden', __( 'You do not have permission to update property media.', 'propertyhive' ), array( 'status' => 403 ) );
1046 + }
1047 + if ( ! is_array( $value ) || count( $value ) > 100 ) {
1048 + return new WP_Error( 'propertyhive_rest_media_invalid', __( 'Supply an array containing no more than 100 media items.', 'propertyhive' ), array( 'status' => 400 ) );
1049 + }
1050 + foreach ( $value as $media_item ) {
1051 + if ( ! is_array( $media_item ) || ! isset( $media_item['url'] ) || ! is_string( $media_item['url'] ) || ! preg_match( '~^(https?:)?//~i', $media_item['url'] ) ) {
1052 + return new WP_Error( 'propertyhive_rest_media_invalid', __( 'Each media item must contain an HTTP or HTTPS URL.', 'propertyhive' ), array( 'status' => 400 ) );
1053 + }
1054 + }
1055 +
1056 + if ( !function_exists('media_handle_upload') ) {
1057 + require_once(ABSPATH . "wp-admin" . '/includes/image.php');
1058 + require_once(ABSPATH . "wp-admin" . '/includes/file.php');
1059 + require_once(ABSPATH . "wp-admin" . '/includes/media.php');
1060 + }
1061 +
1062 + $hive_field_name = trim($field_name, 's');
1063 + if ( $hive_field_name == 'image' ) { $hive_field_name = 'photo'; }
1064 +
1065 + if ( is_array($value) && !empty($value) )
1066 + {
1067 + if ( get_option('propertyhive_' . $field_name . '_stored_as', '') == 'urls' )
1068 + {
1069 + $media_items = array();
1070 + foreach ( $value as $media_item )
1071 + {
1072 + if (
1073 + isset($media_item['url']) &&
1074 + (
1075 + substr( strtolower($media_item['url']), 0, 2 ) == '//' ||
1076 + substr( strtolower($media_item['url']), 0, 4 ) == 'http'
1077 + )
1078 + )
1079 + {
1080 + $media_items[] = array(
1081 + 'url' => $media_item['url'],
1082 + );
1083 + }
1084 + }
1085 + update_post_meta( $object->ID, '_' . $hive_field_name . '_urls', $media_items );
1086 + }
1087 + else
1088 + {
1089 + $media_ids = array();
1090 + $previous_media_ids = get_post_meta( $object->ID, '_' . $hive_field_name . 's', TRUE );
1091 + foreach ( $value as $media_item )
1092 + {
1093 + if (
1094 + isset($media_item['url']) &&
1095 + (
1096 + substr( strtolower($media_item['url']), 0, 2 ) == '//' ||
1097 + substr( strtolower($media_item['url']), 0, 4 ) == 'http'
1098 + )
1099 + )
1100 + {
1101 + // This is a URL
1102 + $url = $media_item['url'];
1103 + $description = '';
1104 +
1105 + $filename = basename( $url );
1106 +
1107 + // Check, based on the URL, whether we have previously imported this media
1108 + $imported_previously = false;
1109 + $imported_previously_id = '';
1110 + if ( is_array($previous_media_ids) && !empty($previous_media_ids) )
1111 + {
1112 + foreach ( $previous_media_ids as $previous_media_id )
1113 + {
1114 + if (
1115 + get_post_meta( $previous_media_id, '_imported_url', TRUE ) == $url
1116 + )
1117 + {
1118 + $imported_previously = true;
1119 + $imported_previously_id = $previous_media_id;
1120 + break;
1121 + }
1122 + }
1123 + }
1124 +
1125 + if ($imported_previously)
1126 + {
1127 + $media_ids[] = $imported_previously_id;
1128 + }
1129 + else
1130 + {
1131 + $tmp = self::download_media( $url );
1132 + $file_array = array(
1133 + 'name' => basename( $url ),
1134 + 'tmp_name' => $tmp
1135 + );
1136 +
1137 + // Check for download errors
1138 + if ( is_wp_error( $tmp ) )
1139 + {
1140 + return $tmp;
1141 + }
1142 + else
1143 + {
1144 + $id = media_handle_sideload( $file_array, $object->ID, $description, array('post_title' => $filename) );
1145 +
1146 + // Check for handle sideload errors.
1147 + if ( is_wp_error( $id ) )
1148 + {
1149 + wp_delete_file( $file_array['tmp_name'] );
1150 +
1151 + return $id;
1152 + }
1153 + else
1154 + {
1155 + $media_ids[] = $id;
1156 +
1157 + update_post_meta( $id, '_imported_url', $url);
1158 + }
1159 + }
1160 + }
1161 + }
1162 + }
1163 + update_post_meta( $object->ID, '_' . $hive_field_name . 's', $media_ids );
1164 +
1165 + // Loop through $previous_media_ids, check each one exists in $media_ids, and if it doesn't then delete
1166 + if ( is_array($previous_media_ids) && !empty($previous_media_ids) )
1167 + {
1168 + foreach ( $previous_media_ids as $previous_media_id )
1169 + {
1170 + if ( ! in_array( $previous_media_id, $media_ids ) && current_user_can( 'delete_post', $previous_media_id ) )
1171 + {
1172 + if ( wp_delete_attachment( $previous_media_id, TRUE ) !== FALSE )
1173 + {
1174 +
1175 + }
1176 + }
1177 + }
1178 + }
1179 + }
1180 + }
1181 +
1182 + break;
1183 + }
1184 + case "virtual_tours":
1185 + {
1186 + if ( is_array($value) && !empty($value) )
1187 + {
1188 + update_post_meta( $object->ID, '_virtual_tours', count($value) );
1189 +
1190 + $i = 0;
1191 + foreach ( $value as $virtual_tour )
1192 + {
1193 + update_post_meta( $object->ID, '_virtual_tour_' . $i, ( isset($virtual_tour['url']) ? $virtual_tour['url'] : '' ) );
1194 + update_post_meta( $object->ID, '_virtual_tour_label_' . $i, ( isset($virtual_tour['label']) ? $virtual_tour['label'] : '' ) );
1195 +
1196 + ++$i;
1197 + }
1198 + }
1199 + break;
1200 + }
1201 + default:
1202 + {
1203 + update_post_meta( $object->ID, '_' . $field_name, $value );
1204 + }
1205 + }
1206 + }
1207 +
1208 + do_action( 'propertyhive_rest_api_property_field_update_callback', $value, $object, $field_name );
1209 + },
1210 + 'schema' => null,
1211 + )
1212 + );
1213 + }
1214 + }
1215 +
1216 + public function rest_prepare_property($response, $post, $request)
1217 + {
1218 + // Hide/show fields dynamically per item before it's returned
1219 +
1220 + $data = $response->get_data();
1221 +
1222 + // Get the department
1223 + $department = $data['department'] ?? get_post_meta($post->ID, '_department', true);
1224 + if ( ph_get_custom_department_based_on( $department ) !== false )
1225 + {
1226 + $department = ph_get_custom_department_based_on( $department );
1227 + }
1228 +
1229 + // Define the "not applicable" fields per department
1230 + $remove_for = [
1231 + 'residential-sales' => [
1232 + // specify non-sales fields
1233 + 'available_date', 'deposit', 'furnished', 'rent_frequency',
1234 + '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'
1235 + ],
1236 + 'residential-lettings' => [
1237 + // specify non-lettings fields
1238 + 'sale_by', 'tenure',
1239 + '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',
1240 + ],
1241 + 'commercial' => [
1242 + // specify non-commercial fields
1243 + 'price', 'price_actual', 'bedrooms', 'bathrooms', 'reception_rooms', 'outside_space', 'parking',
1244 + 'available_date', 'deposit', 'furnished', 'rent_frequency',
1245 + ],
1246 + ];
1247 +
1248 + if ( !empty($remove_for[$department]) )
1249 + {
1250 + foreach ( $remove_for[$department] as $k )
1251 + {
1252 + unset($data[$k]);
1253 + }
1254 + }
1255 +
1256 + $response->set_data($data);
1257 +
1258 + return $response;
1259 + }
1260 +
1261 + public function register_rest_api_office_fields()
1262 + {
1263 + $field_array = array(
1264 + 'primary',
1265 + 'office_address_1',
1266 + 'office_address_2',
1267 + 'office_address_3',
1268 + 'office_address_4',
1269 + 'office_address_postcode',
1270 + 'office_latitude',
1271 + 'office_longitude',
1272 + );
1273 +
1274 + $departments = ph_get_departments();
1275 +
1276 + foreach ( $departments as $key => $value )
1277 + {
1278 + $field_array[] = 'office_telephone_number_' . str_replace("residential-", "", $key);
1279 + $field_array[] = 'office_email_address_' . str_replace("residential-", "", $key);
1280 + }
1281 +
1282 + $field_array = apply_filters( 'propertyhive_rest_api_office_fields', $field_array );
1283 +
1284 + foreach ( $field_array as $field )
1285 + {
1286 + register_rest_field( 'office',
1287 + $field,
1288 + array(
1289 + 'get_callback' => function( $object, $field_name, $request )
1290 + {
1291 + $return = '';
1292 +
1293 + switch ($field_name)
1294 + {
1295 + default:
1296 + {
1297 + $return = get_post_meta( $object[ 'id' ], '_' . $field_name, TRUE );
1298 + }
1299 + }
1300 +
1301 + $return = apply_filters( 'propertyhive_rest_api_office_field_callback', $return, $field_name, $object[ 'id' ] );
1302 + return $return;
1303 + },
150 1304 'update_callback' => null,
151 1305 'schema' => null,
152 1306 )
153 1307 );
@@ -154,5 +1308,4 @@
154 1308 }
155 1309 }
156 1310
157 1311 }
158 -