get_license_type(); if ( $license_type == 'pro' ) { if ( get_option('propertyhive_pro_license_key', '') != '' ) { if ( !$this->is_valid_pro_license_key() ) { // show warning return false; } // Valid license. Check this plugin is valid for the purchased type of plan (i.e. map search can't be used with an 'import only' plan) $feature = get_ph_pro_feature( $slug ); $product_id_and_package = PH()->license->get_pro_license_product_id_and_package(); if ( isset($product_id_and_package['success']) && $product_id_and_package['success'] === true ) { if ( isset($feature['plans']) && isset($product_id_and_package['package']) && in_array($product_id_and_package['package'], $feature['plans']) ) { } else { // show warning return false; } } } } return $can; } public function ph_check_add_on_can_be_updated( $can, $slug ) { $license_type = $this->get_license_type(); if ( $license_type == 'old' ) { $license = get_option( 'propertyhive_license_key_details', array() ); if ( !is_array( $license ) ) { $license = array(); } if ( isset($license['active']) && $license['active'] == '1' && isset($license['expires_at']) && $license['expires_at'] != '' && strtotime($license['expires_at']) > time() ) { return true; } } elseif ( $license_type == 'pro' ) { // get new license information if ( get_option('propertyhive_pro_license_key', '') != '' ) { if ( $this->is_valid_pro_license_key() ) { // Valid license. Check this plugin is valid for the purchased type of plan (i.e. map search can't be used with an 'import only' plan) $feature = get_ph_pro_feature( $slug ); $product_id_and_package = PH()->license->get_pro_license_product_id_and_package(); if ( isset($product_id_and_package['success']) && $product_id_and_package['success'] === true ) { if ( ( $product_id_and_package['is_trial'] ?? null ) !== false ) { return false; } if ( isset($feature['plans']) && isset($product_id_and_package['package']) && in_array($product_id_and_package['package'], $feature['plans']) ) { return true; } } } } } return $can; } public function get_license_type() { $license_type = get_option( 'propertyhive_license_type', '' ); if ( $license_type == '' ) { // We don't know. Let's work it out by seeing if they have a license key $existing_license_key = get_option( 'propertyhive_license_key', '' ); if ( $existing_license_key != '' ) { $license = PH()->license->get_current_license(); if ( isset($license['active']) && $license['active'] == '1' ) { return 'old'; } } } return $license_type; // pro / old } private function get_data_for_license_check() { global $wpdb; $license_type = $this->get_license_type(); $data = array(); $data['license_key_type'] = $license_type; $data['license_key'] = ( $license_type == 'old' ? get_option( 'propertyhive_license_key', '' ) : get_option( 'propertyhive_pro_license_key', '' ) ); $data['url'] = home_url(); if ( get_option( 'propertyhive_data_sharing' ) != 'no' ) { // Not opted-out // Retrieve current theme info $theme_data = wp_get_theme(); $theme = $theme_data->Name . ' ' . $theme_data->Version; $data['php_version'] = phpversion(); $data['ph_version'] = PH_VERSION; $data['wp_version'] = get_bloginfo( 'version' ); $data['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) && is_string( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : ''; $data['install_date'] = get_option('propertyhive_install_timestamp', ''); if ( $data['install_date'] != '' && $data['install_date'] != 0 ) { $data['install_date'] = gmdate("jS F Y", $data['install_date']); } $data['multisite'] = is_multisite(); $data['theme'] = $theme; $data['email'] = get_bloginfo( 'admin_email' ); // Retrieve current plugin information if ( !function_exists( 'get_plugins' ) ) { include ABSPATH . '/wp-admin/includes/plugin.php'; } $plugins = get_plugins(); // Fetch all plugins with details $active_plugins = get_option('active_plugins', array()); $active_plugins_data = []; $inactive_plugins_data = []; // Loop through plugins to separate active and inactive along with version foreach ($plugins as $plugin_path => $plugin_data) { $plugin_info = [ 'name' => isset($plugin_data['Name']) ? $plugin_data['Name'] : '', 'version' => isset($plugin_data['Version']) ? $plugin_data['Version'] : '', 'path' => $plugin_path, ]; if (in_array($plugin_path, $active_plugins)) { $active_plugins_data[] = $plugin_info; } else { $inactive_plugins_data[] = $plugin_info; } } $data['active_plugins'] = $active_plugins_data; $data['inactive_plugins'] = $inactive_plugins_data; $data['locale'] = get_locale(); // Import info $property_import = get_option( 'propertyhive_property_import', array() ); if ( !is_array($property_import) ) { $property_import = array(); } $formats = array(); foreach ( $property_import as $import_id => $options ) { if ( $options['running'] == '1' ) { if ( $options['format'] == 'xml' ) { $options['format'] .= ' (' . esc_url($options['xml_url']) . ')'; } elseif ( $options['format'] == 'csv' ) { $options['format'] .= ' (' . esc_url($options['csv_url']) . ')'; } $formats[] = $options['format']; } } $data['property_import_formats'] = $formats; // Exports $formats = array(); $exports = array( 'propertyhive_realtimefeed' => array( 'label' => 'RTDF', 'field' => 'send_property_api_url', ), 'propertyhive_zooplarealtimefeed' => array( 'label' => 'Zoopla', 'field' => 'send_property_api_url', ), 'propertyhive_blmexport' => array( 'label' => 'BLM', 'field' => 'ftp_host', ), ); foreach ( $exports as $option_name => $config ) { $property_export = get_option( $option_name, array() ); if ( ! is_array( $property_export ) ) { continue; } if ( ! isset( $property_export['portals'] ) ) { continue; } if ( ! is_array( $property_export['portals'] ) ) { continue; } $portals = isset( $property_export['portals'] ) && is_array( $property_export['portals'] ) ? $property_export['portals'] : array(); foreach ( $portals as $portal_id => $portal ) { if ( ! is_array( $portal ) ) { continue; } if ( ! is_array( $portal ) || ( $portal['mode'] ?? '' ) !== 'live' ) { continue; } $destination = $portal[ $config['field'] ] ?? ''; $formats[] = sprintf( '%s (%s)', $config['label'], $destination ); } } $data['property_export_formats'] = $formats; // Locrating $locrating_settings = get_option( 'propertyhive_locrating', array() ); if ( !empty($locrating_settings) && isset($locrating_settings['enabled']) ) { $data['locrating_enabled'] = ( $locrating_settings['enabled'] == 1 ? $locrating_settings['enabled'] : 0 ); } // Search analytics // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Aggregate current custom-table analytics for the license report; new search events and retention change these counts independently. $counts = $wpdb->get_row( "SELECT COALESCE( SUM(searched_at >= UTC_TIMESTAMP() - INTERVAL 7 DAY), 0 ) AS last_7_days, COALESCE( SUM(searched_at >= UTC_TIMESTAMP() - INTERVAL 30 DAY), 0 ) AS last_30_days, COUNT(*) AS last_90_days FROM {$wpdb->prefix}ph_search_log WHERE searched_at >= UTC_TIMESTAMP() - INTERVAL 90 DAY", ARRAY_A ); if ( is_array($counts) ) { $data['searches_last_7_days'] = (int) ( $counts['last_7_days'] ?? 0 ); $data['searches_last_30_days'] = (int) ( $counts['last_30_days'] ?? 0 ); $data['searches_last_90_days'] = (int) ( $counts['last_90_days'] ?? 0 ); } // get counts $post_types = array('office', 'property', 'contact', 'appraisal', 'viewing', 'offer', 'sale', 'tenancy', 'key_date'); foreach ( $post_types as $post_type ) { $args = array( 'post_type' => $post_type, 'fields' => 'ids', 'posts_per_page' => 1, 'post_status' => 'publish' ); if ( $post_type == 'property' ) { // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- On-market state is stored in property metadata; retrieve one ID and use found_posts for the aggregate count. $args['meta_query'] = array( array( 'key' => '_on_market', 'value' => 'yes' ) ); } $post_query = new WP_Query( $args ); $data['post_type_count_' . $post_type] = $post_query->found_posts; wp_reset_postdata(); } $departments = array_keys( ph_get_departments() ); foreach ( $departments as $department ) { $department_option = 'propertyhive_active_departments_' . str_replace( 'residential-', '', $department ); if ( get_option( $department_option ) != 'yes' ) { continue; } $args = array( 'post_type' => 'property', 'fields' => 'ids', 'posts_per_page' => 1, 'post_status' => 'publish', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Department and on-market state use property metadata; retrieve one ID and use found_posts for the opted-in aggregate count. 'meta_query' => array( array( 'key' => '_on_market', 'value' => 'yes' ), array( 'key' => '_department', 'value' => $department ) ) ); $post_query = new WP_Query( $args ); $data['property_count_by_department_' . $department] = $post_query->found_posts; wp_reset_postdata(); } $data = apply_filters( 'propertyhive_license_check_data', $data ); } return $data; } /** * Automated check for licenses * */ public function ph_check_licenses( $force_check = false ) { // Do a maximum of once per week $last_send = get_option( 'propertyhive_last_license_check', '' ); if( !$force_check && is_numeric( $last_send ) && $last_send > strtotime( '-1 week' ) ) { return false; } update_option( 'propertyhive_last_license_check', time() ); // Retain last-known license details during outages; replace them only with a valid HTTPS response. update_option( 'propertyhive_license_key_error', '', 'no' ); $data = $this->get_data_for_license_check(); $request = wp_remote_post( 'http://license.wp-property-hive.com/check-license.php', array( 'method' => 'POST', 'timeout' => 20, 'redirection' => 1, 'httpversion' => '1.1', 'blocking' => true, 'body' => $data, 'user-agent' => 'PH/' . PH_VERSION . '; ' . get_bloginfo( 'url' ) ) ); if ( is_wp_error( $request ) ) { update_option( 'propertyhive_license_key_error', __( 'The license service could not be reached securely. Your saved license details have been retained.', 'propertyhive' ) . ' ' . $request->get_error_message(), 'no' ); return false; } if ( 200 !== wp_remote_retrieve_response_code( $request ) || '' === wp_remote_retrieve_body( $request ) ) { update_option( 'propertyhive_license_key_error', __( 'The license service returned an invalid response.', 'propertyhive' ) . ' ' . wp_remote_retrieve_response_code( $request ), 'no' ); return false; } $body = @unserialize($request['body'], ['allowed_classes' => false]); if ( $body !== FALSE && is_array($body) ) { update_option( 'propertyhive_license_key_details', $body, 'no' ); } else { update_option( 'propertyhive_license_key_error', __( 'The license service returned invalid license data.', 'propertyhive' ) . ' ' . print_r($body, true), 'no' ); } } public function get_current_license() { $license = get_option( 'propertyhive_license_key_details', array() ); if ( !is_array( $license ) ) { $license = array(); } return $license; } public function is_valid_pro_license_key($force = false) { $license = $this->get_current_pro_license($force); if ( isset($license['success']) && $license['success'] === true ) { return true; } return false; } public function activate_pro_license_key() { $license = $this->get_pro_license_product_id_and_package(true); if ( isset($license['success']) && $license['success'] === true ) { $license_key = get_option( 'propertyhive_pro_license_key', '' ); $instance_id = get_option( 'propertyhive_pro_instance_id', '' ); if ( empty($instance_id) ) { $instance_id = wp_generate_password( 12, false ); // disable specialchars update_option('propertyhive_pro_instance_id', $instance_id ); } $data = array(); // found API key. It's product ID will be stored in product_id $url = 'https://wp-property-hive.com/?'; $url .= 'wc-api=wc-am-api&'; $url .= 'wc_am_action=activate&'; $url .= 'instance=' . $instance_id . '&'; $url .= 'object=' . wp_parse_url( get_site_url(), PHP_URL_HOST ) . '&'; $url .= 'product_id=' . $license['product_id'] . '&'; $url .= 'api_key=' . $license_key; $response = wp_remote_post( $url, array( 'body' => $data, ) ); if ( is_wp_error($response) ) { $return = array( 'success' => false, 'error' => __( 'Failed to activate license status', 'propertyhive' ) . ': ' . $response->get_error_message() ); return $return; } if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { $return = array( 'success' => false, 'error' => sprintf( /* translators: %s: HTTP header response code */ __( 'Received response code %s when activating license key status', 'propertyhive' ), wp_remote_retrieve_response_code( $response ) ) ); return $return; } $result = $response['body']; $body = json_decode($result, true); if ( json_last_error() !== JSON_ERROR_NONE ) { $return = array( 'success' => false, 'error' => __( 'Failed to decode response when activating license key status. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result ) ); return $return; } if ( isset($body['success']) ) { if ( $body['success'] === true ) { $return = array( 'success' => true, ); return $return; } else { $return = array( 'success' => false, 'error' => __( 'Error when activating license key', 'propertyhive' ) . ': ' . $body['error'] ); return $return; } } else { $return = array( 'success' => false, 'error' => __( 'Something went wrong when trying to activate license key', 'propertyhive' ) . ': ' . wp_json_encode( $body ) ); return $return; } } else { return $license; } } public function deactivate_pro_license_key() { $license = $this->get_pro_license_product_id_and_package(true); if ( isset($license['success']) && $license['success'] === true ) { $license_key = get_option( 'propertyhive_pro_license_key', '' ); $instance_id = get_option( 'propertyhive_pro_instance_id', '' ); if ( empty($instance_id) ) { $instance_id = wp_generate_password( 12, false ); // disable specialchars update_option('propertyhive_pro_instance_id', $instance_id ); } $data = array(); // found API key. It's product ID will be stored in product_id $url = 'https://wp-property-hive.com/?'; $url .= 'wc-api=wc-am-api&'; $url .= 'wc_am_action=deactivate&'; $url .= 'instance=' . $instance_id . '&'; $url .= 'object=' . wp_parse_url( get_site_url(), PHP_URL_HOST ) . '&'; $url .= 'product_id=' . $license['product_id'] . '&'; $url .= 'api_key=' . $license_key; $response = wp_remote_post( $url, array( 'body' => $data, ) ); if ( is_wp_error($response) ) { $return = array( 'success' => false, 'error' => __( 'Failed to deactivate license status', 'propertyhive' ) . ': ' . $response->get_error_message() ); return $return; } if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { $return = array( 'success' => false, 'error' => sprintf( /* translators: %s: HTTP header response code */ __( 'Received response code %s when deactivating license key status', 'propertyhive' ), wp_remote_retrieve_response_code( $response ) ) ); return $return; } $result = $response['body']; $body = json_decode($result, true); if ( json_last_error() !== JSON_ERROR_NONE ) { $return = array( 'success' => false, 'error' => __( 'Failed to decode response when deactivating license key status. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result ) ); return $return; } if ( isset($body['success']) ) { if ( $body['success'] === true ) { $return = array( 'success' => true, ); return $return; } else { $return = array( 'success' => false, 'error' => __( 'Error when deactivating license key', 'propertyhive' ) . ': ' . $body['error'] ); return $return; } } else { $return = array( 'success' => false, 'error' => __( 'Something went wrong when trying to deactivate license key', 'propertyhive' ) . ': ' . wp_json_encode( $body ) ); return $return; } } else { return $license; } } public function get_pro_license_product_id_and_package($force = false) { if ( $force !== true ) { // Not forcing. Get from transient if possible $pro_license_product_id_and_package = get_transient( 'ph_pro_license_product_id_and_package' ); if ( $pro_license_product_id_and_package !== false ) { // return transient value return $pro_license_product_id_and_package; } } $license_key = get_option( 'propertyhive_pro_license_key', '' ); if ( empty($license_key) ) { $return = array( 'success' => false, 'error' => __( 'No license key entered', 'propertyhive' ) ); set_transient( 'ph_pro_license_product_id_and_package', $return, HOUR_IN_SECONDS ); return $return; } $instance_id = get_option( 'propertyhive_pro_instance_id', '' ); if ( empty($instance_id) ) { $instance_id = wp_generate_password( 12, false ); // disable specialchars update_option('propertyhive_pro_instance_id', $instance_id ); } $data = $this->get_data_for_license_check(); $url = 'https://wp-property-hive.com/?'; $url .= 'wc-api=wc-am-api&'; $url .= 'wc_am_action=product_list&'; $url .= 'instance=' . $instance_id . '&'; $url .= 'api_key=' . $license_key; $response = wp_remote_post( $url, array( 'body' => $data, ) ); if ( is_wp_error($response) ) { $return = array( 'success' => false, 'error' => __( 'Failed to request license product list', 'propertyhive' ) . ': ' . $response->get_error_message() ); $last_known = get_option('ph_pro_last_known_license_product_id_and_package', array()); if ( !empty($last_known) ) { set_transient( 'ph_pro_license_product_id_and_package', $last_known, HOUR_IN_SECONDS ); return $last_known; } return $return; } if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { $return = array( 'success' => false, 'error' => sprintf( /* translators: %s: HTTP header response code */ __( 'Received response code %s when requesting license key product list', 'propertyhive' ), wp_remote_retrieve_response_code( $response ) ) ); $last_known = get_option('ph_pro_last_known_license_product_id_and_package', array()); if ( !empty($last_known) ) { set_transient( 'ph_pro_license_product_id_and_package', $last_known, HOUR_IN_SECONDS ); return $last_known; } return $return; } $result = $response['body']; $body = json_decode($result, true); if ( json_last_error() !== JSON_ERROR_NONE ) { $return = array( 'success' => false, 'error' => __( 'Failed to decode response when requesting license key product list. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result ) ); $last_known = get_option('ph_pro_last_known_license_product_id_and_package', array()); if ( !empty($last_known) ) { set_transient( 'ph_pro_license_product_id_and_package', $last_known, HOUR_IN_SECONDS ); return $last_known; } return $return; } if ( isset($body['success']) ) { if ( $body['success'] === true ) { if ( isset($body['data']['product_list']['wc_subs_resources']) && !empty($body['data']['product_list']['wc_subs_resources']) ) { $package = false; $product_id = ''; $is_trial = null; foreach ( $body['data']['product_list']['wc_subs_resources'] as $resource ) { if ( isset($resource['product_id']) ) { if ( in_array($resource['product_id'], array(14492, 14493, 14494)) ) { $package = 'import'; } elseif ( in_array($resource['product_id'], array(14495, 14496, 14497)) ) { $package = 'complete'; } $product_id = $resource['product_id']; $is_trial = isset( $resource['is_trial'] ) && is_bool( $resource['is_trial'] ) ? $resource['is_trial'] : null; } } if ( $package !== FALSE ) { $return = array( 'success' => true, 'package' => $package, 'product_id' => $product_id, 'is_trial' => $is_trial, ); } else { $return = array( 'success' => false, 'error' => __( 'API key exists but unable to establish the plan', 'propertyhive' ) ); } } else { $return = array( 'success' => false, 'error' => __( 'API key doesn\'t appear to belong to any orders', 'propertyhive' ) . ': ' . wp_json_encode( $body ) ); } } else { $return = array( 'success' => false, 'error' => __( 'Error when requesting license key product list', 'propertyhive' ) . ': ' . $body['error'] ); } set_transient( 'ph_pro_license_product_id_and_package', $return, HOUR_IN_SECONDS * 3 ); update_option( 'ph_pro_last_known_license_product_id_and_package', $return ); return $return; } else { $return = array( 'success' => false, 'error' => __( 'Something went wrong when requesting license key product list', 'propertyhive' ) . ': ' . wp_json_encode( $body ) ); $last_known = get_option('ph_pro_last_known_license_product_id_and_package', array()); if ( !empty($last_known) ) { set_transient( 'ph_pro_license_product_id_and_package', $last_known, HOUR_IN_SECONDS ); return $last_known; } return $return; } } public function get_current_pro_license($force = false) { if ( $force !== true ) { // Not forcing. Get from transient if possible $pro_license_status = get_transient( 'ph_pro_license_status' ); if ( $pro_license_status !== false ) { // return transient value return $pro_license_status; } } $product_id_and_package = $this->get_pro_license_product_id_and_package($force); if ( !isset($product_id_and_package['success']) || ( isset($product_id_and_package['success']) && $product_id_and_package['success'] === false ) ) { return $product_id_and_package; } $license_key = get_option( 'propertyhive_pro_license_key', '' ); if ( empty($license_key) ) { $return = array( 'success' => false, 'error' => __( 'No license key entered', 'propertyhive' ) ); set_transient( 'ph_pro_license_status', $return, HOUR_IN_SECONDS ); return $return; } $instance_id = get_option( 'propertyhive_pro_instance_id', '' ); if ( empty($instance_id) ) { $instance_id = wp_generate_password( 12, false ); // disable specialchars update_option('propertyhive_pro_instance_id', $instance_id ); } $data = $this->get_data_for_license_check(); $url = 'https://wp-property-hive.com/?'; $url .= 'wc-api=wc-am-api&'; $url .= 'wc_am_action=status&'; $url .= 'product_id=' . $product_id_and_package['product_id'] . '&'; $url .= 'instance=' . $instance_id . '&'; $url .= 'api_key=' . $license_key; $response = wp_remote_post( $url, array( 'body' => $data, ) ); if ( is_wp_error($response) ) { // error for some reason. Return last known status $previous_license_key_status = get_option( 'propertyhive_pro_license_key_status', '' ); if ( !empty($previous_license_key_status) ) { set_transient( 'ph_pro_license_status', $previous_license_key_status, HOUR_IN_SECONDS ); return $previous_license_key_status; } $return = array( 'success' => false, 'error' => __( 'Failed to request license status', 'propertyhive' ) . ': ' . $response->get_error_message() ); return $return; } if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { // error for some reason. Return last known status $previous_license_key_status = get_option( 'propertyhive_pro_license_key_status', '' ); if ( !empty($previous_license_key_status) ) { set_transient( 'ph_pro_license_status', $previous_license_key_status, HOUR_IN_SECONDS ); return $previous_license_key_status; } $return = array( 'success' => false, 'error' => sprintf( /* translators: %s: HTTP header response code */ __( 'Received response code %s when requesting license key status', 'propertyhive' ), wp_remote_retrieve_response_code( $response ) ) ); return $return; } $result = $response['body']; $body = json_decode($result, true); if ( json_last_error() !== JSON_ERROR_NONE ) { $return = array( 'success' => false, 'error' => __( 'Failed to decode response when requesting license key status. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result ) ); // error for some reason. Return last known status $previous_license_key_status = get_option( 'propertyhive_pro_license_key_status', '' ); if ( !empty($previous_license_key_status) ) { set_transient( 'ph_pro_license_status', $previous_license_key_status, HOUR_IN_SECONDS ); return $previous_license_key_status; } return $return; } if ( isset($body['success']) ) { if ( $body['success'] === true ) { if ( isset($body['status_check']) && $body['status_check'] === 'active' ) { $return = array( 'success' => true ); } else { $return = array( 'success' => false, 'error' => __( 'License key inactive', 'propertyhive' ) ); } } else { $return = array( 'success' => false, 'error' => __( 'Error when requesting license key status', 'propertyhive' ) . ': ' . $body['error'] ); } update_option( 'propertyhive_pro_license_key_last_checked', time() ); update_option( 'propertyhive_pro_license_key_status', $return ); set_transient( 'ph_pro_license_status', $return, HOUR_IN_SECONDS * 3 ); return $return; } else { $return = array( 'success' => false, 'error' => __( 'Something went wrong when requesting license key status', 'propertyhive' ) . ': ' . wp_json_encode( $body ) ); // error for some reason. Return last known status $previous_license_key_status = get_option( 'propertyhive_pro_license_key_status', '' ); if ( !empty($previous_license_key_status) ) { set_transient( 'ph_pro_license_status', $previous_license_key_status, HOUR_IN_SECONDS ); return $previous_license_key_status; } return $return; } } }