PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
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 1.4.62 All 260 releases
← All changes | includes/class-ph-rest-api.php +125 -48 2.2.62.3.0 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
@@ -97,9 +127,9 @@
97 127 }
98 128
99 129 public function block_enquiry_rest_listing($response, $server, $request)
100 130 {
101 - if ( $request->get_route() === '/wp/v2/enquiry' )
131 + if ( preg_match( '#^/wp/v2/enquiry(?:/|$)#', $request->get_route() ) )
102 132 {
103 133 $current_user = wp_get_current_user();
104 134
105 135 if ( !current_user_can('manage_propertyhive') )
@@ -105,9 +135,9 @@
105 135 if ( !current_user_can('manage_propertyhive') )
106 136 {
107 137 return new WP_Error(
108 138 'rest_forbidden',
109 - __('You are not allowed to list enquiries.', 'propertyhive'),
139 + __( 'You are not allowed to access enquiries.', 'propertyhive' ),
110 140 ['status' => 403]
111 141 );
112 142 }
113 143
@@ -246,22 +276,14 @@
246 276 ),
247 277 ));
248 278 }
249 279
250 - public function enquiry_permission_check()
251 - {
252 - // Check if the user is authenticated
253 - if (is_user_logged_in() || apply_filters('rest_authentication_errors', null) === null) {
254 - // Check if the user has the capability to create enquiries (e.g., 'edit_posts')
255 - if (current_user_can('edit_posts')) {
256 - return true;
257 - } else {
258 - return new WP_Error('rest_forbidden', 'You do not have permissions to create enquiries.', array('status' => 403));
259 - }
260 - } else {
261 - return new WP_Error('rest_forbidden', 'You are not authenticated.', array('status' => 403));
262 - }
263 - }
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 + }
264 286
265 287 public function handle_enquiry_post(WP_REST_Request $request)
266 288 {
267 289 // Handle the creation of the enquiry post
@@ -534,11 +556,13 @@
534 556
535 557 $PH_Query = new PH_Query();
536 558
537 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.
538 561 $args['meta_query'] = $PH_Query->get_meta_query();
539 562
540 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.
541 565 $args['tax_query'] = $PH_Query->get_tax_query();
542 566
543 567 // Date query
544 568 $args['date_query'] = $PH_Query->get_date_query();
@@ -546,8 +570,9 @@
546 570 $ordering = $PH_Query->get_search_results_ordering_args();
547 571 $args['orderby'] = $ordering['orderby'] . ' post_title';
548 572 $args['order'] = $ordering['order'];
549 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.
550 575 $args['meta_key'] = $ordering['meta_key'];
551 576
552 577 $args = apply_filters( 'propertyhive_rest_api_query_args', $args );
553 578
@@ -613,8 +638,10 @@
613 638 'brochures',
614 639 'epcs',
615 640 'virtual_tours',
616 641 'views_total',
642 + 'views_today',
643 + 'views_yesterday',
617 644 'views_last_7_days',
618 645 'views_last_14_days',
619 646 'views_last_30_days',
620 647 );
@@ -851,8 +878,10 @@
851 878 {
852 879 $return = $property->get_virtual_tours();
853 880 break;
854 881 }
882 + case "views_today":
883 + case "views_yesterday":
855 884 case "views_last_7_days":
856 885 case "views_last_14_days":
857 886 case "views_last_30_days":
858 887 case "views_total":
@@ -857,35 +886,68 @@
857 886 case "views_last_30_days":
858 887 case "views_total":
859 888 {
860 889 $view_statistics = $property->_view_statistics;
861 - if ( !is_array($view_statistics) )
862 - {
863 - $view_statistics = array();
864 - }
890 + if ( !is_array($view_statistics) )
891 + {
892 + $view_statistics = array();
893 + }
865 894
866 - $views = 0;
895 + $views = 0;
867 896
868 - $date_from = '2001-01-01';
869 - switch ($field_name)
870 - {
871 - case "views_last_7_days": { $date_from = date("Y-m-d", strtotime('7 days ago')); break; }
872 - case "views_last_14_days": { $date_from = date("Y-m-d", strtotime('14 days ago')); break; }
873 - case "views_last_30_days": { $date_from = date("Y-m-d", strtotime('30 days ago')); break; }
874 - }
875 - $date_from = strtotime($date_from);
897 + $today = current_datetime()->setTime(0, 0, 0);
876 898
877 - $date_to = date("Y-m-d");
878 - $date_to = strtotime($date_to);
899 + $date_from = new DateTimeImmutable('2001-01-01', wp_timezone());
900 + $date_to = $today;
879 901
880 - for ($i = $date_from; $i <= $date_to; $i += 86400)
881 - {
882 - if ( isset($view_statistics[date("Y-m-d", $i)]) )
883 - {
884 - $views += $view_statistics[date("Y-m-d", $i)];
885 - }
886 - }
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 + }
887 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 +
888 950 $return = $views;
889 951 break;
890 952 }
891 953 default:
@@ -952,8 +1014,12 @@
952 1014 break;
953 1015 }
954 1016 case "description":
955 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 );
956 1022 $property = new PH_Property($object->ID);
957 1023
958 1024 if ( isset($property->_department) && $property->_department == 'commercial' )
959 1025 {
@@ -958,9 +1024,9 @@
958 1024 if ( isset($property->_department) && $property->_department == 'commercial' )
959 1025 {
960 1026 update_post_meta( $object->ID, '_descriptions', '1' );
961 1027 update_post_meta( $object->ID, '_description_name_0', '' );
962 - update_post_meta( $object->ID, '_description_0', $value );
1028 + update_post_meta( $object->ID, '_description_0', wp_slash( $value ) );
963 1029 }
964 1030 else
965 1031 {
966 1032 update_post_meta( $object->ID, '_rooms', '1' );
@@ -965,9 +1031,9 @@
965 1031 {
966 1032 update_post_meta( $object->ID, '_rooms', '1' );
967 1033 update_post_meta( $object->ID, '_room_name_0', '' );
968 1034 update_post_meta( $object->ID, '_room_dimensions_0', '' );
969 - update_post_meta( $object->ID, '_room_description_0', $value );
1035 + update_post_meta( $object->ID, '_room_description_0', wp_slash( $value ) );
970 1036 }
971 1037 break;
972 1038 }
973 1039 case "images":
@@ -974,8 +1040,20 @@
974 1040 case "floorplans":
975 1041 case "brochures":
976 1042 case "epcs":
977 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 +
978 1056 if ( !function_exists('media_handle_upload') ) {
979 1057 require_once(ABSPATH . "wp-admin" . '/includes/image.php');
980 1058 require_once(ABSPATH . "wp-admin" . '/includes/file.php');
981 1059 require_once(ABSPATH . "wp-admin" . '/includes/media.php');
@@ -1049,9 +1127,9 @@
1049 1127 $media_ids[] = $imported_previously_id;
1050 1128 }
1051 1129 else
1052 1130 {
1053 - $tmp = download_url( $url );
1131 + $tmp = self::download_media( $url );
1054 1132 $file_array = array(
1055 1133 'name' => basename( $url ),
1056 1134 'tmp_name' => $tmp
1057 1135 );
@@ -1058,9 +1136,9 @@
1058 1136
1059 1137 // Check for download errors
1060 1138 if ( is_wp_error( $tmp ) )
1061 1139 {
1062 - // ERROR: $tmp->get_error_message();
1140 + return $tmp;
1063 1141 }
1064 1142 else
1065 1143 {
1066 1144 $id = media_handle_sideload( $file_array, $object->ID, $description, array('post_title' => $filename) );
@@ -1067,11 +1145,11 @@
1067 1145
1068 1146 // Check for handle sideload errors.
1069 1147 if ( is_wp_error( $id ) )
1070 1148 {
1071 - @unlink( $file_array['tmp_name'] );
1149 + wp_delete_file( $file_array['tmp_name'] );
1072 1150
1073 - // ERROR: $id->get_error_message();
1151 + return $id;
1074 1152 }
1075 1153 else
1076 1154 {
1077 1155 $media_ids[] = $id;
@@ -1088,9 +1166,9 @@
1088 1166 if ( is_array($previous_media_ids) && !empty($previous_media_ids) )
1089 1167 {
1090 1168 foreach ( $previous_media_ids as $previous_media_id )
1091 1169 {
1092 - if ( !in_array($previous_media_id, $media_ids) )
1170 + if ( ! in_array( $previous_media_id, $media_ids ) && current_user_can( 'delete_post', $previous_media_id ) )
1093 1171 {
1094 1172 if ( wp_delete_attachment( $previous_media_id, TRUE ) !== FALSE )
1095 1173 {
1096 1174
@@ -1230,5 +1308,4 @@
1230 1308 }
1231 1309 }
1232 1310
1233 1311 }
1234 -