# propertyhive/2.3.1/includes/class-ph-rest-api.php

Property Hive, version 2.3.1. 1,312 lines.

- Page: https://pluginprobe.com/plugins/propertyhive/2.3.1/code/includes/class-ph-rest-api.php
- Raw: https://pluginprobe.com/plugins/propertyhive/2.3.1/raw/includes/class-ph-rest-api.php
- Modified: 2026-09-16T09:29:48+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/propertyhive/2.3.1/code/includes/class-ph-rest-api.php#L10-L20`.

```php
<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/**
 * Serve properties using the REST API
 *
 * @class 		PH_Rest_Api
 * @version		1.0.0
 * @package		PropertyHive/Classes/
 * @category	Class
 * @author 		PropertyHive
 */
// 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.
class PH_Rest_Api {

	/** @var PH_Rest_Api The single instance of the class */
	protected static $_instance = null;

	/**
	 * Download REST media within the site's upload size limit.
	 *
	 * @param string $url Remote media URL.
	 * @return string|WP_Error Temporary filename or an error.
	 */
	private static function download_media( $url ) {
		$temporary_file = wp_tempnam( $url );
		if ( ! $temporary_file ) {
			return new WP_Error( 'propertyhive_media_temp_file', __( 'Unable to create a temporary file.', 'propertyhive' ) );
		}
		$maximum_size = wp_max_upload_size();
		$response = wp_safe_remote_get( $url, array(
			'timeout'             => 30,
			'stream'              => true,
			'filename'            => $temporary_file,
			'limit_response_size' => $maximum_size + 1,
		) );
		if ( is_wp_error( $response ) ) {
			wp_delete_file( $temporary_file );
			return $response;
		}
		if ( 200 !== wp_remote_retrieve_response_code( $response ) || filesize( $temporary_file ) > $maximum_size ) {
			wp_delete_file( $temporary_file );
			return new WP_Error( 'propertyhive_media_download', __( 'The media could not be downloaded or exceeds the upload size limit.', 'propertyhive' ) );
		}
		return $temporary_file;
	}

	/**
	 * Main PH_Rest_Api Instance.
	 *
	 * Ensures only one instance of PH_Rest_Api is loaded or can be loaded.
	 *
	 * @since 1.0.0
	 * @static
	 * @return PH_Licenses Main instance
	 */
	public static function instance() {
		if ( is_null( self::$_instance ) ) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}

	/**
	 * Cloning is forbidden.
	 *
	 * @since 1.0.0
	 */
	public function __clone() {
		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'propertyhive' ), '1.0.0' );
	}

	/**
	 * Unserializing instances of this class is forbidden.
	 *
	 * @since 1.0.0
	 */
	public function __wakeup() {
		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'propertyhive' ), '1.0.0' );
	}

	/**
	 * Constructor for the licenses class
	 *
	 */
	public function __construct() {
		// Property
		add_filter( 'rest_property_query', array( $this, 'modify_rest_property_query' ), 10, 2 );
		add_filter( 'rest_prepare_property', array( $this, 'rest_prepare_property' ), 10, 3 );
		add_action( 'rest_api_init', array( $this, 'register_rest_api_property_fields' ), 99 );
		add_filter( 'rest_property_collection_params', array( $this, 'modify_rest_order_by' ), 10, 1 );
		add_action( "rest_after_insert_property", array( $this, 'ensure_key_property_fields_set' ), 10, 3 );

		// Enquiry - Create
		add_action( 'rest_api_init', array( $this, 'register_enquiry_rest_route' ), 11 );
		add_action( "rest_after_insert_enquiry", array( $this, 'ensure_key_enquiry_fields_set' ), 10, 3 );

		// Enquiry - Read
		add_filter( 'pre_get_posts', array( $this, 'restrict_enquiry_rest_listing' ));
		add_filter( 'rest_pre_dispatch', array( $this, 'block_enquiry_rest_listing' ), 10, 3);
		add_action( 'rest_api_init', array( $this, 'register_rest_api_enquiry_fields' ), 99 );

		// Office
		add_action( 'rest_api_init', array( $this, 'register_rest_api_office_fields' ), 99 );
	}

	// Restrict listing of enquiries via REST API
	public function restrict_enquiry_rest_listing(WP_Query $query) 
	{
	    if ( !is_admin() && defined('REST_REQUEST') && REST_REQUEST ) 
	    {
	        $post_type = $query->get('post_type');

	        // Check if it's the 'enquiry' CPT
	        if ( $post_type === 'enquiry' ) 
	        {
	            if ( !current_user_can('manage_propertyhive') ) 
	        	{
	                // Force an empty result
	                $query->set('post__in', [0]);
	            }
	        }
	    }
	}

	public function block_enquiry_rest_listing($response, $server, $request) 
	{
	    if ( preg_match( '#^/wp/v2/enquiry(?:/|$)#', $request->get_route() ) )
	    {
	    	$current_user = wp_get_current_user();

	        if ( !current_user_can('manage_propertyhive') ) 
	        {
	            return new WP_Error(
	                'rest_forbidden',
	                __( 'You are not allowed to access enquiries.', 'propertyhive' ),
	                ['status' => 403]
	            );
	        }

	        if ( get_option( 'propertyhive_module_disabled_enquiries', '' ) == 'yes' )
	        {
	        	return new WP_Error(
	                'rest_forbidden',
	                __('The enquiries module is currently disabled.', 'propertyhive'),
	                ['status' => 403]
	            );
	        }

	        if ( get_option( 'propertyhive_store_property_enquiries', 'yes' ) != 'yes' )
	        {
	        	return new WP_Error(
	                'rest_forbidden',
	                __('Storing of property enquiries has been disabled in the GDPR settings.', 'propertyhive'),
	                ['status' => 403]
	            );
	        }

	    }
	    return $response;
	}

	public function register_rest_api_enquiry_fields()
	{
		$field_array = array(
			'office',
			'negotiator',
			'status',
			'source',
			'properties',
			'details',
		);

		$field_array = apply_filters( 'propertyhive_rest_api_enquiry_fields', $field_array );

		foreach ( $field_array as $field )
		{
			register_rest_field( 'enquiry',
		        $field,
		        array(
		            'get_callback'  => function( $object, $field_name, $request )
		            {
		            	$enquiry = new PH_Enquiry($object['id']);

		            	$return = '';

		            	switch ($field_name)
		            	{
		            		case "office": 
		            		{ 
		            			$return = array(
		            				'name' => get_the_title($enquiry->_office_id),
		            			); 
		            			break; 
							}
							case "negotiator": 
		            		{ 
		            			$user = get_userdata( $enquiry->_negotiator_id );

		            			$return = array(
		            				'name' => ( $user !== false ) ? $user->display_name : '',
		            			); 
		            			break; 
							}
							case "properties": 
		            		{ 
		            			$return = array();

		            			$property_ids = $enquiry->get_properties();

		            			if ( !empty($property_ids) )
		            			{
		            				foreach ( $property_ids as $property_id )
		            				{
		            					$property = new PH_Property((int)$property_id);

		            					$return[] = array(
		            						'id' => (int)$property_id,
		            						'address' => $property->get_formatted_full_address()
		            					);
		            				}
		            			}
		            			break; 
							}
							case "details": 
		            		{ 
		            			$ignore_keys = array(
					                '_status',
					                '_source',
					                '_negotiator_id',
					                '_office_id',
					                '_action',
					                '_contact_id',
					                '_property_id',
					                'property_id',
					            );

		            			$enquiry_meta = get_metadata( 'post', $object['id'] );

		            			$return = array();

		            			foreach ($enquiry_meta as $key => $value)
					            {
					                if ( !in_array( $key, $ignore_keys ) && substr( $key, 0, 1 ) != '_' && strpos($key, 'captcha') === FALSE )
					                {
					                	$return[trim($key, "_")] = $value;
					                }
					            }
		            			break; 
							}
		            		default:
		            		{
		            			$return = $enquiry->{$field_name};			            	
				            }
				        }

				        $return = apply_filters( 'propertyhive_rest_api_enquiry_field_callback', $return, $field_name, $enquiry );
				        return $return;
		            },
		            'schema' => null,
		        )
		    );
		}
	}

	public function register_enquiry_rest_route()
	{
	    register_rest_route('wp/v2', '/enquiry', array(
	        array(
	            'methods'   => WP_REST_Server::CREATABLE,
	            'callback'  => array( $this, 'handle_enquiry_post' ),
	            'permission_callback' => array( $this, 'enquiry_permission_check' )
	        ),
	    ));
	}

	public function enquiry_permission_check() {
        if ( current_user_can( 'manage_propertyhive' ) ) {
            return true;
        }
        return new WP_Error( 'rest_forbidden', __( 'You do not have permission to create enquiries.', 'propertyhive' ), array( 'status' => 403 ) );
    }

	public function handle_enquiry_post(WP_REST_Request $request)
	{
	    // Handle the creation of the enquiry post
	    $params = $request->get_params();

	    // validate office ID
	    if ( isset($params['office_id']) && !empty($params['office_id']) ) 
	    {
	    	if ( get_post_type((int)$params['office_id']) != 'office' )
	    	{
		    	return new WP_Error('rest_enquiry_error', __( 'Invalid office ID passed', 'propertyhive' ) . ' ('. (int)$params['office_id'] . ')', array('status' => 400));
		    }
	    }

	    // validate negotiator ID
	    if ( isset($params['negotiator_id']) && !empty($params['negotiator_id']) ) 
	    {
	    	if ( get_userdata((int)$params['negotiator_id']) === false )
	    	{
		    	return new WP_Error('rest_enquiry_error', __( 'Invalid negotiator ID passed', 'propertyhive' ) . ' ('. (int)$params['negotiator_id'] . ')', array('status' => 400));
		    }
	    }

	    // validate source
	    $valid_sources = array(
            'office' => __( 'Office', 'propertyhive' ),
            'website' => __( 'Website', 'propertyhive' )
        );

        $valid_sources = apply_filters( 'propertyhive_enquiry_sources', $valid_sources );

	    if ( isset($params['source']) && !empty($params['source']) ) 
	    {
	    	if ( !array_key_exists($params['source'], $valid_sources) )
	    	{
			    return new WP_Error('rest_enquiry_error', __( 'Invalid source passed. Should be one of', 'propertyhive' ) . ': ' . implode(", ", array_keys($valid_sources)), array('status' => 400));
			}
	    }

	    if ( isset($params['property_id']) && !empty($params['property_id']) ) 
	    {
	    	if ( !is_array($params['property_id']) ) 
	    	{ 
	    		$property_ids = array($params['property_id']); 
	    	}
	    	else
	    	{
	    		$property_ids = $params['property_id'];
	    	}

	    	foreach ( $property_ids as $property_id )
	    	{
	    		if ( get_post_type((int)$property_id) != 'property' )
	    		{
	    			return new WP_Error('rest_enquiry_error', __( 'Invalid property ID passed', 'propertyhive' ) . ' ('. (int)$property_id . ')', array('status' => 400));
	    		}
	    	}
	    }

	    $title = ( isset($params['title']) && !empty($params['title']) ? $params['title'] : '' );
	    if (empty($title))
	    {
	    	$title = 'Enquiry';
	    	if ( isset($params['name']) && !empty($params['name']) )
	    	{
	    		$title .= ' from ' . $params['name'];
	    	}
	    	if ( isset($params['property_id']) && !empty($params['property_id']) )
	    	{
	    		if ( is_array($params['property_id']) )
	    		{
	    			$title .= ' about ' . count($params['property_id']) . ' properties';
		    	}
		    	else
		    	{
		    		$title .= ' about ' . get_the_title($params['property_id']);
		    	}
	    	}
	    }
	    $data = array(
	        'post_type'    => 'enquiry',
	        'post_status'  => 'publish',
	        'post_title'   => sanitize_text_field($title),
	        'post_content'   => '',
	        'comment_status' => 'closed',
            'ping_status'    => 'closed',
	    );

	    $post_id = wp_insert_post($data, true);

	    if ( is_wp_error($post_id) ) 
	    {
	        return new WP_Error('rest_enquiry_error', $post_id->get_error_message(), array('status' => 500));
	    }

	    // Status
	    if ( isset($params['status']) && !empty($params['status']) && in_array($params['status'], array('open', 'closed')) )
	    {
	    	update_post_meta( $post_id, '_status', sanitize_text_field($params['status']) );
	    }
	    else
	    {
			update_post_meta( $post_id, '_status', 'open' );
	    }

		if ( isset($params['office_id']) && !empty($params['office_id']) && is_numeric($params['office_id']) )
	    {
	    	// Should be a valid office ID as this has already been validated by this point
	    	update_post_meta( $post_id, '_office_id', (int)$params['office_id'] );
	    }
	    else
	    {
	        $primary_office_id = '';
	        $args = array(
	            'post_type' => 'office',
	            'nopaging' => true
	        );
	        $office_query = new WP_Query($args);

	        if ($office_query->have_posts())
	        {
	            while ($office_query->have_posts())
	            {
	                $office_query->the_post();

	                if (get_post_meta(get_the_ID(), 'primary', TRUE) == '1')
	                {
	                    $primary_office_id = get_the_ID();
	                }
	            }
	        }
	        $office_query->reset_postdata();

	        update_post_meta( $post_id, '_office_id', $primary_office_id );
	    }

	    if ( isset($params['negotiator_id']) && !empty($params['negotiator_id']) && is_numeric($params['negotiator_id']) )
	    {
	    	// Should be a valid neg ID as this has already been validated by this point
	    	update_post_meta( $post_id, '_negotiator_id', (int)$params['negotiator_id'] );
	    }
	    
		// Source
		if ( isset($params['source']) && !empty($params['source']) && array_key_exists($params['source'], $valid_sources) )
	    {
	    	update_post_meta( $post_id, '_source', sanitize_text_field($params['source']) );
	    }
	    else
	    {
			update_post_meta( $post_id, '_source', 'website' );
	    }

	    if ( isset($params['name']) && !empty($params['name']) )
	    {
	    	update_post_meta( $post_id, 'name', sanitize_text_field($params['name']) );
	    }

	    if ( isset($params['email_address']) && !empty($params['email_address']) )
	    {
	    	update_post_meta( $post_id, 'email_address', sanitize_email($params['email_address']) );
	    }

	    if ( isset($params['telephone_number']) && !empty($params['telephone_number']) )
	    {
	    	update_post_meta( $post_id, 'telephone_number', sanitize_text_field($params['telephone_number']) );
	    }

	    if ( isset($params['message']) && !empty($params['message']) )
	    {	
	    	update_post_meta( $post_id, 'message', sanitize_textarea_field($params['message']) );
	    }

	    if ( isset($params['property_id']) && !empty($params['property_id']) && ( is_numeric($params['property_id']) || is_array($params['property_id']) ) )
	    {
	    	// Should be a valid property  ID as this has already been validated by this point
	    	$property_ids = $params['property_id'];
	    	if ( !is_array($property_ids) ) { $property_ids = array($property_ids); }

	    	foreach ( $property_ids as $property_id )
	    	{
		    	add_post_meta( $post_id, 'property_id', (int)$property_id );
		    }
	    }

	    return new WP_REST_Response(array('ID' => $post_id), 201);
	}

	public function ensure_key_property_fields_set( $post, $request, $creating )
	{
		// Country / price actual
		$country = get_post_meta( $post->ID, '_address_country', true );
		if ( $country == '' )
		{
			$default_country = get_option( 'propertyhive_default_country', 'GB' );
			update_post_meta( $post->ID, '_address_country', $default_country );
		}

		$ph_countries = new PH_Countries();
		$ph_countries->update_property_price_actual( $post->ID );

		// Department
		$department = get_post_meta( $post->ID, '_department', true );
		if ( $department == '' )
		{
			$primary_department = get_option( 'propertyhive_primary_department', 'residential-sales' );
			update_post_meta( $post->ID, '_department', $primary_department );
		}

		// On Market
		$on_market = get_post_meta( $post->ID, '_on_market', true );
		if ( $on_market != 'yes' )
		{
			update_post_meta( $post->ID, '_on_market', '' );
		}

		// Featured
		$featured = get_post_meta( $post->ID, '_featured', true );
		if ( $featured != 'yes' )
		{
			update_post_meta( $post->ID, '_featured', '' );
		}

		// Office
		$office_id = get_post_meta( $post->ID, '_office_id', true );
		if ( $office_id == '' )
		{
			$primary_office_id = '';
			$args = array(
	            'post_type' => 'office',
	            'nopaging' => true
	        );
	        $office_query = new WP_Query($args);
	        
	        if ($office_query->have_posts())
	        {
	            while ($office_query->have_posts())
	            {
	                $office_query->the_post();

	                if (get_post_meta(get_the_ID(), 'primary', TRUE) == '1')
	                {
	                	$primary_office_id = get_the_ID();
	                }
	            }
	        }
	        $office_query->reset_postdata();
	        
			update_post_meta( $post->ID, '_office_id', $primary_office_id );
		}
	}

	public function modify_rest_order_by($params)
	{
		$fields = ["price-asc", "price-desc"];

		foreach ( $fields as $key => $value ) 
		{
			$params['orderby']['enum'][] = $value;
		}

		return $params;
	}

	public function modify_rest_property_query($args, $request)
	{
		/*if ( !isset( $args['meta_query'] ) )
		{
			$args['meta_query'] = array();
		}*/

		$PH_Query = new PH_Query();

		// Meta query
		// 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.
		$args['meta_query'] = $PH_Query->get_meta_query();
        
        // Tax query
        // 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.
        $args['tax_query'] = $PH_Query->get_tax_query();

        // Date query
		$args['date_query'] = $PH_Query->get_date_query();

		$ordering   = $PH_Query->get_search_results_ordering_args();
		$args['orderby'] = $ordering['orderby'] . ' post_title';
		$args['order'] = $ordering['order'];
		if ( isset( $ordering['meta_key'] ) )
			// 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.
			$args['meta_key'] = $ordering['meta_key'];

		$args = apply_filters( 'propertyhive_rest_api_query_args', $args );
		
		return $args;
	}

	public function register_rest_api_property_fields()
	{
		$field_array = array(
			'department',
			'reference_number',
			'address_street',
			'address_two',
			'address_three',
			'address_four',
			'address_postcode',
			'address_country',
			'latitude',
			'longitude',
			'price_actual',
			'price',
			'price_from',
			'price_to',
			'price_units',
			'rent_from',
			'rent_to',
			'rent_units',
			'price_formatted',
			'rent_frequency',
			'currency',
			'price_qualifier',
			'sale_by',
			'tenure',
			'council_tax_band',
			'deposit',
			'furnished',
			'available_date',
			'bedrooms',
			'bathrooms',
			'reception_rooms',
			'property_type',
			'parking',
			'outside_space',
			'for_sale',
			'to_rent',
			'floor_area_from',
			'floor_area_to',
			'floor_area_units',
			'site_area_from',
			'site_area_to',
			'site_area_units',
			'on_market',
			'featured',
			'location',
			'availability',
			'marketing_flag',
			'features',
			'description',
			'office',
			'negotiator',
			'images',
			'floorplans',
			'brochures',
			'epcs',
			'virtual_tours',
			'views_total',
			'views_today',
			'views_yesterday',
			'views_last_7_days',
			'views_last_14_days',
			'views_last_30_days',
		);

		$field_array = apply_filters( 'propertyhive_rest_api_property_fields', $field_array );

		foreach ( $field_array as $field )
		{
			register_rest_field( 'property',
		        $field,
		        array(
		            'get_callback'  => function( $object, $field_name, $request )
		            {
		            	$property = new PH_Property($object[ 'id' ]);

		            	$return = '';

		            	switch ($field_name)
		            	{
		            		case "price":
		            		{
		            			if ( $property->_poa != 'yes' )
		            			{
		            				$department = $property->_department;
								    if ( ph_get_custom_department_based_on( $department ) !== false )
							        {
							        	$department = ph_get_custom_department_based_on( $department );
							        }
		            				if ( $department == 'residential-lettings' ) { $return = $property->_rent; }else{ $return = $property->_price; } 
		            			}
		            			break;
		            		}
		            		case "price_from":
		            		case "price_to":
		            		{
		            			if ( $property->_price_poa != 'yes' )
		            			{
		            				$return = $property->{'_' . $field_name};
		            			}
		            			break;
		            		}
		            		case "rent_from":
		            		case "rent_to":
		            		{
		            			if ( $property->_rent_poa != 'yes' )
		            			{
		            				$return = $property->{'_' . $field_name};
		            			}
		            			break;
		            		}
		            		case "price_formatted": { $return = $property->get_formatted_price(); break; }
		            		case "features": { $return = $property->get_features(); break; }
		            		case "description": { $return = $property->get_formatted_description(); break; }
		            		case "office": 
		            		{ 
		            			$return = array(
		            				'name' => $property->office_name,
		            				'address' => $property->office_address,
		            				'telephone_number' => $property->office_telephone_number,
		            				'email_address' => $property->office_email_address,
		            			); 
		            			break; 
							}
							case "negotiator": 
		            		{ 
		            			$return = array(
		            				'name' => $property->negotiator_name,
		            				'telephone_number' => $property->negotiator_telephone_number,
		            				'email_address' => $property->negotiator_email_address,
		            			); 
		            			break; 
							}
							case "images":
							{
								$images_array = array();
								if ( get_option('propertyhive_images_stored_as', '') == 'urls' )
								{
									$image_urls = $property->_photo_urls;
									if ( !is_array($image_urls) ) { $image_urls = array(); }

									foreach ( $image_urls as $image_url )
									{
										if ( isset($image_url['url']) )
										{
											$images_array[] = array(
												'url' => $image_url['url'],
												'large' => $image_url['url'],
												'medium' => $image_url['url'],
												'thumbnail' => $image_url['url'],
											);
										}
									}
								}
								else
								{
									$image_ids = $property->get_gallery_attachment_ids();
									foreach ( $image_ids as $image_id )
									{
										$image_url = wp_get_attachment_url($image_id);
										if ($image_url !== false)
										{
											$large_image = wp_get_attachment_image_src( $image_id, 'large' );
											$medium_image = wp_get_attachment_image_src( $image_id, 'medium' );
											$thumbnail_image = wp_get_attachment_image_src( $image_id, 'thumbnail' );

											$images_array[] = array(
												'url' => $image_url,
												'large' => ( $large_image !== FALSE ? $large_image[0] : $image_url ),
												'medium' => ( $medium_image !== FALSE ? $medium_image[0] : $image_url ),
												'thumbnail' => ( $thumbnail_image !== FALSE ? $thumbnail_image[0] : $image_url ),
											);
										}
									}
								}
								$return = $images_array;
								break;
							}
							case "floorplans":
							{
								$floorplans_array = array();
								if ( get_option('propertyhive_floorplans_stored_as', '') == 'urls' )
								{
									$floorplan_urls = $property->_floorplan_urls;
									if ( !is_array($floorplan_urls) ) { $floorplan_urls = array(); }

									foreach ( $floorplan_urls as $floorplan_url )
									{
										if ( isset($floorplan_url['url']) )
										{
											$floorplans_array[] = array(
												'url' => $floorplan_url['url'],
												'large' => $floorplan_url['url'],
												'medium' => $floorplan_url['url'],
												'thumbnail' => $floorplan_url['url'],
											);
										}
									}
								}
								else
								{
									$floorplan_ids = $property->get_floorplan_attachment_ids();
									foreach ( $floorplan_ids as $floorplan_id )
									{
										$floorplan_url = wp_get_attachment_url($floorplan_id);
										if ($floorplan_url !== false)
										{
											$large_image = wp_get_attachment_image_src( $floorplan_id, 'large' );
											$medium_image = wp_get_attachment_image_src( $floorplan_id, 'medium' );
											$thumbnail_image = wp_get_attachment_image_src( $floorplan_id, 'thumbnail' );

											$floorplans_array[] = array(
												'url' => $floorplan_url,
												'large' => ( $large_image !== FALSE ? $large_image[0] : $floorplan_url ),
												'medium' => ( $medium_image !== FALSE ? $medium_image[0] : $floorplan_url ),
												'thumbnail' => ( $thumbnail_image !== FALSE ? $thumbnail_image[0] : $floorplan_url ),
											);
										}
									}
								}
								$return = $floorplans_array;
								break;
							}
							case "brochures":
							{
								$brochures_array = array();
								if ( get_option('propertyhive_brochures_stored_as', '') == 'urls' )
								{
									$brochure_urls = $property->_brochure_urls;
									if ( !is_array($brochure_urls) ) { $brochure_urls = array(); }

									foreach ( $brochure_urls as $brochure_url )
									{
										if ( isset($brochure_url['url']) )
										{
											$brochures_array[] = array(
												'url' => $brochure_url['url'],
											);
										}
									}
								}
								else
								{
									$brochure_ids = $property->get_brochure_attachment_ids();
									foreach ( $brochure_ids as $brochure_id )
									{
										$brochure_url = wp_get_attachment_url($brochure_id);
										if ($brochure_url !== false)
										{
											$brochures_array[] = array(
												'url' => $brochure_url,
											);
										}
									}
								}
								$return = $brochures_array;
								break;
							}
							case "epcs":
							{
								$epcs_array = array();
								if ( get_option('propertyhive_epcs_stored_as', '') == 'urls' )
								{
									$epc_urls = $property->_epc_urls;
									if ( !is_array($epc_urls) ) { $epc_urls = array(); }

									foreach ( $epc_urls as $epc_url )
									{
										if ( isset($epc_url['url']) )
										{
											$epcs_array[] = array(
												'url' => $epc_url['url'],
											);
										}
									}
								}
								else
								{
									$epc_ids = $property->get_epc_attachment_ids();
									foreach ( $epc_ids as $epc_id )
									{
										$epc_url = wp_get_attachment_url($epc_id);
										if ($epc_url !== false)
										{
											$epcs_array[] = array(
												'url' => $epc_url,
											);
										}
									}
								}
								$return = $epcs_array;
								break;
							}
		            		case "virtual_tours":
		            		{
		            			$return = $property->get_virtual_tours();
		            			break;
		            		}
		            		case "views_today":
		            		case "views_yesterday":
		            		case "views_last_7_days":
		            		case "views_last_14_days":
		            		case "views_last_30_days":
		            		case "views_total":
		            		{
		            			$view_statistics = $property->_view_statistics;
								if ( !is_array($view_statistics) )
								{
								    $view_statistics = array();
								}

								$views = 0;

								$today = current_datetime()->setTime(0, 0, 0);

								$date_from = new DateTimeImmutable('2001-01-01', wp_timezone());
								$date_to   = $today;

								switch ($field_name)
								{
								    case 'views_last_7_days':
								    {
								        $date_from = $today->modify('-7 days');
								        $date_to   = $today->modify('-1 day');
								        break;
								    }

								    case 'views_last_14_days':
								    {
								        $date_from = $today->modify('-14 days');
								        $date_to   = $today->modify('-1 day');
								        break;
								    }

								    case 'views_last_30_days':
								    {
								        $date_from = $today->modify('-30 days');
								        $date_to   = $today->modify('-1 day');
								        break;
								    }

								    case 'views_today':
								    {
								        $date_from = $today;
								        $date_to   = $today;
								        break;
								    }

								    case 'views_yesterday':
								    {
								        $date_from = $today->modify('-1 day');
								        $date_to   = $date_from;
								        break;
								    }
								}

								for ( $date = $date_from; $date <= $date_to; $date = $date->modify('+1 day') )
								{
								    $date_key = $date->format('Y-m-d');

								    if ( isset($view_statistics[$date_key]) )
								    {
								        $views += (int)$view_statistics[$date_key];
								    }
								}

		            			$return = $views;
		            			break;
		            		}
		            		default:
		            		{
		            			$return = $property->{$field_name};			            	
				            }
				        }

				        $return = apply_filters( 'propertyhive_rest_api_property_field_callback', $return, $field_name, $property );
				        return $return;
		            },
		            'update_callback' => function ( $value, $object, $field_name ) 
		            {
		            	if ( taxonomy_exists($field_name) )
		            	{
		            		wp_set_post_terms( $object->ID, $value, $field_name );
		            	}
		            	else
		            	{
		            		switch ( $field_name )
		            		{
		            			case "price":
		            			{
		            				$property = new PH_Property($object->ID);

		            				$price = '';
		            				if ( $value != '' )
		            				{
		            					$price = preg_replace("/[^0-9.]/", '', $value);
		            				}

		            				if ( isset($property->_department) && $property->_department == 'residential-lettings' )
		            				{
		            					update_post_meta( $object->ID, '_rent', $price );
		            				}
		            				else
		            				{
		            					update_post_meta( $object->ID, '_price', $price );
		            				}

		            				if ( $price != '' )
		            				{
			            				// Store price in common currency (GBP) used for ordering
							            $ph_countries = new PH_Countries();
							            $ph_countries->update_property_price_actual( $object->ID );
							        }

		            				break;
		            			}
		            			case "features":
		            			{
		            				if ( is_array($value) && !empty($value) )
		            				{
		            					update_post_meta( $object->ID, '_features', count($value) );

		            					$i = 0;
		            					foreach ( $value as $feature )
		            					{
		            						update_post_meta( $object->ID, '_feature_' . $i, $feature );
		            						
		            						++$i;
		            					}
		            				}
		            				break;
		            			}
		            			case "description":
		            			{
                                if ( ! is_string( $value ) ) {
                                    return new WP_Error( 'propertyhive_rest_description_invalid', __( 'The description must be text.', 'propertyhive' ), array( 'status' => 400 ) );
                                }
                                $value = propertyhive_sanitize_description( $value );
		            				$property = new PH_Property($object->ID);

		            				if ( isset($property->_department) && $property->_department == 'commercial' )
		            				{
		            					update_post_meta( $object->ID, '_descriptions', '1' );
										update_post_meta( $object->ID, '_description_name_0', '' );
                                        update_post_meta( $object->ID, '_description_0', wp_slash( $value ) );
		            				}
		            				else
		            				{
		            					update_post_meta( $object->ID, '_rooms', '1' );
										update_post_meta( $object->ID, '_room_name_0', '' );
							            update_post_meta( $object->ID, '_room_dimensions_0', '' );
							            update_post_meta( $object->ID, '_room_description_0', wp_slash( $value ) );
		            				}
		            				break;
		            			}
		            			case "images":
		            			case "floorplans":
		            			case "brochures":
		            			case "epcs":
		            			{
                                if ( ! current_user_can( 'upload_files' ) || ! current_user_can( 'edit_post', $object->ID ) ) {
                                    return new WP_Error( 'propertyhive_rest_media_forbidden', __( 'You do not have permission to update property media.', 'propertyhive' ), array( 'status' => 403 ) );
                                }
                                if ( ! is_array( $value ) || count( $value ) > 100 ) {
                                    return new WP_Error( 'propertyhive_rest_media_invalid', __( 'Supply an array containing no more than 100 media items.', 'propertyhive' ), array( 'status' => 400 ) );
                                }
                                foreach ( $value as $media_item ) {
                                    if ( ! is_array( $media_item ) || ! isset( $media_item['url'] ) || ! is_string( $media_item['url'] ) || ! preg_match( '~^(https?:)?//~i', $media_item['url'] ) ) {
                                        return new WP_Error( 'propertyhive_rest_media_invalid', __( 'Each media item must contain an HTTP or HTTPS URL.', 'propertyhive' ), array( 'status' => 400 ) );
                                    }
                                }

		            				if ( !function_exists('media_handle_upload') ) {
										require_once(ABSPATH . "wp-admin" . '/includes/image.php');
										require_once(ABSPATH . "wp-admin" . '/includes/file.php');
										require_once(ABSPATH . "wp-admin" . '/includes/media.php');
									}

		            				$hive_field_name = trim($field_name, 's');
		            				if ( $hive_field_name == 'image' ) { $hive_field_name = 'photo'; }

		            				if ( is_array($value) && !empty($value) )
		            				{
		            					if ( get_option('propertyhive_' . $field_name . '_stored_as', '') == 'urls' )
										{
											$media_items = array();
											foreach ( $value as $media_item )
											{
												if ( 
													isset($media_item['url']) &&
													(
														substr( strtolower($media_item['url']), 0, 2 ) == '//' || 
														substr( strtolower($media_item['url']), 0, 4 ) == 'http'
													)
												)
												{
													$media_items[] = array(
														'url' => $media_item['url'],
													);
												}
											}
											update_post_meta( $object->ID, '_' . $hive_field_name . '_urls', $media_items );
										}
										else
										{
											$media_ids = array();
											$previous_media_ids = get_post_meta( $object->ID, '_' . $hive_field_name . 's', TRUE );
											foreach ( $value as $media_item )
											{
												if ( 
													isset($media_item['url']) &&
													(
														substr( strtolower($media_item['url']), 0, 2 ) == '//' || 
														substr( strtolower($media_item['url']), 0, 4 ) == 'http'
													)
												)
												{
													// This is a URL
													$url = $media_item['url'];
													$description = '';

													$filename = basename( $url );

													// Check, based on the URL, whether we have previously imported this media
													$imported_previously = false;
													$imported_previously_id = '';
													if ( is_array($previous_media_ids) && !empty($previous_media_ids) )
													{
														foreach ( $previous_media_ids as $previous_media_id )
														{
															if ( 
																get_post_meta( $previous_media_id, '_imported_url', TRUE ) == $url
															)
															{
																$imported_previously = true;
																$imported_previously_id = $previous_media_id;
																break;
															}
														}
													}
													
													if ($imported_previously)
													{
														$media_ids[] = $imported_previously_id;
													}
													else
													{
													    $tmp = self::download_media( $url );
													    $file_array = array(
													        'name' => basename( $url ),
													        'tmp_name' => $tmp
													    );

													    // Check for download errors
													    if ( is_wp_error( $tmp ) ) 
													    {
													        return $tmp;
													    }
													    else
													    {
														    $id = media_handle_sideload( $file_array, $object->ID, $description, array('post_title' => $filename) );

														    // Check for handle sideload errors.
														    if ( is_wp_error( $id ) ) 
														    {
														        wp_delete_file( $file_array['tmp_name'] );
														        
														        return $id;
														    }
														    else
														    {
														    	$media_ids[] = $id;

														    	update_post_meta( $id, '_imported_url', $url);
														    }
														}
													}
												}
											}
											update_post_meta( $object->ID, '_' . $hive_field_name . 's', $media_ids );

											// Loop through $previous_media_ids, check each one exists in $media_ids, and if it doesn't then delete
											if ( is_array($previous_media_ids) && !empty($previous_media_ids) )
											{
												foreach ( $previous_media_ids as $previous_media_id )
												{
													if ( ! in_array( $previous_media_id, $media_ids ) && current_user_can( 'delete_post', $previous_media_id ) )
													{
														if ( wp_delete_attachment( $previous_media_id, TRUE ) !== FALSE )
														{

														}
													}
												}
											}
										}
		            				}

		            				break;
		            			}
		            			case "virtual_tours":
		            			{
		            				if ( is_array($value) && !empty($value) )
		            				{
		            					update_post_meta( $object->ID, '_virtual_tours', count($value) );

		            					$i = 0;
		            					foreach ( $value as $virtual_tour )
		            					{
		            						update_post_meta( $object->ID, '_virtual_tour_' . $i, ( isset($virtual_tour['url']) ? $virtual_tour['url'] : '' ) );
		            						update_post_meta( $object->ID, '_virtual_tour_label_' . $i, ( isset($virtual_tour['label']) ? $virtual_tour['label'] : '' ) );

		            						++$i;
		            					}
		            				}
		            				break;
		            			}
		            			default:
		            			{
		            				update_post_meta( $object->ID, '_' . $field_name, $value );
		            			}
		            		}
						}

						do_action( 'propertyhive_rest_api_property_field_update_callback', $value, $object, $field_name );
					},
		            'schema' => null,
		        )
		    );
		}
	}

	public function rest_prepare_property($response, $post, $request) 
	{
		// Hide/show fields dynamically per item before it's returned

	    $data = $response->get_data();

	    // Get the department
	    $department = $data['department'] ?? get_post_meta($post->ID, '_department', true);
	    if ( ph_get_custom_department_based_on( $department ) !== false )
        {
        	$department = ph_get_custom_department_based_on( $department );
        }

	    // Define the "not applicable" fields per department
	    $remove_for = [
	        'residential-sales' => [
	        	// specify non-sales fields
	            'available_date', 'deposit', 'furnished', 'rent_frequency', 
	            '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'
	        ],
	        'residential-lettings' => [
	            // specify non-lettings fields
	            'sale_by', 'tenure',
	            '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',
	        ],
	        'commercial' => [
	        	// specify non-commercial fields
	        	'price', 'price_actual', 'bedrooms', 'bathrooms', 'reception_rooms', 'outside_space', 'parking',
	            'available_date', 'deposit', 'furnished', 'rent_frequency',
	        ],
	    ];

	    if ( !empty($remove_for[$department]) ) 
	    {
	        foreach ( $remove_for[$department] as $k ) 
	        {
	            unset($data[$k]);
	        }
	    }

	    $response->set_data($data);

	    return $response;
	}

	public function register_rest_api_office_fields()
	{
		$field_array = array(
			'primary',
			'office_address_1',
			'office_address_2',
			'office_address_3',
			'office_address_4',
			'office_address_postcode',
			'office_latitude',
			'office_longitude',
		);

		$departments = ph_get_departments();

        foreach ( $departments as $key => $value )
        {
        	$field_array[] = 'office_telephone_number_' . str_replace("residential-", "", $key);
        	$field_array[] = 'office_email_address_' . str_replace("residential-", "", $key);
        }

		$field_array = apply_filters( 'propertyhive_rest_api_office_fields', $field_array );

		foreach ( $field_array as $field )
		{
			register_rest_field( 'office',
		        $field,
		        array(
		            'get_callback'  => function( $object, $field_name, $request )
		            {
		            	$return = '';

		            	switch ($field_name)
		            	{
		            		default:
		            		{
		            			$return = get_post_meta( $object[ 'id' ], '_' . $field_name, TRUE );
				            }
				        }

				        $return = apply_filters( 'propertyhive_rest_api_office_field_callback', $return, $field_name, $object[ 'id' ] );
				        return $return;
		            },
		            'update_callback' => null,
		            'schema' => null,
		        )
		    );
		}
	}

}

```
