PluginProbe
Property Hive / 2.3.1
Property Hive v2.3.1
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 1.4.61 All 261 releases
propertyhive / includes / admin / class-ph-admin-matching-applicants.php

class-ph-admin-matching-applicants.php in Property Hive 2.3.1, at includes/admin/class-ph-admin-matching-applicants.php

867 lines 41.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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
5 /**
6 * PropertyHive Admin Matching Applicants Class.
7 *
8 * @author PropertyHive
9 * @category Admin
10 * @package PropertyHive/Admin
11 * @version 1.0.0
12 */
13
14 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
15
16 if ( ! class_exists( 'PH_Admin_Matching_Applicants' ) ) :
17
18 /**
19 * PH_Admin_Matching_Applicants
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.
22 class PH_Admin_Matching_Applicants {
23
24 public function output()
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'] );
32
33 $property_id = ( isset( $request_get['property_id'] ) && is_scalar( $request_get['property_id'] ) ) ? absint( $request_get['property_id'] ) : 0;
34
35 if ( ! $property_id || get_post_type( $property_id ) !== 'property' )
36 {
37 die('Invalid property_id passed');
38 }
39
40 $property = new PH_Property($property_id);
41
42 if ( $has_step )
43 {
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' )) );
48
49 $request_post = wp_unslash( $_POST );
50 $step = is_string( $request_post['step'] ) ? sanitize_key( $request_post['step'] ) : '';
51
52 switch ( $step )
53 {
54 case "one":
55 {
56 // Properties have been selected to email or dismiss
57
58 // Handle dismissed properties
59 $this->dismiss_properties();
60
61 $nothing_to_send = true;
62
63 // Handle properties to email
64 if ( isset( $request_post['email_contact_applicant_profile_id'] ) && ! empty( $request_post['email_contact_applicant_profile_id'] ) )
65 {
66 $nothing_to_send = false;
67
68 $subject = get_option( 'propertyhive_property_match_default_email_subject', '' );
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 }
81 }
82
83 $nothing_to_send = apply_filters( 'propertyhive_applicant_match_nothing_to_send', $nothing_to_send );
84
85 if ( $nothing_to_send != true )
86 {
87 ?>
88 <div class="wrap propertyhive">
89
90 <div id="poststuff">
91
92 <form method="post" id="mainform" action="" enctype="multipart/form-data">
93 <?php
94 if ( isset( $request_post['email_contact_applicant_profile_id'] ) && ! empty( $request_post['email_contact_applicant_profile_id'] ) )
95 {
96 // We've got emails to send
97 include 'views/html-admin-matching-applicants-email.php';
98 }
99
100 do_action( 'propertyhive_applicant_match_step_two', $property_id );
101 ?>
102 <p class="submit">
103
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' )); ?>" />
107 <?php } ?>
108
109 <input type="hidden" name="step" value="two" />
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 ?>" />
124 <?php do_action( 'propertyhive_applicant_match_step_two_hidden_fields' ); ?>
125 <?php wp_nonce_field( 'propertyhive-matching-applicants' ); ?>
126
127 </p>
128
129 <p>
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 );
142 ?>
143 </p>
144
145 </form>
146
147 </div>
148
149 </div>
150
151 <script>
152
153 jQuery(document).ready(function()
154 {
155 jQuery('#preview_email').click(function(e)
156 {
157 e.preventDefault();
158
159 showPreview();
160 });
161 });
162
163 function showPreview()
164 {
165 jQuery('#mainform').attr('target', '_blank');
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 ); ?>);
167
168 jQuery('#mainform').submit();
169 jQuery('#mainform').attr('target', '_self');
170 jQuery('#mainform').attr('action', '');
171 }
172
173 </script>
174 <?php
175 }
176
177 if ( $nothing_to_send == true )
178 {
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>';
180
181 //header("Location: " . get_edit_post_link( $contact_id, 'url' ) . '&ph_message=2' ); // properties marked as not interested
182 //die();
183 }
184
185 break;
186 }
187 case "two":
188 {
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 ) );
193
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 }
205
206 $email_address = get_post_meta( (int)$contact_id, '_email_address', TRUE );
207
208 $to_email_addresses = explode(",", $email_address);
209 $new_to_email_addresses = array();
210 foreach ( $to_email_addresses as $to_email_address)
211 {
212 $new_to_email_addresses[] = sanitize_email($to_email_address);
213 }
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
257 // Email info entered. Time to send emails
258 $this->send_emails(
259 (int)$contact_id,
260 (int)$applicant_profile_id,
261 array($property_id),
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)
269 );
270 }
271
272 //header("Location: " . get_edit_post_link( $contact_id, 'url' ) . '&ph_message=1' ); // email sent
273 //die();
274 }
275
276 do_action( 'propertyhive_applicant_match_step_send', $property_id );
277
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>';
279 }
280 }
281 }
282 else
283 {
284 $applicants = $this->get_matching_applicants( $property_id );
285
286 $on_market_change_date = $property->_on_market_change_date;
287 $price_change_date = $property->_price_change_date;
288
289 include 'views/html-admin-matching-applicants.php';
290 }
291 }
292
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;
301
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'] ) )
303 {
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 }
313
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 }
319
320 // Get currently dismissed properties for this contact to decide if we need to add or remove it
321 $dismissed_properties = get_post_meta( $contact_id, '_dismissed_properties', TRUE );
322
323 if ( !is_array($dismissed_properties) )
324 {
325 $dismissed_properties = array();
326 }
327
328 if ( in_array((int)$property_id, $dismissed_properties) )
329 {
330 // Already dismissed. Need to remove from array
331 if( ($key = array_search((int)$property_id, $dismissed_properties)) !== false )
332 {
333 unset($dismissed_properties[$key]);
334 }
335 }
336 else
337 {
338 // Not dismissed. Add to array
339 $dismissed_properties[] = (int)$property_id;
340 }
341
342 $dismissed_properties = array_unique($dismissed_properties);
343
344 update_post_meta( $contact_id, '_dismissed_properties', $dismissed_properties );
345 }
346 }
347 }
348
349 public function get_matching_applicants( $property_id )
350 {
351 global $post;
352
353 $hot_applicants = array();
354 $applicants = array();
355
356 $property = new PH_Property((int)$property_id);
357
358 if ( $property !== FALSE )
359 {
360 $property_types = array();
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"));
363 if ( !is_wp_error($term_list) && is_array($term_list) && !empty($term_list) )
364 {
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 }
381 }
382
383 if ( get_option('propertyhive_applicant_locations_type') != 'text' )
384 {
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 }
406 }
407
408 $floor_area_from = $property->floor_area_from_sqft;
409 if ( $floor_area_from === '' )
410 {
411 $floor_area_from = 0;
412 }
413 $floor_area_to = $property->floor_area_to_sqft;
414 if ( $floor_area_to === '' )
415 {
416 $floor_area_to = 99999999999;
417 }
418
419 $percentage_lower = get_option( 'propertyhive_applicant_match_price_range_percentage_lower', '' );
420 $percentage_higher = get_option( 'propertyhive_applicant_match_price_range_percentage_higher', '' );
421
422 $args = array(
423 'post_type' => 'contact',
424 'nopaging' => true,
425 );
426
427 // Meta query
428 $meta_query = array();
429 $meta_query[] = array(
430 'key' => '_contact_types',
431 'value' => 'applicant',
432 'compare' => 'LIKE'
433 );
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.
436 $args['meta_query'] = $meta_query;
437
438 $contacts_query = new WP_Query( $args );
439
440 if ( $contacts_query->have_posts() )
441 {
442 while ( $contacts_query->have_posts() )
443 {
444 $contacts_query->the_post();
445
446 $contact = new PH_Contact(get_the_ID());
447
448 $dismissed_properties = get_post_meta( get_the_ID(), '_dismissed_properties', TRUE );
449
450 if ( is_array($dismissed_properties) && in_array($property_id, $dismissed_properties) )
451 {
452 // This property is dismissed
453 }
454 else
455 {
456 $num_applicant_profiles = get_post_meta( get_the_ID(), '_applicant_profiles', TRUE );
457
458 for ( $i = 0; $i < $num_applicant_profiles; ++$i )
459 {
460 $applicant_profile = get_post_meta( get_the_ID(), '_applicant_profile_' . $i, TRUE );
461
462 if ( isset($applicant_profile['send_matching_properties']) && $applicant_profile['send_matching_properties'] == 'yes' )
463 {
464 $elements_checked = 0;
465 $matching_elements = 0;
466
467 ++$elements_checked;
468 if ( $applicant_profile['department'] != $property->department )
469 {
470
471 }
472 else
473 {
474 ++$matching_elements;
475
476 if ( $property->department != 'commercial' && ph_get_custom_department_based_on($property->department) != 'commercial' )
477 {
478 if ( $property->department == 'residential-sales' || ph_get_custom_department_based_on($property->department) == 'residential-sales' )
479 {
480 if ( $percentage_lower != '' && $percentage_higher != '' )
481 {
482 $match_price_range_lower = '';
483 if ( !isset($applicant_profile['match_price_range_lower_actual']) || ( isset($applicant_profile['match_price_range_lower_actual']) && $applicant_profile['match_price_range_lower_actual'] == '' ) )
484 {
485 if ( isset($applicant_profile['max_price_actual']) && $applicant_profile['max_price_actual'] != '' )
486 {
487 if ( $percentage_lower != '' )
488 {
489 $match_price_range_lower = $applicant_profile['max_price_actual'] - ( $applicant_profile['max_price_actual'] * ( $percentage_lower / 100 ) );
490 }
491 }
492 }
493 else
494 {
495 $match_price_range_lower = $applicant_profile['match_price_range_lower_actual'];
496 }
497
498 $match_price_range_higher = '';
499 if ( !isset($applicant_profile['match_price_range_higher_actual']) || ( isset($applicant_profile['match_price_range_higher_actual']) && $applicant_profile['match_price_range_higher_actual'] == '' ) )
500 {
501 if ( isset($applicant_profile['max_price_actual']) && $applicant_profile['max_price_actual'] != '' )
502 {
503 if ( $percentage_higher != '' )
504 {
505 $match_price_range_higher = $applicant_profile['max_price_actual'] + ( $applicant_profile['max_price_actual'] * ( $percentage_higher / 100 ) );
506 }
507 }
508 }
509 else
510 {
511 $match_price_range_higher = $applicant_profile['match_price_range_higher_actual'];
512 }
513
514 if (
515 ( $match_price_range_lower == '' && $match_price_range_higher == '' ) ||
516 (
517 $property->_price_actual >= $match_price_range_lower &&
518 $property->_price_actual <= $match_price_range_higher
519 )
520 )
521 {
522 ++$matching_elements;
523 }
524 ++$elements_checked;
525 }
526 else
527 {
528 if (
529 $applicant_profile['max_price_actual'] == '' ||
530 $property->_price_actual <= $applicant_profile['max_price_actual']
531 )
532 {
533 ++$matching_elements;
534 }
535 ++$elements_checked;
536 }
537 }
538 else
539 {
540 if (
541 $applicant_profile['max_price_actual'] == '' ||
542 $property->_price_actual <= $applicant_profile['max_price_actual']
543 )
544 {
545 ++$matching_elements;
546 }
547 ++$elements_checked;
548 }
549
550 if (
551 $applicant_profile['min_beds'] == '' ||
552 $property->_bedrooms >= $applicant_profile['min_beds']
553 )
554 {
555 ++$matching_elements;
556 }
557 ++$elements_checked;
558 }
559 else
560 {
561 if ( $applicant_profile['min_floor_area'] === '' )
562 {
563 $applicant_profile['min_floor_area'] = 0;
564 }
565 if ( $applicant_profile['max_floor_area'] === '' )
566 {
567 $applicant_profile['max_floor_area'] = 99999999999;
568 }
569
570 if (
571 $applicant_profile['min_floor_area'] <= $floor_area_to &&
572 $applicant_profile['max_floor_area'] >= $floor_area_from
573 )
574 {
575 ++$matching_elements;
576 }
577 ++$elements_checked;
578 }
579
580 if (
581 !isset($applicant_profile[$prefix . 'property_types']) ||
582 ( isset($applicant_profile[$prefix . 'property_types']) && empty($applicant_profile[$prefix . 'property_types']) )
583 )
584 {
585 ++$matching_elements;
586 }
587 elseif ( isset($applicant_profile[$prefix . 'property_types']) && !empty($applicant_profile[$prefix . 'property_types']) )
588 {
589 foreach ( $applicant_profile[$prefix . 'property_types'] as $applicant_property_type )
590 {
591 if ( in_array($applicant_property_type, $property_types) )
592 {
593 ++$matching_elements;
594 break;
595 }
596 }
597 }
598 ++$elements_checked;
599
600 if ( apply_filters( 'propertyhive_location_used_when_matching_applicants', TRUE, $applicant_profile ) === TRUE )
601 {
602 if ( get_option('propertyhive_applicant_locations_type') != 'text' )
603 {
604 if (
605 !isset($applicant_profile['locations']) ||
606 ( isset($applicant_profile['locations']) && empty($applicant_profile['locations']) )
607 )
608 {
609 ++$matching_elements;
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 }
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;
638 }
639 }
640
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 )
644 {
645 $applicant_profile['applicant_profile_id'] = $i;
646
647 // Matched all criteria
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 }
662 }
663 }
664 }
665 }
666 }
667 }
668
669 wp_reset_postdata();
670 }
671
672 return array_merge($hot_applicants, $applicants);
673 }
674
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 = '' )
676 {
677 global $wpdb;
678
679 $current_user = wp_get_current_user();
680
681 $applicant_profile_details = get_post_meta( $contact_id, '_applicant_profile_' . $applicant_profile, TRUE );
682
683 $contact = new PH_Contact($contact_id);
684 if ( $to_email_address == '' )
685 {
686 $to_email_address = $contact->email_address;
687 }
688
689 $subject = str_replace("[property_count]", count($email_property_ids) . ' propert' . ( ( count($email_property_ids) != 1 ) ? 'ies' : 'y' ), $subject);
690
691 $body = str_replace( '[contact_name]', esc_html( $contact->post_title ), $body );
692 $body = str_replace( '[contact_dear]', esc_html( $contact->dear() ), $body );
693 $body = str_replace("[property_count]", count($email_property_ids) . ' propert' . ( ( count($email_property_ids) != 1 ) ? 'ies' : 'y' ), $body);
694
695 $office_counts = array();
696
697 if ( strpos($body, '[properties]') !== FALSE )
698 {
699 ob_start();
700
701 if ( !empty($email_property_ids) )
702 {
703 foreach ( $email_property_ids as $email_property_id )
704 {
705 $property = new PH_Property((int)$email_property_id);
706
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
713 ph_get_template( 'emails/applicant-match-property.php', array( 'property' => $property ) );
714 }
715 }
716 $body = str_replace("[properties]", ob_get_clean(), $body);
717 }
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
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.
759 $insert = $wpdb->insert(
760 $wpdb->prefix . 'ph_email_log',
761 array(
762 'contact_id' => $contact_id,
763 'property_ids' => serialize($email_property_ids),
764 'applicant_profile_id' => $applicant_profile,
765 'to_email_address' => $to_email_address,
766 'cc_email_address' => $cc_email_address,
767 'bcc_email_address' => $bcc_email_address,
768 'from_name' => $from_name,
769 'from_email_address' => $from_email_address,
770 'subject' => stripslashes($subject),
771 'body' => $body,
772 'status' => '',
773 'send_at' => gmdate("Y-m-d H:i:s"),
774 'sent_by' => $current_user->ID,
775 ),
776 array(
777 '%d',
778 '%s',
779 '%d',
780 '%s',
781 '%s',
782 '%s',
783 '%s',
784 '%s',
785 '%s',
786 '%s',
787 '%s',
788 '%s',
789 '%d',
790 )
791 );
792
793 if ( $insert !== FALSE )
794 {
795 $email_log_id = $wpdb->insert_id;
796
797 // Insert properties into applicant match history
798 $applicant_profile_match_history = get_post_meta( $contact_id, '_applicant_profile_' . $applicant_profile . '_match_history', TRUE );
799 if ( !is_array($applicant_profile_match_history) )
800 {
801 $applicant_profile_match_history = array();
802 }
803
804 if ( is_array($email_property_ids) && !empty($email_property_ids) )
805 {
806 foreach ( $email_property_ids as $email_property_id )
807 {
808 if ( !isset($applicant_profile_match_history[$email_property_id]) )
809 {
810 $applicant_profile_match_history[$email_property_id] = array();
811 }
812
813 $applicant_profile_match_history[$email_property_id][] = array(
814 'date' => gmdate("Y-m-d H:i:s"),
815 'method' => 'email',
816 'email_log_id' => $email_log_id,
817 );
818
819 // Add note/comment to property
820 $comment = array(
821 'note_type' => 'mailout',
822 'method' => 'email',
823 'email_log_id' => $email_log_id
824 );
825
826 $data = array(
827 'comment_post_ID' => $email_property_id,
828 'comment_author' => $current_user->display_name,
829 'comment_author_email' => 'propertyhive@noreply.com',
830 'comment_author_url' => '',
831 'comment_date' => gmdate("Y-m-d H:i:s"),
832 'comment_content' => serialize($comment),
833 'comment_approved' => 1,
834 'comment_type' => 'propertyhive_note',
835 );
836 wp_insert_comment( $data );
837
838 }
839
840 update_post_meta( $contact_id, '_applicant_profile_' . $applicant_profile . '_match_history', $applicant_profile_match_history );
841
842 // Add note/comment to contact
843 $comment = array(
844 'note_type' => 'mailout',
845 'method' => 'email',
846 'email_log_id' => $email_log_id
847 );
848
849 $data = array(
850 'comment_post_ID' => $contact_id,
851 'comment_author' => $current_user->display_name,
852 'comment_author_email' => 'propertyhive@noreply.com',
853 'comment_author_url' => '',
854 'comment_date' => gmdate("Y-m-d H:i:s"),
855 'comment_content' => serialize($comment),
856 'comment_approved' => 1,
857 'comment_type' => 'propertyhive_note',
858 );
859 wp_insert_comment( $data );
860 }
861 }
862 }
863
864 }
865
866 endif;
867