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/admin/class-ph-admin-matching-applicants.php +319 -80 1.4.46 → 2.4.0 View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +// phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 +// ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4 +
2 5 /**
3 6 * PropertyHive Admin Matching Applicants Class.
4 7 *
5 8 * @author PropertyHive
@@ -14,27 +17,40 @@
14 17
15 18 /**
16 19 * PH_Admin_Matching_Applicants
17 20 */
21 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Admin_Matching_Applicants; preserving the existing PH_* class name is required for plugin and extension compatibility.
18 22 class PH_Admin_Matching_Applicants {
19 23
20 24 public function output()
21 25 {
22 - if ( !isset($_GET['property_id']) || (isset($_GET['property_id']) && get_post_type((int)$_GET['property_id']) != 'property') )
23 - {
24 - die('Invalid property_id passed');
25 - }
26 + // The initial matching screen is read-only. The POST branch below verifies the
27 + // matching nonce before it performs any state-changing action.
28 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This request is used to render the read-only matching screen; POST mutations verify the matching nonce below.
29 + $request_get = wp_unslash( $_GET );
30 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Only the presence of the action selector is checked here; request values are normalized after the nonce check below.
31 + $has_step = isset( $_POST['step'] );
26 32
27 - $property_id = (int)$_GET['property_id'];
33 + $property_id = ( isset( $request_get['property_id'] ) && is_scalar( $request_get['property_id'] ) ) ? absint( $request_get['property_id'] ) : 0;
28 34
29 - $property = new PH_Property($property_id);
35 + if ( ! $property_id || get_post_type( $property_id ) !== 'property' )
36 + {
37 + die('Invalid property_id passed');
38 + }
30 39
31 - if ( isset($_POST['step']) )
40 + $property = new PH_Property($property_id);
41 +
42 + if ( $has_step )
32 43 {
33 - if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'propertyhive-matching-applicants' ) )
34 - die( __( 'Action failed. Please refresh the page and retry.', 'propertyhive' ) );
44 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Only the nonce value is read before verification; all other POST values are normalized after the check below.
45 + $request_request = wp_unslash( $_REQUEST );
46 + if ( empty( $request_request['_wpnonce'] ) || ! wp_verify_nonce( ( isset( $request_request['_wpnonce'] ) && is_string( $request_request['_wpnonce'] ) ) ? sanitize_text_field( $request_request['_wpnonce'] ) : '', 'propertyhive-matching-applicants' ) )
47 + die( esc_html(__( 'Action failed. Please refresh the page and retry.', 'propertyhive' )) );
35 48
36 - switch ( $_POST['step'] )
49 + $request_post = wp_unslash( $_POST );
50 + $step = is_string( $request_post['step'] ) ? sanitize_key( $request_post['step'] ) : '';
51 +
52 + switch ( $step )
37 53 {
38 54 case "one":
39 55 {
40 56 // Properties have been selected to email or dismiss
@@ -44,14 +60,25 @@
44 60
45 61 $nothing_to_send = true;
46 62
47 63 // Handle properties to email
48 - if ( isset($_POST['email_contact_applicant_profile_id']) && !empty($_POST['email_contact_applicant_profile_id']) )
64 + if ( isset( $request_post['email_contact_applicant_profile_id'] ) && ! empty( $request_post['email_contact_applicant_profile_id'] ) )
49 65 {
50 66 $nothing_to_send = false;
51 67
52 68 $subject = get_option( 'propertyhive_property_match_default_email_subject', '' );
53 69 $body = get_option( 'propertyhive_property_match_default_email_body', '' );
70 +
71 + $from_email_option = get_option( 'propertyhive_property_match_default_from', '' );
72 + if( $from_email_option == 'default_from_email' )
73 + {
74 + $from_email_address = get_option('propertyhive_email_from_address', '');
75 + }
76 + else
77 + {
78 + $current_user = wp_get_current_user();
79 + $from_email_address = $current_user->user_email;
80 + }
54 81 }
55 82
56 83 $nothing_to_send = apply_filters( 'propertyhive_applicant_match_nothing_to_send', $nothing_to_send );
57 84
@@ -63,9 +90,9 @@
63 90 <div id="poststuff">
64 91
65 92 <form method="post" id="mainform" action="" enctype="multipart/form-data">
66 93 <?php
67 - if ( isset($_POST['email_contact_applicant_profile_id']) && !empty($_POST['email_contact_applicant_profile_id']) )
94 + if ( isset( $request_post['email_contact_applicant_profile_id'] ) && ! empty( $request_post['email_contact_applicant_profile_id'] ) )
68 95 {
69 96 // We've got emails to send
70 97 include 'views/html-admin-matching-applicants-email.php';
71 98 }
@@ -73,15 +100,28 @@
73 100 do_action( 'propertyhive_applicant_match_step_two', $property_id );
74 101 ?>
75 102 <p class="submit">
76 103
77 - <input name="save" class="button-primary" type="submit" value="<?php echo __( 'Send Matches', 'propertyhive' ); ?>" />
78 - <?php if ( isset($_POST['email_contact_applicant_profile_id']) && !empty($_POST['email_contact_applicant_profile_id']) ) { ?>
79 - <input name="preview" id="preview_email" class="button" type="button" value="<?php echo __( 'Preview Email', 'propertyhive' ); ?>" />
104 + <input name="save" class="button-primary" type="submit" value="<?php echo esc_attr(__( 'Send Matches', 'propertyhive' )); ?>" />
105 + <?php if ( isset( $request_post['email_contact_applicant_profile_id'] ) && ! empty( $request_post['email_contact_applicant_profile_id'] ) ) { ?>
106 + <input name="preview" id="preview_email" class="button" type="button" value="<?php echo esc_attr(__( 'Preview Email', 'propertyhive' )); ?>" />
80 107 <?php } ?>
81 108
82 109 <input type="hidden" name="step" value="two" />
83 - <input type="hidden" name="email_contact_applicant_profile_id" value="<?php echo ( isset($_POST['email_contact_applicant_profile_id']) && is_array($_POST['email_contact_applicant_profile_id']) && !empty($_POST['email_contact_applicant_profile_id']) ) ? implode(",", ph_clean($_POST['email_contact_applicant_profile_id'])) : ''; ?>" />
110 + <input type="hidden" name="email_contact_applicant_profile_id" value="<?php
111 + $selected_contact_applicant_profile_ids = array();
112 + if ( isset( $request_post['email_contact_applicant_profile_id'] ) && is_array( $request_post['email_contact_applicant_profile_id'] ) ) {
113 + foreach ( $request_post['email_contact_applicant_profile_id'] as $selected_contact_applicant_profile_id ) {
114 + if ( is_string( $selected_contact_applicant_profile_id ) ) {
115 + $parts = explode( '|', $selected_contact_applicant_profile_id );
116 + if ( count( $parts ) >= 2 ) {
117 + $selected_contact_applicant_profile_ids[] = absint( $parts[0] ) . '|' . absint( $parts[1] );
118 + }
119 + }
120 + }
121 + }
122 + echo esc_attr( implode( ',', $selected_contact_applicant_profile_ids ) );
123 + ?>" />
84 124 <?php do_action( 'propertyhive_applicant_match_step_two_hidden_fields' ); ?>
85 125 <?php wp_nonce_field( 'propertyhive-matching-applicants' ); ?>
86 126
87 127 </p>
@@ -86,9 +126,20 @@
86 126
87 127 </p>
88 128
89 129 <p>
90 - <?php echo __( 'When sending out lots of emails we recommend using <a href="https://en-gb.wordpress.org/plugins/tags/smtp" target="_blank">a plugin</a> to send them out using SMTP. Your web developer or hosting company should be able to advise on this.', 'propertyhive' );
130 + <?php
131 + echo wp_kses_post(
132 + sprintf(
133 + /* translators: 1: Opening link tag to the WordPress.org SMTP plugins page, 2: Closing link tag. */
134 + __(
135 + 'When sending out lots of emails we recommend using %1$sa plugin%2$s to send them out using SMTP. Your web developer or hosting company should be able to advise on this.',
136 + 'propertyhive'
137 + ),
138 + '<a href="https://en-gb.wordpress.org/plugins/tags/smtp" target="_blank" rel="noopener noreferrer">',
139 + '</a>'
140 + )
141 + );
91 142 ?>
92 143 </p>
93 144
94 145 </form>
@@ -111,9 +162,9 @@
111 162
112 163 function showPreview()
113 164 {
114 165 jQuery('#mainform').attr('target', '_blank');
115 - jQuery('#mainform').attr('action', '<?php echo admin_url( '?preview_propertyhive_email=true&property_id=' . (int)$_GET['property_id']); ?>');
166 + jQuery('#mainform').attr('action', <?php echo wp_json_encode( admin_url( '?preview_propertyhive_email=true&property_id=' . $property_id ), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); ?>);
116 167
117 168 jQuery('#mainform').submit();
118 169 jQuery('#mainform').attr('target', '_self');
119 170 jQuery('#mainform').attr('action', '');
@@ -124,9 +175,9 @@
124 175 }
125 176
126 177 if ( $nothing_to_send == true )
127 178 {
128 - echo '<script>window.location.href = "' . get_edit_post_link( $property_id, 'url' ) . '&ph_message=2";</script>';
179 + echo '<script>window.location.href = ' . wp_json_encode( get_edit_post_link( $property_id, 'url' ) . '&ph_message=2', JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . ';</script>';
129 180
130 181 //header("Location: " . get_edit_post_link( $contact_id, 'url' ) . '&ph_message=2' ); // properties marked as not interested
131 182 //die();
132 183 }
@@ -134,17 +185,24 @@
134 185 break;
135 186 }
136 187 case "two":
137 188 {
138 - if ( isset($_POST['email_contact_applicant_profile_id']) && !empty($_POST['email_contact_applicant_profile_id']) )
139 - {
140 - $email_contact_applicant_profile_id = explode(",", $_POST['email_contact_applicant_profile_id']);
189 + if ( isset( $request_post['email_contact_applicant_profile_id'] ) && ! empty( $request_post['email_contact_applicant_profile_id'] ) )
190 + {
191 + $email_contact_applicant_profile_id_input = ( isset( $request_post['email_contact_applicant_profile_id'] ) && is_string( $request_post['email_contact_applicant_profile_id'] ) ) ? $request_post['email_contact_applicant_profile_id'] : '';
192 + $email_contact_applicant_profile_id = explode( ',', sanitize_text_field( $email_contact_applicant_profile_id_input ) );
141 193
142 - foreach ( $email_contact_applicant_profile_id as $contact_applicant_profile_id )
143 - {
144 - $explode_contact_applicant_profile_id = explode("|", $contact_applicant_profile_id);
145 - $contact_id = $explode_contact_applicant_profile_id[0];
146 - $applicant_profile_id = $explode_contact_applicant_profile_id[1];
194 + foreach ( $email_contact_applicant_profile_id as $contact_applicant_profile_id )
195 + {
196 + $explode_contact_applicant_profile_id = explode("|", $contact_applicant_profile_id);
197 + if ( count( $explode_contact_applicant_profile_id ) < 2 ) {
198 + continue;
199 + }
200 + $contact_id = absint( $explode_contact_applicant_profile_id[0] );
201 + $applicant_profile_id = absint( $explode_contact_applicant_profile_id[1] );
202 + if ( ! $contact_id ) {
203 + continue;
204 + }
147 205
148 206 $email_address = get_post_meta( (int)$contact_id, '_email_address', TRUE );
149 207
150 208 $to_email_addresses = explode(",", $email_address);
@@ -153,18 +211,62 @@
153 211 {
154 212 $new_to_email_addresses[] = sanitize_email($to_email_address);
155 213 }
156 214
215 + $cc_email_address_input = ( isset( $request_post['cc_email_address'] ) && is_string( $request_post['cc_email_address'] ) ) ? $request_post['cc_email_address'] : '';
216 + $cc_email_addresses = explode( ',', $cc_email_address_input );
217 + $new_cc_email_addresses = array();
218 + foreach ( $cc_email_addresses as $cc_email_address )
219 + {
220 + $new_cc_email_addresses[] = sanitize_email($cc_email_address);
221 + }
222 +
223 + $bcc_email_address_input = ( isset( $request_post['bcc_email_address'] ) && is_string( $request_post['bcc_email_address'] ) ) ? $request_post['bcc_email_address'] : '';
224 + $bcc_email_addresses = explode( ',', $bcc_email_address_input );
225 + $new_bcc_email_addresses = array();
226 + foreach ( $bcc_email_addresses as $bcc_email_address )
227 + {
228 + $new_bcc_email_addresses[] = sanitize_email($bcc_email_address);
229 + }
230 +
231 + $allowed_tags = array(
232 + 'strong' => array(),
233 + 'span' => array(),
234 + 'em' => array(),
235 + 'h1' => array(),
236 + 'h2' => array(),
237 + 'h3' => array(),
238 + 'h4' => array(),
239 + 'h5' => array(),
240 + 'h6' => array(),
241 + 'i' => array(),
242 + 'u' => array(),
243 + 'b' => array(),
244 + 'a' => array(
245 + 'href' => array(),
246 + 'target' => array(),
247 + ),
248 + );
249 + $allowed_tags = apply_filters( 'propertyhive_match_email_allowed_tags', $allowed_tags );
250 +
251 + $body_input = ( isset( $request_post['body'] ) && is_string( $request_post['body'] ) ) ? $request_post['body'] : '';
252 + $body = wp_kses( $body_input, $allowed_tags );
253 + $from_name_input = ( isset( $request_post['from_name'] ) && is_string( $request_post['from_name'] ) ) ? $request_post['from_name'] : '';
254 + $from_email_address_input = ( isset( $request_post['from_email_address'] ) && is_string( $request_post['from_email_address'] ) ) ? $request_post['from_email_address'] : '';
255 + $subject_input = ( isset( $request_post['subject'] ) && is_string( $request_post['subject'] ) ) ? $request_post['subject'] : '';
256 +
157 257 // Email info entered. Time to send emails
158 258 $this->send_emails(
159 259 (int)$contact_id,
160 260 (int)$applicant_profile_id,
161 261 array($property_id),
162 - ph_clean($_POST['from_name']),
163 - sanitize_email($_POST['from_email_address']),
164 - ph_clean($_POST['subject']),
165 - sanitize_textarea_field($_POST['body']),
166 - implode(",", $new_to_email_addresses)
262 + ph_clean( $from_name_input ),
263 + sanitize_email( $from_email_address_input ),
264 + ph_clean( $subject_input ),
265 + $body,
266 + implode(",", $new_to_email_addresses),
267 + implode(",", $new_cc_email_addresses),
268 + implode(",", $new_bcc_email_addresses)
167 269 );
168 270 }
169 271
170 272 //header("Location: " . get_edit_post_link( $contact_id, 'url' ) . '&ph_message=1' ); // email sent
@@ -172,15 +274,15 @@
172 274 }
173 275
174 276 do_action( 'propertyhive_applicant_match_step_send', $property_id );
175 277
176 - echo '<script>window.location.href = "' . get_edit_post_link( $property_id, 'url' ) . '&ph_message=1";</script>';
278 + echo '<script>window.location.href = ' . wp_json_encode( get_edit_post_link( $property_id, 'url' ) . '&ph_message=1', JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . ';</script>';
177 279 }
178 280 }
179 281 }
180 282 else
181 283 {
182 - $applicants = $this->get_matching_applicants( (int)$_GET['property_id'] );
284 + $applicants = $this->get_matching_applicants( $property_id );
183 285
184 286 $on_market_change_date = $property->_on_market_change_date;
185 287 $price_change_date = $property->_price_change_date;
186 288
@@ -187,20 +289,34 @@
187 289 include 'views/html-admin-matching-applicants.php';
188 290 }
189 291 }
190 292
191 - private function dismiss_properties()
192 - {
193 - $property_id = (int)$_GET['property_id'];
293 + private function dismiss_properties()
294 + {
295 + // output() verifies the matching nonce before calling this private mutator.
296 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- This private helper is only called from output() after the matching nonce has been verified.
297 + $request_post = wp_unslash( $_POST );
298 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This private helper receives the read-only property identifier from the already-authorized matching screen.
299 + $request_get = wp_unslash( $_GET );
300 + $property_id = ( isset( $request_get['property_id'] ) && is_scalar( $request_get['property_id'] ) ) ? absint( $request_get['property_id'] ) : 0;
194 301
195 - if ( isset($_POST['not_interested_contact_applicant_profile_id']) && !empty($_POST['not_interested_contact_applicant_profile_id']) )
196 - {
197 - foreach ( $_POST['not_interested_contact_applicant_profile_id'] as $contact_applicant_profile_id )
302 + if ( isset( $request_post['not_interested_contact_applicant_profile_id'] ) && is_array( $request_post['not_interested_contact_applicant_profile_id'] ) && ! empty( $request_post['not_interested_contact_applicant_profile_id'] ) )
198 303 {
199 - $explode_contact_applicant_profile_id = explode("|", $contact_applicant_profile_id);
304 + foreach ( $request_post['not_interested_contact_applicant_profile_id'] as $contact_applicant_profile_id )
305 + {
306 + if ( ! is_string( $contact_applicant_profile_id ) ) {
307 + continue;
308 + }
309 + $explode_contact_applicant_profile_id = explode("|", $contact_applicant_profile_id);
310 + if ( count( $explode_contact_applicant_profile_id ) < 2 ) {
311 + continue;
312 + }
200 313
201 - $contact_id = $explode_contact_applicant_profile_id[0];
202 - $applicant_profile_id = $explode_contact_applicant_profile_id[1];
314 + $contact_id = absint( $explode_contact_applicant_profile_id[0] );
315 + $applicant_profile_id = absint( $explode_contact_applicant_profile_id[1] );
316 + if ( ! $contact_id ) {
317 + continue;
318 + }
203 319
204 320 // Get currently dismissed properties for this contact to decide if we need to add or remove it
205 321 $dismissed_properties = get_post_meta( $contact_id, '_dismissed_properties', TRUE );
206 322
@@ -233,8 +349,9 @@
233 349 public function get_matching_applicants( $property_id )
234 350 {
235 351 global $post;
236 352
353 + $hot_applicants = array();
237 354 $applicants = array();
238 355
239 356 $property = new PH_Property((int)$property_id);
240 357
@@ -240,20 +357,53 @@
240 357
241 358 if ( $property !== FALSE )
242 359 {
243 360 $property_types = array();
244 - $prefix = $property->department == 'commercial' ? 'commercial_' : '';
245 - $term_list = wp_get_post_terms($property_id, $prefix . 'property_type', array("fields" => "ids"));
361 + $prefix = $property->department == 'commercial' || ph_get_custom_department_based_on($property->department) == 'commercial' ? 'commercial_' : '';
362 + $term_list = wp_get_post_terms($property_id, $prefix . 'property_type', array("fields" => "all"));
246 363 if ( !is_wp_error($term_list) && is_array($term_list) && !empty($term_list) )
247 364 {
248 - $property_types = $term_list;
365 + foreach ( $term_list as $term )
366 + {
367 + $property_types[] = $term->term_id;
368 +
369 + if ( $term->parent != 0 )
370 + {
371 + $parent = get_term_by( 'id', $term->parent , $prefix . 'property_type' );
372 + $property_types[] = $parent->term_id;
373 +
374 + if ( $parent->parent != 0 )
375 + {
376 + $parent = get_term_by( 'id', $parent->parent , $prefix . 'property_type' );
377 + $property_types[] = $parent->term_id;
378 + }
379 + }
380 + }
249 381 }
250 382
251 - $locations = array();
252 - $term_list = wp_get_post_terms($property_id, 'location', array("fields" => "ids"));
253 - if ( !is_wp_error($term_list) && is_array($term_list) && !empty($term_list) )
383 + if ( get_option('propertyhive_applicant_locations_type') != 'text' )
254 384 {
255 - $locations = $term_list;
385 + $locations = array();
386 + $term_list = wp_get_post_terms($property_id, 'location', array("fields" => "all"));
387 + if ( !is_wp_error($term_list) && is_array($term_list) && !empty($term_list) )
388 + {
389 + foreach ( $term_list as $term )
390 + {
391 + $locations[] = $term->term_id;
392 +
393 + if ( $term->parent != 0 )
394 + {
395 + $parent = get_term_by( 'id', $term->parent , 'location' );
396 + $locations[] = $parent->term_id;
397 +
398 + if ( $parent->parent != 0 )
399 + {
400 + $parent = get_term_by( 'id', $parent->parent , 'location' );
401 + $locations[] = $parent->term_id;
402 + }
403 + }
404 + }
405 + }
256 406 }
257 407
258 408 $floor_area_from = $property->floor_area_from_sqft;
259 409 if ( $floor_area_from === '' )
@@ -281,8 +431,9 @@
281 431 'value' => 'applicant',
282 432 'compare' => 'LIKE'
283 433 );
284 434
435 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Applicant membership is stored in serialized _contact_types metadata; preserve complete matching results and per-profile extension checks.
285 436 $args['meta_query'] = $meta_query;
286 437
287 438 $contacts_query = new WP_Query( $args );
288 439
@@ -321,11 +472,11 @@
321 472 else
322 473 {
323 474 ++$matching_elements;
324 475
325 - if ( $property->department != 'commercial' )
476 + if ( $property->department != 'commercial' && ph_get_custom_department_based_on($property->department) != 'commercial' )
326 477 {
327 - if ( $property->department == 'residential-sales' )
478 + if ( $property->department == 'residential-sales' || ph_get_custom_department_based_on($property->department) == 'residential-sales' )
328 479 {
329 480 if ( $percentage_lower != '' && $percentage_higher != '' )
330 481 {
331 482 $match_price_range_lower = '';
@@ -445,38 +596,70 @@
445 596 }
446 597 }
447 598 ++$elements_checked;
448 599
449 - if (
450 - !isset($applicant_profile['locations']) ||
451 - ( isset($applicant_profile['locations']) && empty($applicant_profile['locations']) )
452 - )
600 + if ( apply_filters( 'propertyhive_location_used_when_matching_applicants', TRUE, $applicant_profile ) === TRUE )
453 601 {
454 - ++$matching_elements;
455 - }
456 - elseif ( isset($applicant_profile['locations']) && !empty($applicant_profile['locations']) )
457 - {
458 - foreach ( $applicant_profile['locations'] as $applicant_location )
602 + if ( get_option('propertyhive_applicant_locations_type') != 'text' )
459 603 {
460 - if ( in_array($applicant_location, $locations) )
604 + if (
605 + !isset($applicant_profile['locations']) ||
606 + ( isset($applicant_profile['locations']) && empty($applicant_profile['locations']) )
607 + )
461 608 {
462 609 ++$matching_elements;
463 - break;
464 610 }
611 + elseif ( isset($applicant_profile['locations']) && !empty($applicant_profile['locations']) )
612 + {
613 + foreach ( $applicant_profile['locations'] as $applicant_location )
614 + {
615 + if ( in_array($applicant_location, $locations) )
616 + {
617 + ++$matching_elements;
618 + break;
619 + }
620 + }
621 + }
465 622 }
623 + else
624 + {
625 + if ( !isset($applicant_profile['location_text']) || trim($applicant_profile['location_text']) == '' )
626 + {
627 + ++$matching_elements;
628 + }
629 + else
630 + {
631 + if ( propertyhive_is_location_in_address($property, $applicant_profile['location_text']) === true )
632 + {
633 + ++$matching_elements;
634 + }
635 + }
636 + }
637 + ++$elements_checked;
466 638 }
467 - ++$elements_checked;
468 639 }
469 640
470 - if ( $matching_elements == $elements_checked )
641 + $additional_checks = apply_filters( 'propertyhive_matching_applicants_check', true, $property, get_the_ID(), $applicant_profile );
642 +
643 + if ( $additional_checks === true && $matching_elements == $elements_checked )
471 644 {
472 645 $applicant_profile['applicant_profile_id'] = $i;
473 646
474 647 // Matched all criteria
475 - $applicants[] = array(
476 - 'contact_id' => get_the_ID(),
477 - 'applicant_profile' => $applicant_profile,
478 - );
648 + if ( isset($applicant_profile['grading']) && $applicant_profile['grading'] == 'hot' )
649 + {
650 + $hot_applicants[] = array(
651 + 'contact_id' => get_the_ID(),
652 + 'applicant_profile' => $applicant_profile,
653 + );
654 + }
655 + else
656 + {
657 + $applicants[] = array(
658 + 'contact_id' => get_the_ID(),
659 + 'applicant_profile' => $applicant_profile,
660 + );
661 + }
479 662 }
480 663 }
481 664 }
482 665 }
@@ -485,36 +668,49 @@
485 668
486 669 wp_reset_postdata();
487 670 }
488 671
489 - return $applicants;
672 + return array_merge($hot_applicants, $applicants);
490 673 }
491 674
492 - public function send_emails( $contact_id, $applicant_profile, $email_property_ids, $from_name, $from_email_address, $subject, $body, $to_email_address = '' )
675 + public function send_emails( $contact_id, $applicant_profile, $email_property_ids, $from_name, $from_email_address, $subject, $body, $to_email_address = '', $cc_email_address = '', $bcc_email_address = '' )
493 676 {
494 677 global $wpdb;
495 678
496 679 $current_user = wp_get_current_user();
497 680
681 + $applicant_profile_details = get_post_meta( $contact_id, '_applicant_profile_' . $applicant_profile, TRUE );
682 +
683 + $contact = new PH_Contact($contact_id);
498 684 if ( $to_email_address == '' )
499 685 {
500 - $to_email_address = get_post_meta( $contact_id, '_email_address', TRUE );
686 + $to_email_address = $contact->email_address;
501 687 }
502 688
503 689 $subject = str_replace("[property_count]", count($email_property_ids) . ' propert' . ( ( count($email_property_ids) != 1 ) ? 'ies' : 'y' ), $subject);
504 690
505 - $body = str_replace("[contact_name]", get_the_title($contact_id), $body);
691 + $body = str_replace( '[contact_name]', esc_html( $contact->post_title ), $body );
692 + $body = str_replace( '[contact_dear]', esc_html( $contact->dear() ), $body );
506 693 $body = str_replace("[property_count]", count($email_property_ids) . ' propert' . ( ( count($email_property_ids) != 1 ) ? 'ies' : 'y' ), $body);
507 694
695 + $office_counts = array();
696 +
508 697 if ( strpos($body, '[properties]') !== FALSE )
509 698 {
510 699 ob_start();
700 +
511 701 if ( !empty($email_property_ids) )
512 702 {
513 703 foreach ( $email_property_ids as $email_property_id )
514 704 {
705 + $property = new PH_Property((int)$email_property_id);
515 706
516 - $property = new PH_Property((int)$email_property_id);
707 + if ( $property->office_id != '' && $property->office_id != 0 )
708 + {
709 + if ( !isset($office_counts[$property->office_id]) ) { $office_counts[$property->office_id] = 0; }
710 + ++$office_counts[$property->office_id];
711 + }
712 +
517 713 ph_get_template( 'emails/applicant-match-property.php', array( 'property' => $property ) );
518 714 }
519 715 }
520 716 $body = str_replace("[properties]", ob_get_clean(), $body);
@@ -519,9 +715,48 @@
519 715 }
520 716 $body = str_replace("[properties]", ob_get_clean(), $body);
521 717 }
522 718
719 + $office_name = '';
720 + $office_email_address = '';
721 +
722 + $office_id = get_user_meta($current_user->ID, 'office_id', TRUE);
723 + if ($office_id == '')
724 + {
725 + // No office against user. Use email address of office with most properties
726 + if ( !empty($office_counts) )
727 + {
728 + arsort($office_counts);
729 + reset($office_counts);
730 + $office_id = key($office_counts);
731 + }
732 + }
733 +
734 + if ( !empty($office_id) )
735 + {
736 + $office_name = get_the_title($office_id);
737 + $office_email_address = get_post_meta( $office_id, '_office_email_address_' . str_replace("residential-", "", $applicant_profile_details['department']), TRUE );
738 + }
739 +
740 + $body = str_replace( '[office_name]', esc_html( $office_name ), $body );
741 + $body = str_replace( '[office_email_address]', esc_html( $office_email_address ), $body );
742 +
743 + $body = str_replace( '[negotiator_name]', esc_html( $current_user->display_name ), $body );
744 + $body = str_replace( '[negotiator_email_address]', esc_html( $current_user->user_email ), $body );
745 +
746 + $body = stripslashes($body);
747 +
748 + if (extension_loaded('zlib'))
749 + {
750 + $compressed_body = @gzcompress($body);
751 + if ( $compressed_body !== false )
752 + {
753 + $body = $compressed_body;
754 + }
755 + }
756 +
523 757 // Insert into email log
758 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Typed insertion into the plugin-owned email queue table; no WordPress object API represents these queued messages.
524 759 $insert = $wpdb->insert(
525 760 $wpdb->prefix . 'ph_email_log',
526 761 array(
527 762 'contact_id' => $contact_id,
@@ -527,14 +762,16 @@
527 762 'contact_id' => $contact_id,
528 763 'property_ids' => serialize($email_property_ids),
529 764 'applicant_profile_id' => $applicant_profile,
530 765 'to_email_address' => $to_email_address,
766 + 'cc_email_address' => $cc_email_address,
767 + 'bcc_email_address' => $bcc_email_address,
531 768 'from_name' => $from_name,
532 769 'from_email_address' => $from_email_address,
533 770 'subject' => stripslashes($subject),
534 - 'body' => stripslashes($body),
771 + 'body' => $body,
535 772 'status' => '',
536 - 'send_at' => date("Y-m-d H:i:s"),
773 + 'send_at' => gmdate("Y-m-d H:i:s"),
537 774 'sent_by' => $current_user->ID,
538 775 ),
539 776 array(
540 777 '%d',
@@ -546,8 +783,10 @@
546 783 '%s',
547 784 '%s',
548 785 '%s',
549 786 '%s',
787 + '%s',
788 + '%s',
550 789 '%d',
551 790 )
552 791 );
553 792
@@ -571,9 +810,9 @@
571 810 $applicant_profile_match_history[$email_property_id] = array();
572 811 }
573 812
574 813 $applicant_profile_match_history[$email_property_id][] = array(
575 - 'date' => date("Y-m-d H:i:s"),
814 + 'date' => gmdate("Y-m-d H:i:s"),
576 815 'method' => 'email',
577 816 'email_log_id' => $email_log_id,
578 817 );
579 818
@@ -588,9 +827,9 @@
588 827 'comment_post_ID' => $email_property_id,
589 828 'comment_author' => $current_user->display_name,
590 829 'comment_author_email' => '[email protected]',
591 830 'comment_author_url' => '',
592 - 'comment_date' => date("Y-m-d H:i:s"),
831 + 'comment_date' => gmdate("Y-m-d H:i:s"),
593 832 'comment_content' => serialize($comment),
594 833 'comment_approved' => 1,
595 834 'comment_type' => 'propertyhive_note',
596 835 );
@@ -611,9 +850,9 @@
611 850 'comment_post_ID' => $contact_id,
612 851 'comment_author' => $current_user->display_name,
613 852 'comment_author_email' => '[email protected]',
614 853 'comment_author_url' => '',
615 - 'comment_date' => date("Y-m-d H:i:s"),
854 + 'comment_date' => gmdate("Y-m-d H:i:s"),
616 855 'comment_content' => serialize($comment),
617 856 'comment_approved' => 1,
618 857 'comment_type' => 'propertyhive_note',
619 858 );
@@ -623,5 +862,5 @@
623 862 }
624 863
625 864 }
626 865
627 -endif;
866 +endif;