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