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 +294 -85 1.4.49 → 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,9 +357,9 @@
240 357
241 358 if ( $property !== FALSE )
242 359 {
243 360 $property_types = array();
244 - $prefix = $property->department == 'commercial' ? 'commercial_' : '';
361 + $prefix = $property->department == 'commercial' || ph_get_custom_department_based_on($property->department) == 'commercial' ? 'commercial_' : '';
245 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 365 foreach ( $term_list as $term )
@@ -262,25 +379,28 @@
262 379 }
263 380 }
264 381 }
265 382
266 - $locations = array();
267 - $term_list = wp_get_post_terms($property_id, 'location', array("fields" => "all"));
268 - if ( !is_wp_error($term_list) && is_array($term_list) && !empty($term_list) )
383 + if ( get_option('propertyhive_applicant_locations_type') != 'text' )
269 384 {
270 - foreach ( $term_list as $term )
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) )
271 388 {
272 - $locations[] = $term->term_id;
273 -
274 - if ( $term->parent != 0 )
389 + foreach ( $term_list as $term )
275 390 {
276 - $parent = get_term_by( 'id', $term->parent , 'location' );
277 - $locations[] = $parent->term_id;
391 + $locations[] = $term->term_id;
278 392
279 - if ( $parent->parent != 0 )
393 + if ( $term->parent != 0 )
280 394 {
281 - $parent = get_term_by( 'id', $parent->parent , 'location' );
395 + $parent = get_term_by( 'id', $term->parent , 'location' );
282 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 + }
283 403 }
284 404 }
285 405 }
286 406 }
@@ -311,8 +431,9 @@
311 431 'value' => 'applicant',
312 432 'compare' => 'LIKE'
313 433 );
314 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.
315 436 $args['meta_query'] = $meta_query;
316 437
317 438 $contacts_query = new WP_Query( $args );
318 439
@@ -351,11 +472,11 @@
351 472 else
352 473 {
353 474 ++$matching_elements;
354 475
355 - if ( $property->department != 'commercial' )
476 + if ( $property->department != 'commercial' && ph_get_custom_department_based_on($property->department) != 'commercial' )
356 477 {
357 - if ( $property->department == 'residential-sales' )
478 + if ( $property->department == 'residential-sales' || ph_get_custom_department_based_on($property->department) == 'residential-sales' )
358 479 {
359 480 if ( $percentage_lower != '' && $percentage_higher != '' )
360 481 {
361 482 $match_price_range_lower = '';
@@ -475,38 +596,70 @@
475 596 }
476 597 }
477 598 ++$elements_checked;
478 599
479 - if (
480 - !isset($applicant_profile['locations']) ||
481 - ( isset($applicant_profile['locations']) && empty($applicant_profile['locations']) )
482 - )
600 + if ( apply_filters( 'propertyhive_location_used_when_matching_applicants', TRUE, $applicant_profile ) === TRUE )
483 601 {
484 - ++$matching_elements;
485 - }
486 - elseif ( isset($applicant_profile['locations']) && !empty($applicant_profile['locations']) )
487 - {
488 - foreach ( $applicant_profile['locations'] as $applicant_location )
602 + if ( get_option('propertyhive_applicant_locations_type') != 'text' )
489 603 {
490 - if ( in_array($applicant_location, $locations) )
604 + if (
605 + !isset($applicant_profile['locations']) ||
606 + ( isset($applicant_profile['locations']) && empty($applicant_profile['locations']) )
607 + )
491 608 {
492 609 ++$matching_elements;
493 - break;
494 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 + }
495 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;
496 638 }
497 - ++$elements_checked;
498 639 }
499 640
500 - 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 )
501 644 {
502 645 $applicant_profile['applicant_profile_id'] = $i;
503 646
504 647 // Matched all criteria
505 - $applicants[] = array(
506 - 'contact_id' => get_the_ID(),
507 - 'applicant_profile' => $applicant_profile,
508 - );
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 + }
509 662 }
510 663 }
511 664 }
512 665 }
@@ -515,36 +668,49 @@
515 668
516 669 wp_reset_postdata();
517 670 }
518 671
519 - return $applicants;
672 + return array_merge($hot_applicants, $applicants);
520 673 }
521 674
522 - 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 = '' )
523 676 {
524 677 global $wpdb;
525 678
526 679 $current_user = wp_get_current_user();
527 680
681 + $applicant_profile_details = get_post_meta( $contact_id, '_applicant_profile_' . $applicant_profile, TRUE );
682 +
683 + $contact = new PH_Contact($contact_id);
528 684 if ( $to_email_address == '' )
529 685 {
530 - $to_email_address = get_post_meta( $contact_id, '_email_address', TRUE );
686 + $to_email_address = $contact->email_address;
531 687 }
532 688
533 689 $subject = str_replace("[property_count]", count($email_property_ids) . ' propert' . ( ( count($email_property_ids) != 1 ) ? 'ies' : 'y' ), $subject);
534 690
535 - $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 );
536 693 $body = str_replace("[property_count]", count($email_property_ids) . ' propert' . ( ( count($email_property_ids) != 1 ) ? 'ies' : 'y' ), $body);
537 694
695 + $office_counts = array();
696 +
538 697 if ( strpos($body, '[properties]') !== FALSE )
539 698 {
540 699 ob_start();
700 +
541 701 if ( !empty($email_property_ids) )
542 702 {
543 703 foreach ( $email_property_ids as $email_property_id )
544 704 {
705 + $property = new PH_Property((int)$email_property_id);
545 706
546 - $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 +
547 713 ph_get_template( 'emails/applicant-match-property.php', array( 'property' => $property ) );
548 714 }
549 715 }
550 716 $body = str_replace("[properties]", ob_get_clean(), $body);
@@ -549,9 +715,48 @@
549 715 }
550 716 $body = str_replace("[properties]", ob_get_clean(), $body);
551 717 }
552 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 +
553 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.
554 759 $insert = $wpdb->insert(
555 760 $wpdb->prefix . 'ph_email_log',
556 761 array(
557 762 'contact_id' => $contact_id,
@@ -557,14 +762,16 @@
557 762 'contact_id' => $contact_id,
558 763 'property_ids' => serialize($email_property_ids),
559 764 'applicant_profile_id' => $applicant_profile,
560 765 'to_email_address' => $to_email_address,
766 + 'cc_email_address' => $cc_email_address,
767 + 'bcc_email_address' => $bcc_email_address,
561 768 'from_name' => $from_name,
562 769 'from_email_address' => $from_email_address,
563 770 'subject' => stripslashes($subject),
564 - 'body' => stripslashes($body),
771 + 'body' => $body,
565 772 'status' => '',
566 - 'send_at' => date("Y-m-d H:i:s"),
773 + 'send_at' => gmdate("Y-m-d H:i:s"),
567 774 'sent_by' => $current_user->ID,
568 775 ),
569 776 array(
570 777 '%d',
@@ -576,8 +783,10 @@
576 783 '%s',
577 784 '%s',
578 785 '%s',
579 786 '%s',
787 + '%s',
788 + '%s',
580 789 '%d',
581 790 )
582 791 );
583 792
@@ -601,9 +810,9 @@
601 810 $applicant_profile_match_history[$email_property_id] = array();
602 811 }
603 812
604 813 $applicant_profile_match_history[$email_property_id][] = array(
605 - 'date' => date("Y-m-d H:i:s"),
814 + 'date' => gmdate("Y-m-d H:i:s"),
606 815 'method' => 'email',
607 816 'email_log_id' => $email_log_id,
608 817 );
609 818
@@ -618,9 +827,9 @@
618 827 'comment_post_ID' => $email_property_id,
619 828 'comment_author' => $current_user->display_name,
620 829 'comment_author_email' => '[email protected]',
621 830 'comment_author_url' => '',
622 - 'comment_date' => date("Y-m-d H:i:s"),
831 + 'comment_date' => gmdate("Y-m-d H:i:s"),
623 832 'comment_content' => serialize($comment),
624 833 'comment_approved' => 1,
625 834 'comment_type' => 'propertyhive_note',
626 835 );
@@ -641,9 +850,9 @@
641 850 'comment_post_ID' => $contact_id,
642 851 'comment_author' => $current_user->display_name,
643 852 'comment_author_email' => '[email protected]',
644 853 'comment_author_url' => '',
645 - 'comment_date' => date("Y-m-d H:i:s"),
854 + 'comment_date' => gmdate("Y-m-d H:i:s"),
646 855 'comment_content' => serialize($comment),
647 856 'comment_approved' => 1,
648 857 'comment_type' => 'propertyhive_note',
649 858 );
@@ -653,5 +862,5 @@
653 862 }
654 863
655 864 }
656 865
657 -endif;
866 +endif;