PluginProbe
Property Hive / 2.4.0
Property Hive v2.4.0
2.4.0 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 All 262 releases
← All changes | includes/class-ph-licenses.php +214 -28 2.2.42.4.0 View file →
@@ -43,9 +43,9 @@
43 43 *
44 44 * @since 1.0.0
45 45 */
46 46 public function __clone() {
47 - _doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
47 + _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
48 48 }
49 49
50 50 /**
51 51 * Unserializing instances of this class is forbidden.
@@ -52,9 +52,9 @@
52 52 *
53 53 * @since 1.0.0
54 54 */
55 55 public function __wakeup() {
56 - _doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
56 + _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'propertyhive' ), '1.0.0' );
57 57 }
58 58
59 59 /**
60 60 * Constructor for the licenses class
@@ -150,8 +150,13 @@
150 150 $product_id_and_package = PH()->license->get_pro_license_product_id_and_package();
151 151
152 152 if ( isset($product_id_and_package['success']) && $product_id_and_package['success'] === true )
153 153 {
154 + if ( ( $product_id_and_package['is_trial'] ?? null ) !== false )
155 + {
156 + return false;
157 + }
158 +
154 159 if (
155 160 isset($feature['plans']) &&
156 161 isset($product_id_and_package['package']) &&
157 162 in_array($product_id_and_package['package'], $feature['plans'])
@@ -191,8 +196,10 @@
191 196 }
192 197
193 198 private function get_data_for_license_check()
194 199 {
200 + global $wpdb;
201 +
195 202 $license_type = $this->get_license_type();
196 203
197 204 $data = array();
198 205 $data['license_key_type'] = $license_type;
@@ -209,14 +216,14 @@
209 216
210 217 $data['php_version'] = phpversion();
211 218 $data['ph_version'] = PH_VERSION;
212 219 $data['wp_version'] = get_bloginfo( 'version' );
213 - $data['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
220 + $data['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) && is_string( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '';
214 221
215 222 $data['install_date'] = get_option('propertyhive_install_timestamp', '');
216 223 if ( $data['install_date'] != '' && $data['install_date'] != 0 )
217 224 {
218 - $data['install_date'] = date("jS F Y", $data['install_date']);
225 + $data['install_date'] = gmdate("jS F Y", $data['install_date']);
219 226 }
220 227
221 228 $data['multisite'] = is_multisite();
222 229 $data['theme'] = $theme;
@@ -256,8 +263,9 @@
256 263 $data['inactive_plugins'] = $inactive_plugins_data;
257 264
258 265 $data['locale'] = get_locale();
259 266
267 + // Import info
260 268 $property_import = get_option( 'propertyhive_property_import', array() );
261 269 if ( !is_array($property_import) )
262 270 {
263 271 $property_import = array();
@@ -266,13 +274,114 @@
266 274 foreach ( $property_import as $import_id => $options )
267 275 {
268 276 if ( $options['running'] == '1' )
269 277 {
278 + if ( $options['format'] == 'xml' )
279 + {
280 + $options['format'] .= ' (' . esc_url($options['xml_url']) . ')';
281 + }
282 + elseif ( $options['format'] == 'csv' )
283 + {
284 + $options['format'] .= ' (' . esc_url($options['csv_url']) . ')';
285 + }
270 286 $formats[] = $options['format'];
271 287 }
272 288 }
273 289 $data['property_import_formats'] = $formats;
274 290
291 + // Exports
292 + $formats = array();
293 +
294 + $exports = array(
295 + 'propertyhive_realtimefeed' => array(
296 + 'label' => 'RTDF',
297 + 'field' => 'send_property_api_url',
298 + ),
299 + 'propertyhive_zooplarealtimefeed' => array(
300 + 'label' => 'Zoopla',
301 + 'field' => 'send_property_api_url',
302 + ),
303 + 'propertyhive_blmexport' => array(
304 + 'label' => 'BLM',
305 + 'field' => 'ftp_host',
306 + ),
307 + );
308 +
309 + foreach ( $exports as $option_name => $config )
310 + {
311 + $property_export = get_option( $option_name, array() );
312 +
313 + if ( ! is_array( $property_export ) ) {
314 + continue;
315 + }
316 +
317 + if ( ! isset( $property_export['portals'] ) ) {
318 + continue;
319 + }
320 +
321 + if ( ! is_array( $property_export['portals'] ) ) {
322 + continue;
323 + }
324 +
325 + $portals = isset( $property_export['portals'] ) && is_array( $property_export['portals'] )
326 + ? $property_export['portals']
327 + : array();
328 +
329 + foreach ( $portals as $portal_id => $portal )
330 + {
331 + if ( ! is_array( $portal ) ) {
332 + continue;
333 + }
334 +
335 + if ( ! is_array( $portal ) || ( $portal['mode'] ?? '' ) !== 'live' ) {
336 + continue;
337 + }
338 +
339 + $destination = $portal[ $config['field'] ] ?? '';
340 +
341 + $formats[] = sprintf(
342 + '%s (%s)',
343 + $config['label'],
344 + $destination
345 + );
346 + }
347 + }
348 +
349 + $data['property_export_formats'] = $formats;
350 +
351 + // Locrating
352 + $locrating_settings = get_option( 'propertyhive_locrating', array() );
353 + if ( !empty($locrating_settings) && isset($locrating_settings['enabled']) )
354 + {
355 + $data['locrating_enabled'] = ( $locrating_settings['enabled'] == 1 ? $locrating_settings['enabled'] : 0 );
356 + }
357 +
358 + // Search analytics
359 +
360 + // 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.
361 + $counts = $wpdb->get_row(
362 + "SELECT
363 + COALESCE(
364 + SUM(searched_at >= UTC_TIMESTAMP() - INTERVAL 7 DAY),
365 + 0
366 + ) AS last_7_days,
367 + COALESCE(
368 + SUM(searched_at >= UTC_TIMESTAMP() - INTERVAL 30 DAY),
369 + 0
370 + ) AS last_30_days,
371 + COUNT(*) AS last_90_days
372 + FROM {$wpdb->prefix}ph_search_log
373 + WHERE searched_at >= UTC_TIMESTAMP() - INTERVAL 90 DAY",
374 + ARRAY_A
375 + );
376 +
377 + if ( is_array($counts) )
378 + {
379 + $data['searches_last_7_days'] = (int) ( $counts['last_7_days'] ?? 0 );
380 + $data['searches_last_30_days'] = (int) ( $counts['last_30_days'] ?? 0 );
381 + $data['searches_last_90_days'] = (int) ( $counts['last_90_days'] ?? 0 );
382 + }
383 +
275 384 // get counts
276 385 $post_types = array('office', 'property', 'contact', 'appraisal', 'viewing', 'offer', 'sale', 'tenancy', 'key_date');
277 386 foreach ( $post_types as $post_type )
278 387 {
@@ -278,13 +387,14 @@
278 387 {
279 388 $args = array(
280 389 'post_type' => $post_type,
281 390 'fields' => 'ids',
282 - 'nopaging' => TRUE,
391 + 'posts_per_page' => 1,
283 392 'post_status' => 'publish'
284 393 );
285 394 if ( $post_type == 'property' )
286 395 {
396 + // 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.
287 397 $args['meta_query'] = array(
288 398 array(
289 399 'key' => '_on_market',
290 400 'value' => 'yes'
@@ -297,8 +407,82 @@
297 407 $data['post_type_count_' . $post_type] = $post_query->found_posts;
298 408
299 409 wp_reset_postdata();
300 410 }
411 +
412 + $departments = array_keys( ph_get_departments() );
413 + foreach ( $departments as $department )
414 + {
415 + $department_option = 'propertyhive_active_departments_' . str_replace( 'residential-', '', $department );
416 + if ( get_option( $department_option ) != 'yes' )
417 + {
418 + continue;
419 + }
420 +
421 + $args = array(
422 + 'post_type' => 'property',
423 + 'fields' => 'ids',
424 + 'posts_per_page' => 1,
425 + 'post_status' => 'publish',
426 + // 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.
427 + 'meta_query' => array(
428 + array(
429 + 'key' => '_on_market',
430 + 'value' => 'yes'
431 + ),
432 + array(
433 + 'key' => '_department',
434 + 'value' => $department
435 + )
436 + )
437 + );
438 +
439 + $post_query = new WP_Query( $args );
440 +
441 + $data['property_count_by_department_' . $department] = $post_query->found_posts;
442 +
443 + wp_reset_postdata();
444 + }
445 +
446 + // Onboarding wizard step
447 + $onboarding_state = get_option( 'propertyhive_onboarding', false );
448 +
449 + if ( is_array( $onboarding_state ) && ! empty( $onboarding_state ) )
450 + {
451 + $onboarding_status = isset( $onboarding_state['status'] )
452 + ? sanitize_key( $onboarding_state['status'] )
453 + : 'not_started';
454 +
455 + $onboarding_step = isset( $onboarding_state['last_step'] )
456 + ? sanitize_key( $onboarding_state['last_step'] )
457 + : '';
458 +
459 + // When skipped, last_step is the exact step on which Skip was clicked.
460 + // Otherwise, use the most recently viewed step so abandonment is captured.
461 + if (
462 + $onboarding_status !== 'skipped' &&
463 + ! empty( $onboarding_state['events'] ) &&
464 + is_array( $onboarding_state['events'] )
465 + )
466 + {
467 + foreach ( array_reverse( $onboarding_state['events'] ) as $event )
468 + {
469 + if (
470 + isset( $event['event'], $event['data']['step'] ) &&
471 + $event['event'] === 'step_viewed'
472 + )
473 + {
474 + $onboarding_step = sanitize_key( $event['data']['step'] );
475 + break;
476 + }
477 + }
478 + }
479 +
480 + $data['onboarding_status'] = $onboarding_status;
481 + $data['onboarding_step'] = $onboarding_step;
482 + }
483 +
484 + $data = apply_filters( 'propertyhive_license_check_data', $data );
301 485 }
302 486
303 487 return $data;
304 488 }
@@ -316,10 +500,9 @@
316 500 }
317 501
318 502 update_option( 'propertyhive_last_license_check', time() );
319 503
320 - // Start by removing what we already know about the license
321 - update_option( 'propertyhive_license_key_details', '', 'no' );
504 + // Retain last-known license details during outages; replace them only with a valid HTTPS response.
322 505 update_option( 'propertyhive_license_key_error', '', 'no' );
323 506
324 507 $data = $this->get_data_for_license_check();
325 508
@@ -325,9 +508,9 @@
325 508
326 509 $request = wp_remote_post( 'http://license.wp-property-hive.com/check-license.php', array(
327 510 'method' => 'POST',
328 511 'timeout' => 20,
329 - 'redirection' => 5,
512 + 'redirection' => 1,
330 513 'httpversion' => '1.1',
331 514 'blocking' => true,
332 515 'body' => $data,
333 516 'user-agent' => 'PH/' . PH_VERSION . '; ' . get_bloginfo( 'url' )
@@ -334,29 +517,26 @@
334 517 ) );
335 518
336 519 if ( is_wp_error( $request ) )
337 520 {
338 - update_option( 'propertyhive_license_key_details', array(), 'no' );
339 - update_option( 'propertyhive_license_key_error', $request->get_error_message(), 'no' );
521 + 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' );
340 522 return false;
341 523 }
342 524
343 - if ( isset($request['body']) && $request['body'] == '' )
525 + if ( 200 !== wp_remote_retrieve_response_code( $request ) || '' === wp_remote_retrieve_body( $request ) )
344 526 {
345 - update_option( 'propertyhive_license_key_details', array(), 'no' );
346 - update_option( 'propertyhive_license_key_error', 'No response received when checking license', 'no' );
527 + update_option( 'propertyhive_license_key_error', __( 'The license service returned an invalid response.', 'propertyhive' ) . ' ' . wp_remote_retrieve_response_code( $request ), 'no' );
347 528 return false;
348 529 }
349 530
350 531 $body = @unserialize($request['body'], ['allowed_classes' => false]);
351 - if ( $body !== FALSE && is_array($body) && !empty($body) )
532 + if ( $body !== FALSE && is_array($body) )
352 533 {
353 534 update_option( 'propertyhive_license_key_details', $body, 'no' );
354 535 }
355 536 else
356 537 {
357 - update_option( 'propertyhive_license_key_details', array(), 'no' );
358 - update_option( 'propertyhive_license_key_error', 'Failed to process response data: ' . print_r($request['body'], true), 'no' );
538 + update_option( 'propertyhive_license_key_error', __( 'The license service returned invalid license data.', 'propertyhive' ) . ' ' . print_r($body, true), 'no' );
359 539 }
360 540 }
361 541
362 542 public function get_current_license()
@@ -401,9 +581,9 @@
401 581 $url = 'https://wp-property-hive.com/?';
402 582 $url .= 'wc-api=wc-am-api&';
403 583 $url .= 'wc_am_action=activate&';
404 584 $url .= 'instance=' . $instance_id . '&';
405 - $url .= 'object=' . parse_url( get_site_url(), PHP_URL_HOST ) . '&';
585 + $url .= 'object=' . wp_parse_url( get_site_url(), PHP_URL_HOST ) . '&';
406 586 $url .= 'product_id=' . $license['product_id'] . '&';
407 587 $url .= 'api_key=' . $license_key;
408 588
409 589 $response = wp_remote_post( $url, array(
@@ -439,9 +619,9 @@
439 619 if ( json_last_error() !== JSON_ERROR_NONE )
440 620 {
441 621 $return = array(
442 622 'success' => false,
443 - 'error' => __( 'Failed to decode response when activating license key status. Please try again', 'propertyhive' ) . ': ' . print_r( $result, true )
623 + 'error' => __( 'Failed to decode response when activating license key status. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result )
444 624 );
445 625 return $return;
446 626 }
447 627
@@ -466,9 +646,9 @@
466 646 else
467 647 {
468 648 $return = array(
469 649 'success' => false,
470 - 'error' => __( 'Something went wrong when trying to activate license key', 'propertyhive' ) . ': ' . print_r($body, true)
650 + 'error' => __( 'Something went wrong when trying to activate license key', 'propertyhive' ) . ': ' . wp_json_encode( $body )
471 651 );
472 652 return $return;
473 653 }
474 654 }
@@ -500,9 +680,9 @@
500 680 $url = 'https://wp-property-hive.com/?';
501 681 $url .= 'wc-api=wc-am-api&';
502 682 $url .= 'wc_am_action=deactivate&';
503 683 $url .= 'instance=' . $instance_id . '&';
504 - $url .= 'object=' . parse_url( get_site_url(), PHP_URL_HOST ) . '&';
684 + $url .= 'object=' . wp_parse_url( get_site_url(), PHP_URL_HOST ) . '&';
505 685 $url .= 'product_id=' . $license['product_id'] . '&';
506 686 $url .= 'api_key=' . $license_key;
507 687
508 688 $response = wp_remote_post( $url, array(
@@ -538,9 +718,9 @@
538 718 if ( json_last_error() !== JSON_ERROR_NONE )
539 719 {
540 720 $return = array(
541 721 'success' => false,
542 - 'error' => __( 'Failed to decode response when deactivating license key status. Please try again', 'propertyhive' ) . ': ' . print_r( $result, true )
722 + 'error' => __( 'Failed to decode response when deactivating license key status. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result )
543 723 );
544 724 return $return;
545 725 }
546 726
@@ -565,9 +745,9 @@
565 745 else
566 746 {
567 747 $return = array(
568 748 'success' => false,
569 - 'error' => __( 'Something went wrong when trying to deactivate license key', 'propertyhive' ) . ': ' . print_r($body, true)
749 + 'error' => __( 'Something went wrong when trying to deactivate license key', 'propertyhive' ) . ': ' . wp_json_encode( $body )
570 750 );
571 751 return $return;
572 752 }
573 753 }
@@ -668,9 +848,9 @@
668 848 if ( json_last_error() !== JSON_ERROR_NONE )
669 849 {
670 850 $return = array(
671 851 'success' => false,
672 - 'error' => __( 'Failed to decode response when requesting license key product list. Please try again', 'propertyhive' ) . ': ' . print_r( $result, true )
852 + 'error' => __( 'Failed to decode response when requesting license key product list. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result )
673 853 );
674 854
675 855 $last_known = get_option('ph_pro_last_known_license_product_id_and_package', array());
676 856 if ( !empty($last_known) )
@@ -689,8 +869,9 @@
689 869 if ( isset($body['data']['product_list']['wc_subs_resources']) && !empty($body['data']['product_list']['wc_subs_resources']) )
690 870 {
691 871 $package = false;
692 872 $product_id = '';
873 + $is_trial = null;
693 874
694 875 foreach ( $body['data']['product_list']['wc_subs_resources'] as $resource )
695 876 {
696 877 if ( isset($resource['product_id']) )
@@ -703,8 +884,12 @@
703 884 {
704 885 $package = 'complete';
705 886 }
706 887 $product_id = $resource['product_id'];
888 + $is_trial = isset( $resource['is_trial'] )
889 + && is_bool( $resource['is_trial'] )
890 + ? $resource['is_trial']
891 + : null;
707 892 }
708 893 }
709 894
710 895 if ( $package !== FALSE )
@@ -711,9 +896,10 @@
711 896 {
712 897 $return = array(
713 898 'success' => true,
714 899 'package' => $package,
715 - 'product_id' => $product_id
900 + 'product_id' => $product_id,
901 + 'is_trial' => $is_trial,
716 902 );
717 903 }
718 904 else
719 905 {
@@ -726,9 +912,9 @@
726 912 else
727 913 {
728 914 $return = array(
729 915 'success' => false,
730 - 'error' => __( 'API key doesn\'t appear to belong to any orders', 'propertyhive' ) . ': ' . print_r($body, true)
916 + 'error' => __( 'API key doesn\'t appear to belong to any orders', 'propertyhive' ) . ': ' . wp_json_encode( $body )
731 917 );
732 918 }
733 919 }
734 920 else
@@ -746,9 +932,9 @@
746 932 else
747 933 {
748 934 $return = array(
749 935 'success' => false,
750 - 'error' => __( 'Something went wrong when requesting license key product list', 'propertyhive' ) . ': ' . print_r($body, true)
936 + 'error' => __( 'Something went wrong when requesting license key product list', 'propertyhive' ) . ': ' . wp_json_encode( $body )
751 937 );
752 938
753 939 $last_known = get_option('ph_pro_last_known_license_product_id_and_package', array());
754 940 if ( !empty($last_known) )
@@ -861,9 +1047,9 @@
861 1047 if ( json_last_error() !== JSON_ERROR_NONE )
862 1048 {
863 1049 $return = array(
864 1050 'success' => false,
865 - 'error' => __( 'Failed to decode response when requesting license key status. Please try again', 'propertyhive' ) . ': ' . print_r( $result, true )
1051 + 'error' => __( 'Failed to decode response when requesting license key status. Please try again', 'propertyhive' ) . ': ' . wp_json_encode( $result )
866 1052 );
867 1053
868 1054 // error for some reason. Return last known status
869 1055 $previous_license_key_status = get_option( 'propertyhive_pro_license_key_status', '' );
@@ -911,9 +1097,9 @@
911 1097 else
912 1098 {
913 1099 $return = array(
914 1100 'success' => false,
915 - 'error' => __( 'Something went wrong when requesting license key status', 'propertyhive' ) . ': ' . print_r($body, true)
1101 + 'error' => __( 'Something went wrong when requesting license key status', 'propertyhive' ) . ': ' . wp_json_encode( $body )
916 1102 );
917 1103
918 1104 // error for some reason. Return last known status
919 1105 $previous_license_key_status = get_option( 'propertyhive_pro_license_key_status', '' );