PluginProbe
Property Hive / 2.2.3
Property Hive v2.2.3
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 / class-ph-ajax.php

class-ph-ajax.php in Property Hive 2.2.3, at includes/class-ph-ajax.php

8,239 lines 313.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
5 /**
6 * PropertyHive PH_AJAX
7 *
8 * AJAX Event Handler
9 *
10 * @class PH_AJAX
11 * @version 1.0.0
12 * @package PropertyHive/Classes
13 * @category Class
14 * @author PropertyHive
15 */
16 class PH_AJAX {
17
18 /**
19 * Hook into ajax events
20 */
21 public function __construct() {
22
23 // propertyhive_EVENT => nopriv
24 $ajax_events = array(
25 'add_note' => false,
26 'delete_note' => false,
27 'toggle_note_pinned' => false,
28 'get_notes_grid' => false,
29 'get_pinned_notes_grid' => false,
30 'fetch_note_mentions' => false,
31 'search_contacts' => false,
32 'search_properties' => false,
33 'search_negotiators' => false,
34 'load_existing_owner_contact' => false,
35 'load_existing_features' => false,
36 'make_property_enquiry' => true,
37 'create_contact_from_enquiry' => false,
38 'merge_contact_records' => false,
39
40 // Dashboard components
41 'get_news' => false,
42 'get_viewings_awaiting_applicant_feedback' => false,
43 'get_my_upcoming_appointments' => false,
44 'get_upcoming_overdue_key_dates' => false,
45
46 // Property actions
47 'check_duplicate_reference_number' => false,
48 'osm_geocoding_request' => false,
49 'get_property_marketing_statistics_meta_box' => false,
50 'get_property_tenancies_grid' => false,
51
52 // Contact actions
53 'create_contact_login' => false,
54 'get_contact_tenancies_grid' => false,
55 'get_contact_solicitor' => false,
56
57 // Appraisal actions
58 'get_appraisal_details_meta_box' => false,
59 'get_appraisal_actions' => false,
60 'appraisal_carried_out' => false,
61 'appraisal_cancelled' => false,
62 'appraisal_won' => false,
63 'appraisal_lost_reason' => false,
64 'appraisal_instructed' => false,
65 'appraisal_email_owner_booking_confirmation' => false,
66 'appraisal_revert_pending' => false,
67 'appraisal_revert_carried_out' => false,
68 'appraisal_revert_won' => false,
69
70 // Viewing actions
71 'book_viewing_property' => false,
72 'book_viewing_contact' => false,
73 'get_viewing_details_meta_box' => false,
74 'get_viewing_actions' => false,
75 'get_viewing_lightbox' => false,
76 'viewing_carried_out' => false,
77 'viewing_cancelled' => false,
78 'viewing_no_show' => false,
79 'viewing_email_applicant_booking_confirmation' => false,
80 'viewing_email_owner_booking_confirmation' => false,
81 'viewing_email_attending_negotiator_booking_confirmation' => false,
82 'viewing_email_applicant_cancellation_notification' => false,
83 'viewing_email_owner_cancellation_notification' => false,
84 'viewing_email_attending_negotiator_cancellation_notification' => false,
85 'viewing_interested_feedback' => false,
86 'viewing_not_interested_feedback' => false,
87 'viewing_feedback_not_required' => false,
88 'viewing_revert_feedback_pending' => false,
89 'viewing_revert_pending' => false,
90 'viewing_feedback_passed_on' => false,
91 'get_property_viewings_meta_box' => false,
92 'get_contact_viewings_meta_box' => false,
93
94 // Offer actions
95 'record_offer_property' => false,
96 'record_offer_contact' => false,
97 'get_offer_details_meta_box' => false,
98 'get_offer_actions' => false,
99 'get_property_offers_meta_box' => false,
100 'offer_accepted' => false,
101 'offer_declined' => false,
102 'offer_withdrawn' => false,
103 'offer_revert_pending' => false,
104 'get_contact_offers_meta_box' => false,
105
106 // Sale actions
107 'get_sale_details_meta_box' => false,
108 'get_sale_actions' => false,
109 'get_sale_details_meta_box' => false,
110 'sale_exchanged' => false,
111 'sale_completed' => false,
112 'sale_fallen_through' => false,
113 'offer_declined' => false,
114 'get_property_sales_meta_box' => false,
115 'get_contact_sales_meta_box' => false,
116
117 // Enquiry actions
118 'get_property_enquiries_meta_box' => false,
119 'get_contact_enquiries_meta_box' => false,
120
121 // Tenancy actions
122 'add_key_date' => false,
123 'get_management_dates_grid' => false,
124 'get_key_dates_quick_edit_row' => false,
125 'check_key_date_recurrence' => false,
126 'save_key_date' => false,
127 'delete_key_date' => false,
128
129 'validate_save_contact' => false,
130 'applicant_registration' => true,
131 'login' => true,
132 'lost_password' => true,
133 'reset_password' => true,
134 'save_account_details' => true,
135 'save_account_requirements' => true,
136
137 // Dismissing notices
138 'dismiss_notice_leave_review' => false,
139 'dismiss_notice_retired_template_assistant' => false,
140 'dismiss_notice_demo_data' => false,
141 'dismiss_notice_epl' => false,
142 'dismiss_notice_missing_search_results' => false,
143 'dismiss_notice_missing_google_maps_api_key' => false,
144 'dismiss_notice_invalid_expired_license_key' => false,
145 'dismiss_notice_email_cron_not_running' => false,
146
147 // Settings
148 'save_term_order' => false,
149
150 // PRO features activate/deactivate
151 'activate_pro_feature' => false,
152 'deactivate_pro_feature' => false,
153
154 'deactivate_survey' => false,
155 );
156
157 foreach ( $ajax_events as $ajax_event => $nopriv )
158 {
159 add_action( 'wp_ajax_propertyhive_' . $ajax_event, array( $this, $ajax_event ) );
160
161 if ( $nopriv ) {
162 add_action( 'wp_ajax_nopriv_propertyhive_' . $ajax_event, array( $this, $ajax_event ) );
163 }
164 }
165 }
166
167 public function deactivate_survey()
168 {
169 // Verify the nonce
170 if ( !isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'deactivate-survey') )
171 {
172 wp_send_json_error('Invalid nonce', 403);
173 die();
174 }
175
176 if ( !isset($_POST['reason']) || empty($_POST['reason']) )
177 {
178 wp_send_json_error('Reason is required', 400);
179 die();
180 }
181
182 $reason = sanitize_text_field($_POST['reason']);
183 $comments = isset($_POST['comments']) ? sanitize_textarea_field($_POST['comments']) : '';
184 $anonymous = isset($_POST['anonymous']) && $_POST['anonymous'] === 'yes';
185
186 $license_type = get_option('propertyhive_license_type');
187 if ( $license_type == 'pro' )
188 {
189 $license_key = get_option('propertyhive_pro_license_key');
190 }
191 else
192 {
193 $license_key = get_option('propertyhive_license_key');
194 }
195 $propertyhive_install_timestamp = get_option('propertyhive_install_timestamp');
196 $active_plugins = get_option('active_plugins');
197 $all_plugins = get_plugins(); // Fetch detailed data for all plugins
198
199 $active_plugins_with_versions = array();
200
201 foreach ( $active_plugins as $plugin )
202 {
203 if ( isset($all_plugins[$plugin]) )
204 {
205 $active_plugins_with_versions[] = array(
206 'name' => $all_plugins[$plugin]['Name'],
207 'version' => $all_plugins[$plugin]['Version'],
208 'path' => $plugin,
209 );
210 }
211 }
212 $server_software = $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown';
213
214 // Prepare data for third-party POST
215 $third_party_data = array(
216 'reason' => $reason,
217 'comments' => $comments,
218 'anonymous' => $anonymous ? 'yes' : 'no',
219 );
220
221 if (!$anonymous)
222 {
223 $third_party_data['site_url'] = get_site_url();
224 $third_party_data['admin_email'] = get_option('admin_email');
225 $third_party_data['license_type'] = $license_type;
226 $third_party_data['license_key'] = $license_key;
227 $third_party_data['active_plugins'] = $active_plugins_with_versions;
228 $third_party_data['active_theme'] = wp_get_theme()->get('Name');
229 $third_party_data['wordpress_version'] = get_bloginfo('version');
230 $third_party_data['php_version'] = phpversion();
231 $third_party_data['server_software'] = $server_software;
232 }
233
234 //wp_send_json_success(json_encode($third_party_data, true));
235
236 // Make the remote POST request
237 $response = wp_remote_post('https://wp-property-hive.com/deactivate-survey.php', array(
238 'method' => 'POST',
239 'body' => $third_party_data
240 ));
241
242 if ( is_wp_error($response) )
243 {
244 wp_send_json_error($response->get_error_message(), 500);
245 die();
246 }
247
248 $response_body = wp_remote_retrieve_body($response);
249 wp_send_json_success(json_decode($response_body, true));
250
251 die();
252 }
253
254 public function save_term_order()
255 {
256 check_ajax_referer( 'updates', 'security' );
257
258 if ( !isset($_POST['taxonomy']) || ( isset($_POST['taxonomy']) && empty(ph_clean($_POST['taxonomy'])) ) )
259 {
260 die();
261 }
262
263 if ( !isset($_POST['term']) || ( isset($_POST['term']) && empty(ph_clean($_POST['term'])) ) )
264 {
265 die();
266 }
267
268 update_option( 'propertyhive_taxonomy_terms_order_' . ph_clean($_POST['taxonomy']), implode("|", ph_clean($_POST['term'])));
269
270 // Quit out
271 die();
272 }
273
274 public function dismiss_notice_leave_review()
275 {
276 update_option( 'propertyhive_review_prompt_due_timestamp', 0 );
277
278 // Quit out
279 die();
280 }
281
282 public function dismiss_notice_retired_template_assistant()
283 {
284 if ( is_multisite() )
285 {
286 if ( ! is_super_admin() ) return;
287 delete_site_option( 'propertyhive_template_assistant_retired_notice' );
288 }
289 else
290 {
291 if ( ! current_user_can( 'activate_plugins' ) ) return;
292 delete_option( 'propertyhive_template_assistant_retired_notice' );
293 }
294
295 // Quit out
296 die();
297 }
298
299 public function dismiss_notice_demo_data()
300 {
301 update_option( 'propertyhive_hide_demo_data_tab', 'yes' );
302
303 // Quit out
304 die();
305 }
306
307 public function dismiss_notice_epl()
308 {
309 update_option( 'epl_notice_dismissed', 'yes' );
310
311 // Quit out
312 die();
313 }
314
315 public function dismiss_notice_missing_search_results()
316 {
317 update_option( 'missing_search_results_notice_dismissed', 'yes' );
318
319 // Quit out
320 die();
321 }
322
323 public function dismiss_notice_missing_google_maps_api_key()
324 {
325 update_option( 'missing_google_maps_api_key_notice_dismissed', 'yes' );
326
327 // Quit out
328 die();
329 }
330
331 public function dismiss_notice_invalid_expired_license_key()
332 {
333 update_option( 'missing_invalid_expired_license_key_notice_dismissed', 'yes' );
334
335 // Quit out
336 die();
337 }
338
339 public function dismiss_notice_email_cron_not_running()
340 {
341 update_option( 'email_cron_not_running_dismissed', 'yes' );
342 }
343
344 /**
345 * Output headers for JSON requests
346 */
347 private function json_headers() {
348 header( 'Content-Type: application/json; charset=utf-8' );
349 }
350
351 /**
352 * Return a list string, comma delimited with an ampersand(&) before the final item
353 */
354 private function get_list_string( $list_items )
355 {
356 $list_string = '';
357 if ( count($list_items) == 1 )
358 {
359 $list_string = $list_items[0];
360 }
361 elseif ( count($list_items) > 1 )
362 {
363 $last_item = array_pop($list_items);
364 $list_string = implode(', ', $list_items) . ' & ' . $last_item;
365 }
366 return $list_string;
367 }
368
369 private function check_recaptcha_form_response($errors, $key, $control)
370 {
371 $secret = isset( $control['secret'] ) ? $control['secret'] : '';
372 $response = isset( $_POST['g-recaptcha-response'] ) ? ph_clean($_POST['g-recaptcha-response']) : '';
373
374 $response = wp_remote_post(
375 'https://www.google.com/recaptcha/api/siteverify',
376 array(
377 'method' => 'POST',
378 'body' => array( 'secret' => $secret, 'response' => $response ),
379 )
380 );
381 if ( is_wp_error( $response ) )
382 {
383 $errors[] = $response->get_error_message();
384 }
385 else
386 {
387 $response = json_decode($response['body'], TRUE);
388
389 if ( $response === FALSE )
390 {
391 $errors[] = __( 'Error decoding response from reCAPTCHA check', 'propertyhive' );
392 }
393 else
394 {
395 if ( isset($response['success']) && $response['success'] == true )
396 {
397 if ( $key == 'recaptcha' )
398 {
399
400 }
401 elseif ( $key == 'recaptcha-v3' )
402 {
403 $score_threshold = round((float)get_option('propertyhive_captcha_score_threshold', 0.5), 1);
404 if ( !is_numeric($score_threshold) || $score_threshold < 0 || $score_threshold > 1 )
405 {
406 $score_threshold = 0.5;
407 }
408 if ( isset($response['score']) && $response['score'] >= $score_threshold )
409 {
410
411 }
412 else
413 {
414 $errors[] = __('Failed reCAPTCHA validation due to high spam score', 'propertyhive' ) . ': ' . $response['score'];
415 }
416 }
417 }
418 else
419 {
420 $error_message = __( 'Failed reCAPTCHA validation', 'propertyhive' );
421
422 // Check if Google returned error codes
423 if ( isset($response['error-codes']) && is_array($response['error-codes']) )
424 {
425 $error_message .= ' (' . implode(', ', $response['error-codes']) . ')';
426 }
427
428 $errors[] = $error_message;
429 }
430 }
431 }
432 return $errors;
433 }
434
435 public function create_contact_login()
436 {
437 check_ajax_referer( 'create-login', 'security' );
438
439 $this->json_headers();
440
441 if (empty($_POST['contact_id']))
442 {
443 $return = array('error' => 'No contact selected');
444 echo json_encode( $return );
445 die();
446 }
447
448 if (empty($_POST['password']))
449 {
450 $return = array('error' => 'No password entered');
451 echo json_encode( $return );
452 die();
453 }
454
455 $contact = new PH_Contact((int)$_POST['contact_id']);
456
457 $display_name = get_the_title((int)$_POST['contact_id']);
458
459 // Create user
460 $userdata = array(
461 'display_name' => $display_name,
462 'user_login' => sanitize_email($contact->email_address),
463 'user_email' => sanitize_email($contact->email_address),
464 'user_pass' => $_POST['password'],
465 'role' => 'property_hive_contact',
466 'show_admin_bar_front' => 'false',
467 );
468
469 if ( !empty($display_name) )
470 {
471 $name_parts = explode( ' ', $display_name );
472
473 if ( count($name_parts) > 1 )
474 {
475 $userdata['last_name'] = array_pop($name_parts);
476 $userdata['first_name'] = implode(' ', $name_parts);
477 }
478 else
479 {
480 $userdata['last_name'] = $display_name;
481 }
482 }
483
484 $user_id = wp_insert_user( $userdata );
485
486 // On success
487 if ( ! is_wp_error( $user_id ) )
488 {
489 // Assign user ID to CPT
490 add_post_meta( (int)$_POST['contact_id'], '_user_id', $user_id );
491
492 $return = array('success' => true);
493 }
494 else
495 {
496 $return = array('error' => 'Failed to create user login');
497 }
498
499 echo json_encode( $return );
500 die();
501 }
502
503 /**
504 * Login user
505 */
506 public function login()
507 {
508 $return = array(
509 'success' => false,
510 'errors' => array(),
511 );
512
513 if ( check_ajax_referer( 'ph_login', 'security', false ) === FALSE )
514 {
515 $return['errors'][] = 'Invalid nonce';
516
517 $this->json_headers();
518 echo json_encode( $return );
519
520 // Quit out
521 die();
522 }
523
524 $creds = array(
525 'user_login' => ph_clean($_POST['email_address']),
526 'user_password' => ph_clean($_POST['password']),
527 );
528
529 $user = wp_signon( apply_filters( 'propertyhive_login_credentials', $creds ), is_ssl() );
530
531 if ( is_wp_error( $user ) )
532 {
533 //$return['errors'][] = $user->get_error_message();
534 }
535 else
536 {
537 // Check has associated contact CPT and is published
538 $args = array(
539 'post_type' => apply_filters( 'propertyhive_allowed_login_post_type', array( 'contact' ) ),
540 'fields' => 'ids',
541 'posts_per_page' => 1,
542 'post_status' => array( 'publish' ),
543 'meta_query' => array(
544 array(
545 'key' => '_user_id',
546 'value' => $user->ID
547 )
548 )
549 );
550
551 $contact_query = new WP_Query( $args );
552
553 if ( $contact_query->have_posts() )
554 {
555 while ( $contact_query->have_posts() )
556 {
557 $contact_query->the_post();
558
559 // Has associated published contact CPT
560 $return['success'] = true;
561
562 do_action('propertyhive_user_logged_in', get_the_ID(), $user->ID);
563 }
564 }
565
566 wp_reset_postdata();
567 }
568
569 $this->json_headers();
570 echo json_encode( $return );
571
572 // Quit out
573 die();
574 }
575
576 /**
577 * Lost password
578 */
579 public function lost_password()
580 {
581 $return = array(
582 'success' => false,
583 'errors' => array(),
584 );
585
586 if ( check_ajax_referer( 'ph_lost_password', 'security', false ) === FALSE )
587 {
588 $return['errors'][] = 'Invalid nonce';
589
590 $this->json_headers();
591 echo json_encode( $return );
592
593 // Quit out
594 die();
595 }
596
597 $email_address = sanitize_email($_POST['email_address']);
598
599 $user_data = get_user_by( 'email', $email_address );
600
601 // check email address exists
602 if ( !$user_data )
603 {
604 $return['errors'][] = 'Email address not found';
605
606 $this->json_headers();
607 echo json_encode( $return );
608
609 // Quit out
610 die();
611 }
612
613 // Send reset email
614 $to = $email_address;
615 $subject = __( 'Password Reset Request for', 'propertyhive' ) . ' ' . get_bloginfo('name');
616 $body = __( 'Someone has requested a new password for an account on', 'propertyhive' ) . ' ' . get_bloginfo('name') . ".\n\n";
617 $body .= __( 'If you didn\'t make this request you can ignore this email. If you\'d like to proceed please follow the link below', 'propertyhive' ) . ":\n\n";
618 $body .= add_query_arg( array(
619 'key' => get_password_reset_key( $user_data ),
620 'id' => $user_data->ID,
621 ), get_permalink( get_option( 'propertyhive_applicant_reset_password_page_id', '' ) ) );
622
623
624 $from = get_option('propertyhive_email_from_address', '');
625 if ( $from == '' )
626 {
627 $from = get_bloginfo('admin_email');
628 }
629
630 $headers = array();
631 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
632 $headers[] = 'Reply-To: ' . sanitize_email($from);
633 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
634
635 $headers = apply_filters( 'propertyhive_lost_password_email_headers', $headers );
636
637 wp_mail( $to, $subject, $body, $headers );
638
639 $return['success'] = true;
640
641 $this->json_headers();
642 echo json_encode( $return );
643
644 // Quit out
645 die();
646 }
647
648 /**
649 * Reset password
650 */
651 public function reset_password()
652 {
653 $return = array(
654 'success' => false,
655 'errors' => array(),
656 );
657
658 if ( check_ajax_referer( 'ph_reset_password', 'security', false ) === FALSE )
659 {
660 $return['errors'][] = 'Invalid nonce';
661
662 $this->json_headers();
663 echo json_encode( $return );
664
665 // Quit out
666 die();
667 }
668
669 // check key and user login again
670 $user = check_password_reset_key( ph_clean($_POST['reset_key']), ph_clean($_POST['reset_login']) );
671
672 // check passwords match and are strong enough
673 if ( $user instanceof WP_User )
674 {
675 $password_1 = ph_clean($_POST['password_1']);
676 $password_2 = ph_clean($_POST['password_2']);
677
678 if ( empty( $password_1 ) )
679 {
680 $return['errors'][] = __( 'Please enter your password.', 'propertyhive' );
681 }
682
683 if ( $password_1 !== $password_2 )
684 {
685 $return['errors'][] = __( 'Passwords do not match.', 'propertyhive' );
686 }
687
688 // Check password strength?
689 }
690 else
691 {
692 $return['errors'][] = __( 'This key is invalid or has already been used. Please reset your password again if needed..', 'propertyhive' );
693 }
694
695 if ( !empty($return['errors']) )
696 {
697 $this->json_headers();
698 echo json_encode( $return );
699
700 // Quit out
701 die();
702 }
703
704 // do actual reset
705 $errors = new WP_Error();
706 do_action( 'validate_password_reset', $errors, $user );
707
708 do_action( 'password_reset', $user, $password_1 );
709
710 wp_set_password( $password_1, $user->ID );
711
712 $return['success'] = true;
713
714 $this->json_headers();
715 echo json_encode( $return );
716
717 // Quit out
718 die();
719 }
720
721 /**
722 * Register applicant
723 */
724 public function applicant_registration()
725 {
726 // Validate contact
727 global $post;
728
729 $return = array(
730 'success' => false,
731 'errors' => array(),
732 );
733
734 if ( check_ajax_referer( 'ph_register', 'security', false ) === FALSE )
735 {
736 $return['errors'][] = 'Invalid nonce';
737
738 $this->json_headers();
739 echo json_encode( $return );
740
741 // Quit out
742 die();
743 }
744
745 // Validate
746 $errors = array();
747
748 $form_controls = ph_get_user_details_form_fields();
749
750 $form_controls = apply_filters( 'propertyhive_user_details_form_fields', $form_controls );
751
752 $form_controls_2 = ph_get_applicant_requirements_form_fields();
753
754 $form_controls_2 = apply_filters( 'propertyhive_applicant_requirements_form_fields', $form_controls_2, false );
755
756 $form_controls = array_merge( $form_controls, $form_controls_2 );
757
758 // need to improve this as duplicated in ph-shortcodes.php
759 if ( get_option( 'propertyhive_applicant_registration_form_disclaimer', '' ) != '' )
760 {
761 $disclaimer = get_option( 'propertyhive_applicant_registration_form_disclaimer', '' );
762
763 $form_controls['disclaimer'] = array(
764 'type' => 'checkbox',
765 'label' => $disclaimer,
766 'label_style' => 'width:100%;',
767 'required' => true
768 );
769 }
770
771 $form_controls = apply_filters( 'propertyhive_applicant_registration_form_fields', $form_controls );
772
773 $contact_post_id = false;
774
775 foreach ( $form_controls as $key => $control )
776 {
777 if ( isset( $control ) && isset( $control['required'] ) && $control['required'] === TRUE )
778 {
779 // This field is mandatory. Lets check we received it in the post
780 if ( ! isset( $_POST[$key] ) || ( isset( $_POST[$key] ) && empty( $_POST[$key] ) ) )
781 {
782 $errors[] = __( 'Missing required field', 'propertyhive' ) . ': ' . $key;
783 }
784 }
785 if ( isset( $control['type'] ) && $control['type'] == 'email' && isset( $_POST[$key] ) && ! empty( $_POST[$key] ) )
786 {
787 if ( ! is_email( $_POST[$key] ) )
788 {
789 $errors[] = __( 'Invalid email address provided', 'propertyhive' );
790 }
791 else
792 {
793 // Make sure this email address doesn't exist already
794 $args = array(
795 'post_type' => 'contact',
796 'posts_per_page' => 1,
797 'fields' => 'ids',
798 'post_status' => array( 'publish' ),
799 'meta_query' => array(
800 array(
801 'key' => '_email_address',
802 'value' => $_POST[$key]
803 )
804 )
805 );
806
807 $contacts_query = new WP_Query( $args );
808
809 if ( $contacts_query->have_posts() )
810 {
811 while ( $contacts_query->have_posts() )
812 {
813 $contacts_query->the_post();
814
815 $contact_post_id = get_the_ID();
816 }
817 //$errors[] = __( 'This email address is already registered', 'propertyhive' );
818 }
819 else
820 {
821 if ( email_exists( $_POST[$key] ) )
822 {
823 $errors[] = __( 'This email address is already registered to a user', 'propertyhive' );
824 }
825 }
826 wp_reset_postdata();
827 }
828 }
829 if ( in_array( $key, array('recaptcha', 'recaptcha-v3') ) )
830 {
831 $errors = $this->check_recaptcha_form_response($errors, $key, $control);
832 }
833
834 if ( $key == 'hCaptcha' )
835 {
836 $secret = isset( $control['secret'] ) ? $control['secret'] : '';
837 $response = isset( $_POST['h-captcha-response'] ) ? ph_clean($_POST['h-captcha-response']) : '';
838
839 $response = wp_remote_post(
840 'https://hcaptcha.com/siteverify',
841 array(
842 'method' => 'POST',
843 'body' => array( 'secret' => $secret, 'response' => $response ),
844 )
845 );
846
847 if ( is_wp_error( $response ) )
848 {
849 $errors[] = $response->get_error_message();
850 }
851 else
852 {
853 $response = json_decode($response['body'], TRUE);
854 if ( $response === FALSE )
855 {
856 $errors[] = 'Error decoding response from hCaptcha check';
857 }
858 else
859 {
860 if ( isset($response['success']) && $response['success'] == true )
861 {
862
863 }
864 else
865 {
866 $errors[] = 'Failed hCaptcha validation';
867 }
868 }
869 }
870 }
871
872 if ( $key == 'turnstile' )
873 {
874 $secret = isset( $control['secret'] ) ? $control['secret'] : '';
875 $response = isset( $_POST['cf-turnstile-response'] ) ? ph_clean($_POST['cf-turnstile-response']) : '';
876
877 $response = wp_remote_post(
878 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
879 array(
880 'method' => 'POST',
881 'headers' => array(
882 'Content-Type' => 'application/x-www-form-urlencoded',
883 ),
884 'body' => array( 'secret' => $secret, 'response' => $response ),
885 )
886 );
887
888 if ( is_wp_error( $response ) )
889 {
890 $errors[] = $response->get_error_message();
891 }
892 else
893 {
894 $response = json_decode($response['body'], TRUE);
895 if ( $response === FALSE )
896 {
897 $errors[] = 'Error decoding response from turnstile check';
898 }
899 else
900 {
901 if ( isset($response['success']) && $response['success'] == true )
902 {
903
904 }
905 else
906 {
907 $errors[] = 'Failed turnstile validation';
908 }
909 }
910 }
911 }
912 }
913
914 // Check password and password2 match
915 if ( isset( $_POST['password'] ) && isset( $_POST['password2'] ) && $_POST['password'] != $_POST['password2'] )
916 {
917 $errors[] = __( 'The passwords entered do not match', 'propertyhive' );
918 }
919
920 if ( !empty($errors) )
921 {
922 // Failed validation
923
924 $return['success'] = false;
925 $return['reason'] = 'validation';
926 $return['errors'] = $errors;
927 }
928 else
929 {
930 if ( $contact_post_id === FALSE )
931 {
932 // create CPT
933 $contact_post = array(
934 'post_title' => ph_clean($_POST['name']),
935 'post_content' => '',
936 'post_type' => 'contact',
937 'post_status' => 'publish',
938 'comment_status'=> 'closed',
939 'ping_status' => 'closed',
940 );
941
942 // Insert the post into the database
943 $contact_post_id = wp_insert_post( $contact_post );
944 }
945 else
946 {
947 // update CPT
948 $contact_post = array(
949 'ID' => $contact_post_id,
950 'post_title' => ph_clean($_POST['name']),
951 'post_status' => 'publish',
952 );
953
954 // Insert the post into the database
955 wp_update_post( $contact_post );
956 }
957
958 $forbidden_contact_methods = get_post_meta( $contact_post_id, '_forbidden_contact_methods', TRUE );
959 if ( !is_array($forbidden_contact_methods) )
960 {
961 $forbidden_contact_methods = array();
962 }
963 if ( ( $key = array_search('email', $forbidden_contact_methods) ) !== false ) {
964 unset($forbidden_contact_methods[$key]);
965 }
966 update_post_meta( $contact_post_id, '_forbidden_contact_methods', array_unique($forbidden_contact_methods) );
967
968 // Add post meta (contact details, requirements etc)
969 update_post_meta( $contact_post_id, '_email_address', sanitize_email($_POST['email_address']) );
970
971 $telephone_number = get_post_meta( $contact_post_id, '_telephone_number', TRUE );
972 if ( isset($_POST['telephone_number']) && $_POST['telephone_number'] != '' )
973 {
974 $telephone_number = $_POST['telephone_number'];
975 }
976 update_post_meta( $contact_post_id, '_telephone_number', ph_clean($telephone_number) );
977 update_post_meta( $contact_post_id, '_telephone_number_clean', ph_clean( ph_clean_telephone_number($telephone_number) ) );
978
979 $contact_types = get_post_meta( $contact_post_id, '_contact_types', TRUE );
980 if ( !is_array($contact_types) )
981 {
982 $contact_types = array();
983 }
984 if ( !in_array('applicant', $contact_types) )
985 {
986 $contact_types[] = 'applicant';
987 }
988 update_post_meta( $contact_post_id, '_contact_types', array_unique($contact_types) );
989
990 update_post_meta( $contact_post_id, '_applicant_profiles', 1 );
991
992 $applicant_profile = array();
993 $applicant_profile['department'] = $_POST['department'];
994
995 $base_department = $_POST['department'];
996 if ( !in_array( $base_department, array('residential-sales', 'residential-lettings', 'commercial') ) )
997 {
998 $base_department = ph_get_custom_department_based_on($base_department);
999 }
1000
1001 if ( $base_department == 'residential-sales' )
1002 {
1003 $price = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_price']));
1004
1005 $applicant_profile['max_price'] = $price;
1006
1007 // Not used yet but could be if introducing currencies in the future.
1008 $applicant_profile['max_price_actual'] = $price;
1009
1010 $percentage_lower = get_option( 'propertyhive_applicant_match_price_range_percentage_lower', '' );
1011 $percentage_higher = get_option( 'propertyhive_applicant_match_price_range_percentage_higher', '' );
1012
1013 if ( $percentage_lower != '' && $percentage_higher != '' && $_POST['maximum_price'] != '' && $_POST['maximum_price'] != 0 )
1014 {
1015 $price = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_price']));
1016 $applicant_profile['match_price_range_lower'] = $price - ( $price * ( $percentage_lower / 100 ) );
1017 $applicant_profile['match_price_range_lower_actual'] = $price - ( $price * ( $percentage_lower / 100 ) );
1018
1019 $applicant_profile['match_price_range_higher'] = $price + ( $price * ( $percentage_higher / 100 ) );
1020 $applicant_profile['match_price_range_higher_actual'] = $price + ( $price * ( $percentage_higher / 100 ) );
1021 }
1022 }
1023 elseif ( $base_department == 'residential-lettings' )
1024 {
1025 $price = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_rent']));
1026
1027 $applicant_profile['max_rent'] = $price;
1028 $applicant_profile['rent_frequency'] = 'pcm';
1029 $price_actual = $price; // Stored in pcm
1030 $applicant_profile['max_price_actual'] = $price_actual;
1031 }
1032
1033 if ( $base_department == 'residential-sales' || $base_department == 'residential-lettings' )
1034 {
1035 $beds = preg_replace("/[^0-9.]/", '', ph_clean($_POST['minimum_bedrooms']));
1036 $applicant_profile['min_beds'] = $beds;
1037
1038 if ( isset($_POST['property_type']) && !empty($_POST['property_type']) )
1039 {
1040 $applicant_profile['property_types'] = is_array(ph_clean($_POST['property_type'])) ? ph_clean($_POST['property_type']) : array(ph_clean($_POST['property_type']));
1041 }
1042 }
1043
1044 if ( $base_department == 'commercial' )
1045 {
1046 $available_as = array();
1047 if ( isset($_POST['available_as_sale']) && $_POST['available_as_sale'] == 'yes' )
1048 {
1049 $available_as[] = 'sale';
1050 }
1051 if ( isset($_POST['available_as_rent']) && $_POST['available_as_rent'] == 'yes' )
1052 {
1053 $available_as[] = 'rent';
1054 }
1055 $applicant_profile['available_as'] = $available_as;
1056
1057 $floor_area = preg_replace("/[^0-9.]/", '', ph_clean($_POST['minimum_floor_area']));
1058 $applicant_profile['min_floor_area'] = $floor_area;
1059 $applicant_profile['min_floor_area_actual'] = $floor_area;
1060
1061 $floor_area = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_floor_area']));
1062 $applicant_profile['max_floor_area'] = $floor_area;
1063 $applicant_profile['max_floor_area_actual'] = $floor_area;
1064
1065 if ( isset($_POST['commercial_property_type']) && !empty($_POST['commercial_property_type']) )
1066 {
1067 $applicant_profile['commercial_property_types'] = is_array(ph_clean($_POST['commercial_property_type'])) ? ph_clean($_POST['commercial_property_type']) : array(ph_clean($_POST['commercial_property_type']));
1068 }
1069 }
1070
1071 if ( isset($_POST['location']) && !empty($_POST['location']) )
1072 {
1073 $applicant_profile['locations'] = is_array(ph_clean($_POST['location'])) ? ph_clean($_POST['location']) : array(ph_clean($_POST['location']));
1074 }
1075
1076 if ( isset($_POST['location_text']) && !empty($_POST['location_text']) )
1077 {
1078 $applicant_profile['location_text'] = ph_clean($_POST['location_text']);
1079 }
1080
1081 $applicant_profile['notes'] = ( ( isset($_POST['additional_requirements']) ) ? sanitize_textarea_field($_POST['additional_requirements']) : '' );
1082
1083 $applicant_profile['send_matching_properties'] = 'yes';
1084 //$applicant_profile['auto_match_disabled'] = ''; // don't know what to do about this yet. Should probably look at global setting and reflect that
1085
1086 update_post_meta( $contact_post_id, '_applicant_profile_0', $applicant_profile );
1087
1088 if ( get_option( 'propertyhive_applicant_users', '' ) == 'yes' )
1089 {
1090 $display_name = ph_clean($_POST['name']);
1091
1092 // Create user
1093 $userdata = array(
1094 'display_name' => $display_name,
1095 'user_login' => sanitize_email($_POST['email_address']),
1096 'user_email' => sanitize_email($_POST['email_address']),
1097 'user_pass' => ph_clean($_POST['password']),
1098 'role' => 'property_hive_contact',
1099 'show_admin_bar_front' => 'false',
1100 );
1101
1102 if ( !empty($display_name) )
1103 {
1104 $name_parts = explode( ' ', $display_name );
1105
1106 if ( count($name_parts) > 1 )
1107 {
1108 $userdata['last_name'] = array_pop($name_parts);
1109 $userdata['first_name'] = implode(' ', $name_parts);
1110 }
1111 else
1112 {
1113 $userdata['last_name'] = $display_name;
1114 }
1115 }
1116
1117 $user_id = wp_insert_user( $userdata );
1118
1119 //On success
1120 if ( ! is_wp_error( $user_id ) )
1121 {
1122 // Assign user ID to CPT
1123 add_post_meta( $contact_post_id, '_user_id', $user_id );
1124
1125 $return['success'] = true;
1126
1127 wp_set_auth_cookie( $user_id, true );
1128
1129 do_action( 'propertyhive_applicant_registered', $contact_post_id, $user_id );
1130 }
1131 else
1132 {
1133 $return['success'] = false;
1134 $return['reason'] = 'validation';
1135 $return['errors'] = array('Failed to create user. You might experience issues with logging in');
1136 }
1137 }
1138 else
1139 {
1140 $return['success'] = true;
1141
1142 do_action( 'propertyhive_applicant_registered', $contact_post_id, 0 );
1143 }
1144 }
1145
1146 $this->json_headers();
1147 echo json_encode( $return );
1148
1149 // Quit out
1150 die();
1151 }
1152
1153 /**
1154 * Save account details
1155 */
1156 public function save_account_details()
1157 {
1158 global $wpdb, $current_user;
1159
1160 add_filter( 'send_email_change_email', '__return_false' );
1161
1162 $return = array(
1163 'success' => false,
1164 'errors' => array(),
1165 'new_details_nonce' => wp_create_nonce( "ph_userdetails" ),
1166 );
1167
1168 // Got an issue with nonce being declined on second submission.
1169 // Need to sort before putting this back in
1170 if ( check_ajax_referer( 'ph_userdetails', 'ph_account_details_security', false ) === FALSE )
1171 {
1172 $return['errors'][] = 'Invalid nonce';
1173
1174 $this->json_headers();
1175 echo json_encode( $return );
1176
1177 // Quit out
1178 die();
1179 }
1180
1181 // Validate
1182 $errors = array();
1183
1184 $user_id = (int) get_current_user_id();
1185 $current_user = get_user_by( 'id', $user_id );
1186 if ( $user_id <= 0 )
1187 {
1188 $return['success'] = false;
1189 $return['reason'] = 'notloggedin';
1190 $return['errors'] = array('It doesn\'t appear that you\'re logged in');
1191
1192 $this->json_headers();
1193 echo json_encode( $return );
1194
1195 // Quit out
1196 die();
1197 }
1198
1199 $form_controls = ph_get_user_details_form_fields();
1200
1201 $form_controls = apply_filters( 'propertyhive_user_details_form_fields', $form_controls );
1202
1203 foreach ( $form_controls as $key => $control )
1204 {
1205 if ( isset( $control ) && isset( $control['required'] ) && $control['required'] === TRUE )
1206 {
1207 // This field is mandatory. Lets check we received it in the post
1208 if ( ! isset( $_POST[$key] ) || ( isset( $_POST[$key] ) && empty( $_POST[$key] ) ) && $control['type'] != 'password' )
1209 {
1210 $errors[] = __( 'Missing required field', 'propertyhive' ) . ': ' . $key;
1211 }
1212 }
1213 if ( isset( $control['type'] ) && $control['type'] == 'email' && isset( $_POST[$key] ) && ! empty( $_POST[$key] ) )
1214 {
1215 if ( ! is_email( $_POST[$key] ) )
1216 {
1217 $errors[] = __( 'Invalid email address provided', 'propertyhive' );
1218 }
1219
1220 // need to see if email address is being changed and, if so, check new email address doesn't exist already
1221 }
1222 }
1223
1224 // Check password and password2 match
1225 if ( isset( $_POST['password'] ) && isset( $_POST['password2'] ) && !empty( $_POST['password'] ) && $_POST['password'] != $_POST['password2'] )
1226 {
1227 $errors[] = __( 'The passwords entered do not match', 'propertyhive' );
1228 }
1229
1230 if ( !empty($errors) )
1231 {
1232 // Failed validation
1233
1234 $return['success'] = false;
1235 $return['reason'] = 'validation';
1236 $return['errors'] = $errors;
1237 }
1238 else
1239 {
1240 $contact = new PH_Contact( '', $user_id );
1241
1242 // create CPT
1243 $contact_post = array(
1244 'ID' => $contact->id,
1245 'post_title' => ph_clean($_POST['name']),
1246 );
1247
1248 // Update the post in the database
1249 $contact_post_id = wp_update_post( $contact_post );
1250
1251 update_post_meta( $contact_post_id, '_email_address', sanitize_email($_POST['email_address']) );
1252 if (isset($_POST['telephone_number']))
1253 {
1254 update_post_meta( $contact_post_id, '_telephone_number', ph_clean($_POST['telephone_number']) );
1255 update_post_meta( $contact_post_id, '_telephone_number_clean', ph_clean(ph_clean_telephone_number($_POST['telephone_number'])) );
1256 }
1257
1258 // Update user
1259 $userdata = array(
1260 'ID' => $user_id,
1261 'display_name' => ph_clean($_POST['name']),
1262 'user_email' => sanitize_email($_POST['email_address']),
1263 );
1264
1265 if ( isset($_POST['password']) && !empty($_POST['password']) )
1266 {
1267 $userdata['user_pass'] = ph_clean($_POST['password']);
1268 }
1269
1270 $user_id = wp_update_user( $userdata );
1271
1272 $user_roles = $current_user->roles;
1273 $user_role = array_shift($user_roles);
1274
1275 if ( $user_role === 'property_hive_contact' )
1276 {
1277 // Have to update login via SQL as wp_update_user won't allow altering
1278 // Only do it for property hive contacts though as admin or editor might be viewing this page
1279 $wpdb->update($wpdb->users, array('user_login' => sanitize_email($_POST['email_address'])), array('ID' => $user_id));
1280 }
1281
1282 //On success
1283 if ( ! is_wp_error( $user_id ) )
1284 {
1285 $return['success'] = true;
1286
1287 wp_set_auth_cookie( $user_id, true );
1288
1289 do_action( 'propertyhive_account_details_updated', $contact_post_id, $user_id );
1290 }
1291 else
1292 {
1293 $return['success'] = false;
1294 $return['reason'] = 'validation';
1295 $return['errors'] = array('Failed to update user. Please try again');
1296 }
1297 }
1298
1299 $this->json_headers();
1300 echo json_encode( $return );
1301
1302 // Quit out
1303 die();
1304 }
1305
1306 /**
1307 * Save account requirements
1308 */
1309 public function save_account_requirements()
1310 {
1311 // Validate contact
1312 global $post;
1313
1314 $return = array(
1315 'success' => false,
1316 'errors' => array(),
1317 'new_requirements_nonce' => wp_create_nonce( "ph_requirements" ),
1318 );
1319
1320 // Got an issue with nonce being declined on second submission.
1321 // Need to sort before putting this back in
1322 if ( check_ajax_referer( 'ph_requirements', 'ph_account_requirements_security', false ) === FALSE )
1323 {
1324 $return['errors'][] = 'Invalid nonce';
1325
1326 $this->json_headers();
1327 echo json_encode( $return );
1328
1329 // Quit out
1330 die();
1331 }
1332
1333 // Validate
1334 $errors = array();
1335
1336 $user_id = (int) get_current_user_id();
1337 $current_user = get_user_by( 'id', $user_id );
1338 if ( $user_id <= 0 )
1339 {
1340 $return['success'] = false;
1341 $return['reason'] = 'notloggedin';
1342 $return['errors'] = array('It doesn\'t appear that you\'re logged in');
1343
1344 $this->json_headers();
1345 echo json_encode( $return );
1346
1347 // Quit out
1348 die();
1349 }
1350
1351 $contact = new PH_Contact( '', $user_id );
1352
1353 $contact_post_id = $contact->id;
1354
1355 $form_controls = ph_get_applicant_requirements_form_fields();
1356
1357 $form_controls = apply_filters( 'propertyhive_applicant_requirements_form_fields', $form_controls, get_post_meta( $contact_post_id, '_applicant_profile_' . ( isset($_POST['profile_id']) && $_POST['profile_id'] != '' ? (int)$_POST['profile_id'] : '0' ), true ) );
1358
1359 foreach ( $form_controls as $key => $control )
1360 {
1361 if ( isset( $control ) && isset( $control['required'] ) && $control['required'] === TRUE )
1362 {
1363 // This field is mandatory. Lets check we received it in the post
1364 if ( ! isset( $_POST[$key] ) || ( isset( $_POST[$key] ) && empty( $_POST[$key] ) ) )
1365 {
1366 $errors[] = __( 'Missing required field', 'propertyhive' ) . ': ' . $key;
1367 }
1368 }
1369 }
1370
1371 if ( !empty($errors) )
1372 {
1373 // Failed validation
1374
1375 $return['success'] = false;
1376 $return['reason'] = 'validation';
1377 $return['errors'] = $errors;
1378 }
1379 else
1380 {
1381 $applicant_profile = array();
1382 $applicant_profile['department'] = ph_clean($_POST['department']);
1383
1384 $base_department = $_POST['department'];
1385 if ( !in_array( $base_department, array('residential-sales', 'residential-lettings', 'commercial') ) )
1386 {
1387 $base_department = ph_get_custom_department_based_on($base_department);
1388 }
1389
1390 if ( $base_department == 'residential-sales' )
1391 {
1392 $price = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_price']));
1393
1394 $applicant_profile['max_price'] = $price;
1395
1396 // Not used yet but could be if introducing currencies in the future.
1397 $applicant_profile['max_price_actual'] = $price;
1398
1399 $percentage_lower = get_option( 'propertyhive_applicant_match_price_range_percentage_lower', '' );
1400 $percentage_higher = get_option( 'propertyhive_applicant_match_price_range_percentage_higher', '' );
1401
1402 if ( $percentage_lower != '' && $percentage_higher != '' && $_POST['maximum_price'] != '' && $_POST['maximum_price'] != 0 )
1403 {
1404 $price = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_price']));
1405 $applicant_profile['match_price_range_lower'] = $price - ( $price * ( $percentage_lower / 100 ) );
1406 $applicant_profile['match_price_range_lower_actual'] = $price - ( $price * ( $percentage_lower / 100 ) );
1407
1408 $applicant_profile['match_price_range_higher'] = $price + ( $price * ( $percentage_higher / 100 ) );
1409 $applicant_profile['match_price_range_higher_actual'] = $price + ( $price * ( $percentage_higher / 100 ) );
1410 }
1411 }
1412 elseif ( $base_department == 'residential-lettings' )
1413 {
1414 $price = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_rent']));
1415
1416 $applicant_profile['max_rent'] = $price;
1417 $applicant_profile['rent_frequency'] = 'pcm';
1418 $price_actual = $price; // Stored in pcm
1419 $applicant_profile['max_price_actual'] = $price_actual;
1420 }
1421
1422 if ( $base_department == 'residential-sales' || $base_department == 'residential-lettings' )
1423 {
1424 $beds = preg_replace("/[^0-9]/", '', ph_clean($_POST['minimum_bedrooms']));
1425 $applicant_profile['min_beds'] = $beds;
1426
1427 if ( isset($_POST['property_type']) && !empty($_POST['property_type']) )
1428 {
1429 $applicant_profile['property_types'] = is_array(ph_clean($_POST['property_type'])) ? ph_clean($_POST['property_type']) : array(ph_clean($_POST['property_type']));
1430 }
1431 }
1432
1433 if ( $base_department == 'commercial' )
1434 {
1435 $available_as = array();
1436 if ( isset($_POST['available_as_sale']) && $_POST['available_as_sale'] == 'yes' )
1437 {
1438 $available_as[] = 'sale';
1439 }
1440 if ( isset($_POST['available_as_rent']) && $_POST['available_as_rent'] == 'yes' )
1441 {
1442 $available_as[] = 'rent';
1443 }
1444 $applicant_profile['available_as'] = $available_as;
1445
1446 $floor_area = preg_replace("/[^0-9.]/", '', ph_clean($_POST['minimum_floor_area']));
1447 $applicant_profile['min_floor_area'] = $floor_area;
1448 $applicant_profile['min_floor_area_actual'] = $floor_area;
1449
1450 $floor_area = preg_replace("/[^0-9.]/", '', ph_clean($_POST['maximum_floor_area']));
1451 $applicant_profile['max_floor_area'] = $floor_area;
1452 $applicant_profile['max_floor_area_actual'] = $floor_area;
1453
1454 if ( isset($_POST['commercial_property_type']) && !empty($_POST['commercial_property_type']) )
1455 {
1456 $applicant_profile['commercial_property_types'] = is_array(ph_clean($_POST['commercial_property_type'])) ? ph_clean($_POST['commercial_property_type']) : array(ph_clean($_POST['commercial_property_type']));
1457 }
1458 }
1459
1460 if ( isset($_POST['location']) && !empty($_POST['location']) )
1461 {
1462 $applicant_profile['locations'] = is_array(ph_clean($_POST['location'])) ? ph_clean($_POST['location']) : array(ph_clean($_POST['location']));
1463 }
1464
1465 if ( isset($_POST['location_text']) && !empty($_POST['location_text']) )
1466 {
1467 $applicant_profile['location_text'] = ph_clean($_POST['location_text']);
1468 }
1469
1470 $applicant_profile['notes'] = ( ( isset($_POST['additional_requirements']) ) ? sanitize_textarea_field($_POST['additional_requirements']) : '' );
1471
1472 $applicant_profile['send_matching_properties'] = 'yes';
1473 //$applicant_profile['auto_match_disabled'] = ''; // don't know what to do about this yet. Should probably look at global setting and reflect that
1474
1475 update_post_meta( $contact_post_id, '_applicant_profile_' . ( isset($_POST['profile_id']) && $_POST['profile_id'] != '' ? (int)$_POST['profile_id'] : '0' ), $applicant_profile );
1476
1477 $return['success'] = true;
1478
1479 do_action( 'propertyhive_account_requirements_updated', $contact_post_id, $user_id );
1480 }
1481
1482 $this->json_headers();
1483 echo json_encode( $return );
1484
1485 // Quit out
1486 die();
1487 }
1488
1489 /**
1490 * Load existing features
1491 */
1492 public function load_existing_features() {
1493
1494 global $post, $wpdb;
1495
1496 $this->json_headers();
1497
1498 $return = array();
1499
1500 $property_query = new WP_Query(array(
1501 'post_type' => 'property',
1502 'post_status' => 'publish',
1503 'nopaging' => true,
1504 'fields' => 'ids',
1505 ));
1506
1507 if ($property_query->have_posts())
1508 {
1509 while ($property_query->have_posts())
1510 {
1511 $property_query->the_post();
1512
1513 $num_property_features = get_post_meta(get_the_ID(), '_features', TRUE);
1514 if ($num_property_features == '') { $num_property_features = 0; }
1515
1516 for ($i = 0; $i < $num_property_features; ++$i)
1517 {
1518 $feature = get_post_meta(get_the_ID(), '_feature_' . $i, TRUE);
1519 if (!in_array($feature, $return) && trim($feature) != '')
1520 {
1521 $return[] = $feature;
1522 }
1523 }
1524 }
1525 }
1526
1527 echo json_encode($return);
1528
1529 // Quit out
1530 die();
1531 }
1532
1533 /**
1534 * Load existing owner on property record
1535 */
1536 public function load_existing_owner_contact() {
1537
1538 check_ajax_referer( 'load-existing-owner-contact', 'security' );
1539
1540 $contact_id = (int)$_POST['contact_id'];
1541
1542 $contact = get_post($contact_id);
1543
1544 echo '<div id="existing-owner-details-' . esc_attr($contact_id) . '">';
1545
1546 if ( !is_null( $contact ) )
1547 {
1548 echo '<p class="form-field">';
1549 echo '<label>' . esc_html(__('Name', 'propertyhive')) . '</label>';
1550 echo '<a href="' . esc_url(get_edit_post_link( $contact_id )) . '">' . esc_html(get_the_title($contact_id)) . '</a>';
1551 echo '</p>';
1552
1553 $address = array();
1554 $address_elements = array( '_address_name_number', '_address_street', '_address_two', '_address_three', '_address_four', '_address_postcode' );
1555 foreach ( $address_elements as $address_element )
1556 {
1557 if ( get_post_meta($contact_id, $address_element, TRUE) != '' )
1558 {
1559 $address[] = get_post_meta($contact_id, $address_element, TRUE);
1560 }
1561 }
1562
1563 echo '<p class="form-field">';
1564 echo '<label>' . esc_html(__('Address', 'propertyhive')) . '</label>';
1565 echo ( ( !empty($address) ) ? esc_html(implode(", ", $address)) : '-' );
1566 echo '</p>';
1567
1568 echo '<p class="form-field">';
1569 echo '<label>' . esc_html(__('Telephone Number', 'propertyhive')) . '</label>';
1570 echo ( ( get_post_meta($contact_id, '_telephone_number', TRUE) != '' ) ? esc_html(get_post_meta($contact_id, '_telephone_number', TRUE)) : '-' );
1571 echo '</p>';
1572
1573 echo '<p class="form-field">';
1574 echo '<label>' . esc_html(__('Email Address', 'propertyhive')) . '</label>';
1575 echo ( ( get_post_meta($contact_id, '_email_address', TRUE) != '' ) ? esc_html(get_post_meta($contact_id, '_email_address', TRUE)) : '-' );
1576 echo '</p>';
1577
1578 $contact_solicitor_contact_id = get_post_meta( $contact_id, '_contact_solicitor_contact_id', true );
1579
1580 if ( !empty($contact_solicitor_contact_id) )
1581 {
1582 $solicitor_contact = new PH_Contact($contact_solicitor_contact_id);
1583
1584 echo '<p class="form-field">';
1585 echo '<label>' . esc_html(__('Solicitor', 'propertyhive')) . '</label>';
1586 echo '<a href="' . esc_url(get_edit_post_link($contact_solicitor_contact_id, '')) . '">' . esc_html(get_the_title($contact_solicitor_contact_id) . ( $solicitor_contact->company_name != '' && $solicitor_contact->company_name != get_the_title($contact_solicitor_contact_id) ? ' (' . $solicitor_contact->company_name . ')' : '' )) . '</a>';
1587 echo '</p>';
1588 }
1589 }
1590 else
1591 {
1592 echo esc_html(__( 'Invalid contact record', 'propertyhive' ));
1593 }
1594
1595 echo '<p class="form-field">';
1596 echo '<label></label>';
1597 echo '<a href="" class="button" id="remove-owner-contact-' . esc_attr($contact_id) . '">Remove Owner</a> ';
1598 echo '<a href="" class="button add-additional-owner-contact">Add Additional Owner</a>';
1599 echo '</p>';
1600
1601 echo '</div>';
1602
1603 // Quit out
1604 die();
1605
1606 }
1607
1608 /**
1609 * Search contacts via ajax
1610 */
1611 public function search_contacts() {
1612
1613 global $post;
1614
1615 check_ajax_referer( 'search-contacts', 'security' );
1616
1617 $return = array();
1618
1619 $keyword = ph_clean($_POST['keyword']);
1620
1621 if ( !empty( $keyword ) && strlen( $keyword ) > 2 )
1622 {
1623 // Get all contacts that match the name
1624 $args = array(
1625 'post_type' => 'contact',
1626 'nopaging' => true,
1627 'post_status' => array( 'publish', 'private' ),
1628 'fields' => 'ids'
1629 );
1630 if ( isset($_POST['contact_type']) && $_POST['contact_type'] != '' )
1631 {
1632 $args['meta_query'] = array(
1633 array(
1634 'key' => '_contact_types',
1635 'value' => ph_clean($_POST['contact_type']),
1636 'compare' => 'LIKE',
1637 )
1638 );
1639 }
1640 if ( isset($_POST['exclude_ids']) && $_POST['exclude_ids'] != '' )
1641 {
1642 $args['post__not_in'] = explode('|', $_POST['exclude_ids']);
1643 }
1644
1645 add_filter( 'posts_where', array( $this, 'search_contacts_where' ), 10, 2 );
1646
1647 $contact_query = new WP_Query( $args );
1648
1649 remove_filter( 'posts_where', array( $this, 'search_contacts_where' ) );
1650
1651 if ( $contact_query->have_posts() )
1652 {
1653 while ( $contact_query->have_posts() )
1654 {
1655 $contact_query->the_post();
1656
1657 $contact = new PH_Contact( get_the_ID() );
1658
1659 $return[] = array(
1660 'ID' => get_the_ID(),
1661 'post_title' => get_the_title(get_the_ID()) . ( isset($_POST['contact_type']) && $_POST['contact_type'] == 'thirdparty' && $contact->company_name != '' && $contact->company_name != get_the_title(get_the_ID()) ? ' (' . $contact->company_name . ')' : '' ) ,
1662 'address_name_number' => $contact->_address_name_number,
1663 'address_street' => $contact->_address_street,
1664 'address_two' => $contact->_address_two,
1665 'address_three' => $contact->_address_three,
1666 'address_four' => $contact->_address_four,
1667 'address_postcode' => $contact->_address_postcode,
1668 'address_country' => $contact->_address_country,
1669 'address_full_formatted' => $contact->get_formatted_full_address(', '),
1670 'telephone_number' => $contact->_telephone_number,
1671 'email_address' => $contact->_email_address,
1672 );
1673 }
1674 }
1675
1676 wp_reset_postdata();
1677 }
1678
1679 $this->json_headers();
1680 echo json_encode( $return );
1681
1682 // Quit out
1683 die();
1684 }
1685
1686 public function search_contacts_where( $where, $wp_query )
1687 {
1688 global $wpdb;
1689
1690 $where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . esc_sql( $wpdb->esc_like( ph_clean($_POST['keyword']) ) ) . '%\'';
1691
1692 return $where;
1693 }
1694
1695 /**
1696 * Search propertie via ajax
1697 */
1698 public function search_properties() {
1699
1700 global $post;
1701
1702 check_ajax_referer( 'search-properties', 'security' );
1703
1704 $return = array();
1705
1706 $keyword = ph_clean($_POST['keyword']);
1707
1708 if ( !empty( $keyword ) && strlen( $keyword ) > 2 )
1709 {
1710 // Get all contacts that match the name
1711 $args = array(
1712 'post_type' => 'property',
1713 'nopaging' => true,
1714 'post_status' => array( 'publish', 'draft', 'private' ),
1715 'fields' => 'ids'
1716 );
1717
1718 $meta_query = array(
1719 array(
1720 'relation' => 'OR',
1721 array(
1722 'key' => '_address_concatenated',
1723 'value' => ph_clean($_POST['keyword']),
1724 'compare' => 'LIKE'
1725 ),
1726 array(
1727 'key' => '_reference_number',
1728 'value' => ph_clean($_POST['keyword']),
1729 'compare' => '='
1730 ),
1731 ),
1732 );
1733
1734 if ( isset($_POST['department']) && $_POST['department'] != '' )
1735 {
1736 $departments_query = array(
1737 'relation' => 'OR',
1738 );
1739
1740 $explode_departments = explode("|", ph_clean($_POST['department']));
1741 $new_departments = array();
1742 foreach ( $explode_departments as $department )
1743 {
1744 $explode_department = explode("~", $department);
1745
1746 $new_departments[] = $explode_department[0];
1747
1748 $departments_sub_query = array();
1749
1750 $departments_sub_query[] = array(
1751 'key' => '_department',
1752 'value' => $explode_department[0],
1753 );
1754
1755 if ( $explode_department[0] == 'commercial' && isset($explode_department[1]) )
1756 {
1757 switch ($explode_department[1])
1758 {
1759 case "forsale":
1760 {
1761 $departments_sub_query[] = array(
1762 'key' => '_for_sale',
1763 'value' => 'yes',
1764 );
1765 break;
1766 }
1767 }
1768 }
1769
1770 $departments_query[] = $departments_sub_query;
1771 }
1772 $meta_query[] = $departments_query;
1773 }
1774
1775 if ( !empty($meta_query) )
1776 {
1777 $args['meta_query'] = $meta_query;
1778 }
1779
1780 $property_query = new WP_Query( $args );
1781
1782 if ( $property_query->have_posts() )
1783 {
1784 while ( $property_query->have_posts() )
1785 {
1786 $property_query->the_post();
1787
1788 $property = new PH_Property(get_the_ID());
1789
1790 $owner_id = $property->_owner_contact_id;
1791 $owner_name = '';
1792 if ( ( is_array($owner_id) && !empty($owner_id) ) || ( !is_array($owner_id) && $owner_id != '' ) )
1793 {
1794 if ( is_array($owner_id) )
1795 {
1796 $owner_id = reset($owner_id);
1797 }
1798 $owner_name = get_the_title($owner_id);
1799 }
1800
1801 $post_title = $property->get_formatted_full_address();
1802 if ( get_post_status() == 'draft' )
1803 {
1804 $post_title .= ' - Draft';
1805 }
1806
1807 $return[] = array(
1808 'ID' => get_the_ID(),
1809 'post_title' => $post_title,
1810 'owner_id' => $owner_id,
1811 'owner_name' => $owner_name
1812 );
1813 }
1814 }
1815
1816 wp_reset_postdata();
1817 }
1818
1819 $this->json_headers();
1820 echo json_encode( $return );
1821
1822 // Quit out
1823 die();
1824 }
1825
1826 /**
1827 * Search users/negotiators via ajax
1828 */
1829 public function search_negotiators() {
1830
1831 global $post;
1832
1833 check_ajax_referer( 'search-negotiators', 'security' );
1834
1835 $return = array();
1836
1837 $keyword = ph_clean($_POST['keyword']);
1838
1839 if ( !empty( $keyword ) && strlen( $keyword ) > 2 )
1840 {
1841 // Get all contacts that match the name
1842 $args = array(
1843 'number' => 9999,
1844 'search' => $keyword . '*',
1845 'orderby' => 'display_name',
1846 'role__not_in' => apply_filters( 'property_negotiator_exclude_roles', array('property_hive_contact', 'subscriber') )
1847 );
1848
1849 $args = apply_filters( 'propertyhive_negotiators_query', $args );
1850
1851 $user_query = new WP_User_Query( $args );
1852
1853 // Get the results
1854 $users = $user_query->get_results();
1855
1856 if ( !empty($users) )
1857 {
1858 foreach ($users as $user)
1859 {
1860 $user_data = get_userdata($user->ID);
1861
1862 $return[] = array(
1863 'ID' => $user->ID,
1864 'post_title' => $user_data->display_name
1865 );
1866 }
1867 }
1868 }
1869
1870 $this->json_headers();
1871 echo json_encode( $return );
1872
1873 // Quit out
1874 die();
1875 }
1876
1877 /**
1878 * Add note via ajax
1879 */
1880 public function add_note() {
1881
1882 check_ajax_referer( 'add-note', 'security' );
1883
1884 if ( ! current_user_can( 'manage_propertyhive' ) )
1885 wp_die( esc_html(__( 'You do not have permission to manage notes', 'propertyhive' )), 403 );
1886
1887 $post_id = (int)$_POST['post_id'];
1888
1889 if ( $post_id > 0 ) {
1890
1891 $note = trim( stripslashes( $_POST['note'] ) );
1892
1893 $pattern = '/<span [^>]*data-post-id="(\d+)"[^>]*>([^<]*)<\/span>/i';
1894 $replacement = function($matches) {
1895 $post_id = $matches[1];
1896 $text = $matches[2];
1897 return '{{mention-' . $post_id . '|' . $text . '}}';
1898 };
1899 $note = preg_replace_callback($pattern, $replacement, $note);
1900
1901 $note = str_replace( array('<br>', '<br />'), "\n", $note );
1902
1903 $note = strip_tags( $note );
1904
1905 // Add note/comment to property
1906 $comment = array(
1907 'note_type' => 'note',
1908 'note' => $note
1909 );
1910
1911 if ( isset($_POST['pinned']) )
1912 {
1913 $comment['pinned'] = '1';
1914 }
1915
1916 $comment_id = PH_Comments::insert_note( $post_id, $comment );
1917
1918 if ($comment_id !== FALSE)
1919 {
1920 $comment = get_comment($comment_id);
1921 ?>
1922 <li rel="<?php echo absint( $comment_id ) ; ?>" class="note">
1923 <div class="note_content">
1924 <?php echo wpautop( wptexturize( wp_kses_post( $note ) ) ); ?>
1925 </div>
1926 <p class="meta">
1927 <abbr class="exact-date" title="<?php echo esc_attr($comment->comment_date_gmt); ?> GMT"><?php printf( __( '%s ago', 'propertyhive' ), human_time_diff( strtotime( $comment->comment_date_gmt ), current_time( 'timestamp', 1 ) ) ); ?></abbr>
1928 <?php if ( $comment->comment_author !== __( 'Property Hive', 'propertyhive' ) ) printf( ' ' . __( 'by %s', 'propertyhive' ), $comment->comment_author ); ?>
1929 <a href="#" class="delete_note"><?php echo esc_html(__( 'Delete', 'propertyhive' )); ?></a>
1930 </p>
1931 </li>
1932 <?php
1933 }
1934 }
1935
1936 // Quit out
1937 die();
1938 }
1939
1940 /**
1941 * Delete order note via ajax
1942 */
1943 public function delete_note() {
1944
1945 check_ajax_referer( 'delete-note', 'security' );
1946
1947 if ( ! current_user_can( 'manage_propertyhive' ) )
1948 wp_send_json_error( __( 'You do not have permission to manage notes', 'propertyhive' ), 403 );
1949
1950 $note_id = (int)$_POST['note_id'];
1951
1952 if ( $note_id > 0 ) {
1953 wp_delete_comment( $note_id );
1954
1955 wp_send_json_success();
1956 }
1957
1958 wp_send_json_error();
1959 }
1960
1961 /**
1962 * Change existing note entry to be pinned
1963 */
1964 public function toggle_note_pinned() {
1965
1966 check_ajax_referer( 'pin-note', 'security' );
1967
1968 if ( ! current_user_can( 'manage_propertyhive' ) )
1969 wp_send_json_error( __( 'You do not have permission to manage notes', 'propertyhive' ), 403 );
1970
1971 $note_id = (int)$_POST['note_id'];
1972
1973 if ( $note_id > 0 ) {
1974
1975 $comment = get_comment($note_id);
1976 $comment_content = @unserialize($comment->comment_content, ['allowed_classes' => false]);
1977
1978 if ( $comment_content !== false )
1979 {
1980 if ( isset($comment_content['pinned']))
1981 {
1982 unset($comment_content['pinned']);
1983 }
1984 else
1985 {
1986 $comment_content['pinned'] = '1';
1987 }
1988 }
1989
1990 wp_update_comment( array('comment_ID' => $_POST['note_id'], 'comment_content' => serialize($comment_content)) );
1991
1992 wp_send_json_success();
1993 }
1994
1995 wp_send_json_error();
1996 }
1997
1998 public function get_notes_grid() {
1999
2000 global $wpdb, $post;
2001
2002 check_ajax_referer( 'get-notes', 'security' );
2003
2004 if ( ! current_user_can( 'manage_propertyhive' ) )
2005 wp_die( esc_html(__( 'You do not have permission to manage notes', 'propertyhive' )), 403 );
2006
2007 $post = get_post((int)$_POST['post_id']);
2008
2009 $section = $_POST['section'];
2010 include( PH()->plugin_path() . '/includes/admin/views/html-display-notes.php' );
2011
2012 // Quit out
2013 die();
2014 }
2015
2016 public function get_pinned_notes_grid() {
2017
2018 global $wpdb, $post;
2019
2020 check_ajax_referer( 'get-notes', 'security' );
2021
2022 if ( ! current_user_can( 'manage_propertyhive' ) )
2023 wp_die( esc_html(__( 'You do not have permission to manage notes', 'propertyhive' )), 403 );
2024
2025 $post = get_post((int)$_POST['post_id']);
2026
2027 $section = $_POST['section'];
2028 include( PH()->plugin_path() . '/includes/admin/views/html-display-notes.php' );
2029
2030 // Quit out
2031 die();
2032 }
2033
2034 public function fetch_note_mentions() {
2035
2036 global $wpdb;
2037
2038 check_ajax_referer( 'get-notes', 'security' );
2039
2040 if ( ! current_user_can( 'manage_propertyhive' ) )
2041 wp_die( esc_html(__( 'You do not have permission to manage notes', 'propertyhive' )), 403 );
2042
2043 $query = sanitize_text_field($_POST['query']);
2044
2045 $mentions = array();
2046
2047 // Get contacts
2048 $args = array(
2049 'post_type' => 'contact',
2050 'posts_per_page' => 10,
2051 'post_status' => array( 'publish' ),
2052 's' => $query
2053 );
2054
2055 $contacts_query = new WP_Query( $args );
2056
2057 if ( $contacts_query->have_posts() )
2058 {
2059 while ( $contacts_query->have_posts() )
2060 {
2061 $contacts_query->the_post();
2062
2063 $contact = new PH_Contact(get_the_ID());
2064
2065 $details = array();
2066 if ( $contact->get_formatted_full_address() != '' )
2067 {
2068 $details[] = $contact->get_formatted_full_address();
2069 }
2070 if ( $contact->email_address != '' || $contact->telephone_number != '' )
2071 {
2072 $sub_details = array();
2073 if ( $contact->email_address != '' )
2074 {
2075 $sub_details[] = 'E: ' . $contact->email_address;
2076 }
2077 if ( $contact->telephone_number != '' )
2078 {
2079 $sub_details[] = 'T: ' . $contact->telephone_number;
2080 }
2081 $details[] = implode(" | ", $sub_details);
2082 }
2083
2084 $mentions[] = array(
2085 'type' => 'contact',
2086 'id' => get_the_ID(),
2087 'name' => get_the_title(),
2088 'details' => implode("<br>", $details),
2089 );
2090 }
2091 }
2092 wp_reset_postdata();
2093
2094 // Get properties
2095 $args = array(
2096 'post_type' => 'property',
2097 'posts_per_page' => 10,
2098 'post_status' => array( 'publish' ),
2099 'meta_query' => array(
2100 'relation' => 'OR',
2101 array(
2102 'key' => '_address_concatenated',
2103 'value' => $query,
2104 'compare' => 'LIKE'
2105 ),
2106 array(
2107 'key' => '_reference_number',
2108 'value' => $query,
2109 'compare' => '='
2110 )
2111 )
2112 );
2113
2114 $properties_query = new WP_Query( $args );
2115
2116 if ( $properties_query->have_posts() )
2117 {
2118 while ( $properties_query->have_posts() )
2119 {
2120 $properties_query->the_post();
2121
2122 $property = new PH_Property(get_the_ID());
2123
2124 $details = array();
2125 if ( $property->get_formatted_price() != '' )
2126 {
2127 $details[] = $property->get_formatted_price();
2128 }
2129 if ( $property->property_type != '' )
2130 {
2131 $details[] = $property->property_type;
2132 }
2133
2134 $mentions[] = array(
2135 'type' => 'property',
2136 'id' => get_the_ID(),
2137 'name' => $property->get_formatted_full_address(),
2138 'details' => implode(" | ", $details),
2139 );
2140 }
2141 }
2142 wp_reset_postdata();
2143
2144 wp_send_json($mentions);
2145 }
2146
2147 /**
2148 * Delete order note via ajax
2149 */
2150 public function make_property_enquiry() {
2151
2152 global $post;
2153
2154 $return = array();
2155
2156 // Validate
2157 $errors = array();
2158 $form_controls = array();
2159
2160 if ( ! isset( $_POST['property_id'] ) || ( isset( $_POST['property_id'] ) && empty( $_POST['property_id'] ) ) )
2161 {
2162 $errors[] = __( 'Property ID is a required field and must be supplied when making an enquiry', 'propertyhive' );
2163 }
2164 else
2165 {
2166 //$post = get_post((int)$_POST['property_id']);
2167
2168 $form_controls = ph_get_property_enquiry_form_fields();
2169
2170 $form_controls = apply_filters( 'propertyhive_property_enquiry_form_fields', $form_controls, ph_clean($_POST['property_id']) );
2171 }
2172
2173 foreach ( $form_controls as $key => $control )
2174 {
2175 if ( isset( $control ) && isset( $control['required'] ) && $control['required'] === TRUE )
2176 {
2177 // This field is mandatory. Lets check we received it in the post
2178 if ( ! isset( $_POST[$key] ) || ( isset( $_POST[$key] ) && empty( $_POST[$key] ) ) )
2179 {
2180 $errors[] = __( 'Missing required field', 'propertyhive' ) . ': ' . $key;
2181 }
2182 }
2183 if ( isset( $control['type'] ) && $control['type'] == 'email' && isset( $_POST[$key] ) && ! empty( $_POST[$key] ) && ! is_email( $_POST[$key] ) )
2184 {
2185 $errors[] = __( 'Invalid email address provided', 'propertyhive' ) . ': ' . $key;
2186 }
2187 if ( in_array( $key, array('recaptcha', 'recaptcha-v3') ) )
2188 {
2189 $errors = $this->check_recaptcha_form_response($errors, $key, $control);
2190 }
2191 if ( $key == 'hCaptcha' )
2192 {
2193 $secret = isset( $control['secret'] ) ? $control['secret'] : '';
2194 $response = isset( $_POST['h-captcha-response'] ) ? ph_clean($_POST['h-captcha-response']) : '';
2195
2196 $response = wp_remote_post(
2197 'https://hcaptcha.com/siteverify',
2198 array(
2199 'method' => 'POST',
2200 'body' => array( 'secret' => $secret, 'response' => $response ),
2201 )
2202 );
2203
2204 if ( is_wp_error( $response ) )
2205 {
2206 $errors[] = $response->get_error_message();
2207 }
2208 else
2209 {
2210 $response = json_decode($response['body'], TRUE);
2211 if ( $response === FALSE )
2212 {
2213 $errors[] = __( 'Error decoding response from hCaptcha check', 'propertyhive' );
2214 }
2215 else
2216 {
2217 if ( isset($response['success']) && $response['success'] == true )
2218 {
2219
2220 }
2221 else
2222 {
2223 $errors[] = __( 'Failed hCaptcha validation', 'propertyhive' );
2224 }
2225 }
2226 }
2227 }
2228 if ( $key == 'turnstile' )
2229 {
2230 $secret = isset( $control['secret'] ) ? $control['secret'] : '';
2231 $response = isset( $_POST['cf-turnstile-response'] ) ? ph_clean($_POST['cf-turnstile-response']) : '';
2232
2233 $response = wp_remote_post(
2234 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
2235 array(
2236 'method' => 'POST',
2237 'headers' => array(
2238 'Content-Type' => 'application/x-www-form-urlencoded',
2239 ),
2240 'body' => array( 'secret' => $secret, 'response' => $response ),
2241 )
2242 );
2243
2244 if ( is_wp_error( $response ) )
2245 {
2246 $errors[] = $response->get_error_message();
2247 }
2248 else
2249 {
2250 $response = json_decode($response['body'], TRUE);
2251 if ( $response === FALSE )
2252 {
2253 $errors[] = 'Error decoding response from turnstile check';
2254 }
2255 else
2256 {
2257 if ( isset($response['success']) && $response['success'] == true )
2258 {
2259
2260 }
2261 else
2262 {
2263 $errors[] = 'Failed turnstile validation';
2264 }
2265 }
2266 }
2267 }
2268 }
2269
2270 if (
2271 get_option( 'propertyhive_property_enquiry_form_disclaimer', '' ) != '' &&
2272 (
2273 !isset( $_POST['disclaimer'] ) ||
2274 (
2275 isset( $_POST['disclaimer'] ) && empty( $_POST['disclaimer'] )
2276 )
2277 )
2278 )
2279 {
2280 $errors[] = __( 'Missing required field', 'propertyhive' ) . ': disclaimer';
2281 }
2282
2283 // Check only expected fields are received
2284 /*$allowed_keys = array_keys($form_controls);
2285 $allowed_keys[] = 'action';
2286 $allowed_keys[] = 'utm_source';
2287 $allowed_keys[] = 'utm_medium';
2288 $allowed_keys[] = 'utm_term';
2289 $allowed_keys[] = 'utm_content';
2290 $allowed_keys[] = 'utm_campaign';
2291 $allowed_keys[] = 'gclid';
2292 $allowed_keys[] = 'fbclid';
2293 $allowed_keys[] = 'property_id';
2294 $allowed_keys[] = 'disclaimer';
2295 $allowed_keys[] = 'g-recaptcha-response';
2296 $allowed_keys[] = 'h-captcha-response';
2297 $allowed_keys[] = 'cf-turnstile-response';
2298
2299 $allowed_keys = apply_filters(
2300 'propertyhive_property_enquiry_allowed_keys',
2301 $allowed_keys
2302 );
2303
2304 foreach ( $_POST as $key => $value )
2305 {
2306 if ( !in_array($key, $allowed_keys) )
2307 {
2308 // Unexpected field
2309 $errors[] = sprintf(
2310 esc_html__( 'Unexpected field %s received', 'propertyhive' ),
2311 esc_html( $key )
2312 );
2313 break;
2314 }
2315 }*/
2316
2317 // Passed validation
2318 $property_ids = array_filter( array_map( 'absint', explode( '|', sanitize_text_field( wp_unslash( $_POST['property_id'] ) ) ) ) );
2319 foreach ( $property_ids as $property_id )
2320 {
2321 if ( get_post_type($property_id) !== 'property' )
2322 {
2323 $errors[] = __( 'Invalid property supplied', 'propertyhive' );
2324 break;
2325 }
2326 }
2327
2328 if ( !empty($errors) )
2329 {
2330 // Failed validation
2331
2332 $return['success'] = false;
2333 $return['reason'] = 'validation';
2334 $return['errors'] = $errors;
2335 }
2336 else
2337 {
2338 // Get recipient email address
2339 $to = '';
2340
2341 // Try and get office's email address first, else fallback to admin email
2342 $office_id = get_post_meta((int)$property_ids[0], '_office_id', TRUE);
2343 if ( $office_id != '' )
2344 {
2345 if ( get_post_type( $office_id ) == 'office' )
2346 {
2347 $property_department = get_post_meta((int)$property_ids[0], '_department', TRUE);
2348
2349 $fields_to_check = array();
2350 switch ( $property_department )
2351 {
2352 case "residential-sales":
2353 {
2354 $fields_to_check[] = '_office_email_address_sales';
2355 $fields_to_check[] = '_office_email_address_lettings';
2356 $fields_to_check[] = '_office_email_address_commercial';
2357 break;
2358 }
2359 case "residential-lettings":
2360 {
2361 $fields_to_check[] = '_office_email_address_lettings';
2362 $fields_to_check[] = '_office_email_address_sales';
2363 $fields_to_check[] = '_office_email_address_commercial';
2364 break;
2365 }
2366 case "commercial":
2367 {
2368 $fields_to_check[] = '_office_email_address_commercial';
2369 $fields_to_check[] = '_office_email_address_lettings';
2370 $fields_to_check[] = '_office_email_address_sales';
2371 break;
2372 }
2373 default:
2374 {
2375 $fields_to_check[] = '_office_email_address_' . str_replace("residential-", "", $property_department);
2376 $fields_to_check[] = '_office_email_address_sales';
2377 $fields_to_check[] = '_office_email_address_lettings';
2378 $fields_to_check[] = '_office_email_address_commercial';
2379 break;
2380 }
2381 }
2382
2383 foreach ( $fields_to_check as $field_to_check )
2384 {
2385 $to = get_post_meta($office_id, $field_to_check, TRUE);
2386 if ( $to != '' )
2387 {
2388 break;
2389 }
2390 }
2391 }
2392 }
2393 if ( $to == '' )
2394 {
2395 $to = get_option( 'admin_email' );
2396 }
2397
2398 if ( count($property_ids) == 1 )
2399 {
2400 $subject = __( 'New Property Enquiry', 'propertyhive' ) . ': ' . get_the_title( (int)$property_ids[0] );
2401 }
2402 else
2403 {
2404 $subject = __( 'Multiple Property Enquiry', 'propertyhive' ) . ': ' . count($property_ids) . ' Properties';
2405 }
2406 $message = __( "You have received a property enquiry via your website. Please find details of the enquiry below", 'propertyhive' ) . "\n\n";
2407
2408 $message = apply_filters( 'propertyhive_property_enquiry_pre_body', $message, $property_ids );
2409
2410 $message .= ( count($property_ids) > 1 ? __( 'Properties', 'propertyhive' ) : __( 'Property', 'propertyhive' ) ) . ":\n";
2411 foreach ( $property_ids as $property_id )
2412 {
2413 $property = new PH_Property((int)$property_id);
2414 $message .= apply_filters( 'propertyhive_property_enquiry_property_output', $property->get_formatted_full_address() . "\n" . html_entity_decode(strip_tags($property->get_formatted_price())) . "\n" . get_permalink( (int)$property_id ), (int)$property_id ) . "\n\n";
2415 }
2416
2417 unset($form_controls['action']);
2418 unset($_POST['action']);
2419 unset($form_controls['property_id']); // Unset so the field doesn't get shown in the enquiry details
2420
2421 $form_controls = apply_filters( 'propertyhive_property_enquiry_body_form_fields', $form_controls );
2422
2423 foreach ($form_controls as $key => $control)
2424 {
2425 if ( isset($control['type']) && in_array($control['type'], array('html', 'recaptcha', 'recaptcha-v3', 'hCaptcha', 'turnstile')) ) { continue; }
2426
2427 $label = ( isset($control['label']) ) ? $control['label'] : $key;
2428 $label = ( isset($control['email_label']) ) ? $control['email_label'] : $label;
2429 $value = ( isset($_POST[$key]) ) ? sanitize_textarea_field($_POST[$key]) : '';
2430
2431 $message .= strip_tags($label) . ": " . strip_tags($value) . "\n";
2432 }
2433
2434 if (
2435 apply_filters('propertyhive_enquiry_email_show_manage_link', true) &&
2436 count($property_ids) == 1 &&
2437 get_option( 'propertyhive_module_disabled_enquiries', '' ) != 'yes' &&
2438 get_option( 'propertyhive_store_property_enquiries', 'yes' ) == 'yes'
2439 )
2440 {
2441 $post_type_object = get_post_type_object( 'property' );
2442 $property_enquiries_url = admin_url( sprintf( $post_type_object->_edit_link . '&action=edit', (int)$property_ids[0] ) ) . '#propertyhive-property-enquiries';
2443 $message .= "\n" . __( "To manage this enquiry please visit the following URL", 'propertyhive' ) . ':' . "\n\n";
2444 $message .= $property_enquiries_url;
2445 }
2446
2447 $message = apply_filters( 'propertyhive_property_enquiry_post_body', $message, $property_ids );
2448
2449 $from_email_address = get_option('propertyhive_email_from_address', '');
2450 if ( $from_email_address == '' )
2451 {
2452 $from_email_address = get_option('admin_email');
2453 }
2454 if ( $from_email_address == '' )
2455 {
2456 // Should never get here
2457 $from_email_address = $_POST['email_address'];
2458 }
2459
2460 $headers = array();
2461 $name = isset( $_POST['name'] )
2462 ? sanitize_text_field( wp_unslash( $_POST['name'] ) )
2463 : '';
2464
2465 $name = str_replace( array( "\r", "\n" ), '', $name );
2466
2467 $from_email_address = sanitize_email( $from_email_address );
2468
2469 if ( $name !== '' )
2470 {
2471 $headers[] = sprintf( 'From: %s <%s>', $name, $from_email_address );
2472 }
2473 else
2474 {
2475 $headers[] = sprintf( 'From: <%s>', $from_email_address );
2476 }
2477
2478 if ( isset($_POST['email_address']) )
2479 {
2480 $reply_to = sanitize_email(wp_unslash($_POST['email_address']));
2481
2482 if ( is_email($reply_to) )
2483 {
2484 $headers[] = 'Reply-To: ' . $reply_to;
2485 }
2486 }
2487
2488 $to = apply_filters( 'propertyhive_property_enquiry_to', $to, $property_ids );
2489 $subject = apply_filters( 'propertyhive_property_enquiry_subject', $subject, $property_ids );
2490 $headers = apply_filters( 'propertyhive_property_enquiry_headers', $headers, $property_ids );
2491 $message = apply_filters( 'propertyhive_property_enquiry_body', $message, $property_ids );
2492
2493 do_action( 'propertyhive_before_property_enquiry_sent' );
2494
2495 $sent = wp_mail( $to, $subject, $message, $headers );
2496
2497 do_action( 'propertyhive_after_property_enquiry_sent' );
2498
2499 if ( ! $sent )
2500 {
2501 $return['success'] = false;
2502 $return['reason'] = 'nosend';
2503 $return['errors'] = $errors;
2504 }
2505 else
2506 {
2507 $return['success'] = true;
2508
2509 $enquiry_post_id = '';
2510
2511 if ( get_option( 'propertyhive_store_property_enquiries', 'yes' ) == 'yes' )
2512 {
2513 // Now insert into enquiries section of WordPress
2514 if ( count($property_ids) == 1 )
2515 {
2516 $title = __( 'Property Enquiry', 'propertyhive' ) . ': ' . get_the_title( (int)$property_ids[0] );
2517 }
2518 else
2519 {
2520 $title = __( 'Multiple Property Enquiry', 'propertyhive' );
2521 }
2522 if ( isset($_POST['name']) && ! empty($_POST['name']) )
2523 {
2524 $title .= __( ' from ', 'propertyhive' ) . ph_clean(wp_unslash($_POST['name']));
2525 }
2526
2527 $enquiry_post = array(
2528 'post_title' => $title,
2529 'post_content' => '',
2530 'post_type' => 'enquiry',
2531 'post_status' => 'publish',
2532 'comment_status' => 'closed',
2533 'ping_status' => 'closed',
2534 );
2535
2536 // Insert the post into the database
2537 $enquiry_post_id = wp_insert_post( $enquiry_post );
2538
2539 add_post_meta( $enquiry_post_id, '_status', 'open' );
2540 add_post_meta( $enquiry_post_id, '_source', 'website' );
2541 add_post_meta( $enquiry_post_id, '_negotiator_id', '' );
2542 add_post_meta( $enquiry_post_id, '_office_id', $office_id );
2543
2544 foreach ($_POST as $key => $value)
2545 {
2546 if ( $key == 'property_id' )
2547 {
2548 foreach ( $property_ids as $property_id )
2549 {
2550 add_post_meta( $enquiry_post_id, $key, (int)$property_id );
2551 }
2552 }
2553 else
2554 {
2555 add_post_meta( $enquiry_post_id, $key, sanitize_textarea_field(wp_unslash($value)) );
2556 }
2557 }
2558 }
2559
2560 do_action('propertyhive_property_enquiry_sent', $_POST, $to, $enquiry_post_id);
2561
2562 // Send auto-responder
2563 if ( get_option( 'propertyhive_enquiry_auto_responder', '' ) == 'yes' )
2564 {
2565 // Auto-responder enabled
2566 PH()->email->send_enquiry_auto_responder( $_POST );
2567 }
2568 }
2569 }
2570
2571 $this->json_headers();
2572 echo json_encode( $return );
2573
2574 // Quit out
2575 die();
2576 }
2577
2578 /**
2579 * Create contact from enquiry
2580 */
2581 public function create_contact_from_enquiry()
2582 {
2583 global $post;
2584
2585 $enquiry_post_id = ( (isset($_POST['post_id'])) ? (int)$_POST['post_id'] : '' );
2586 $nonce = ( (isset($_POST['security'])) ? ph_clean($_POST['security']) : '' );
2587
2588 if ( ! wp_verify_nonce( $nonce, 'create-contact-from-enquiry-nonce-' . $enquiry_post_id ) )
2589 {
2590 // This nonce is not valid.
2591 die( json_encode( array('error' => 'Invalid nonce. Please refresh and try again') ) );
2592 }
2593
2594 $enquiry_meta = get_metadata( 'post', $enquiry_post_id );
2595
2596 $name = false;
2597 $email = false;
2598 $telephone = false;
2599 $address = false;
2600 $postcode = false;
2601 $property_id = false;
2602
2603 foreach ($enquiry_meta as $key => $value)
2604 {
2605 if ( strpos(strtolower($key), 'name') !== false && strpos(strtolower($key), 'property') === false && $value[0] != '' )
2606 {
2607 if ( $name === false )
2608 {
2609 $name = $value[0];
2610 }
2611 else
2612 {
2613 $name .= ' ' . $value[0];
2614 }
2615 }
2616 elseif ( strpos(strtolower($key), 'email') !== false && $value[0] != '' )
2617 {
2618 if ( $email === false )
2619 {
2620 $email = $value[0];
2621 }
2622 else
2623 {
2624 $email .= ',' . $value[0];
2625 }
2626 }
2627 elseif ( strpos(strtolower($key), 'phone') !== false && $value[0] != '' )
2628 {
2629 if ( $telephone === false )
2630 {
2631 $telephone = $value[0];
2632 }
2633 else
2634 {
2635 $telephone .= ',' . $value[0];
2636 }
2637 }
2638 elseif ( strtolower($key) == 'address' && $value[0] != '' )
2639 {
2640 $address = $value[0];
2641 }
2642 elseif ( strtolower($key) == 'postcode' && $value[0] != '' )
2643 {
2644 $postcode = $value[0];
2645 }
2646 elseif ( !$property_id && strpos(strtolower($key), 'property_id') !== false && !empty($value[0]) )
2647 {
2648 $property_id = (int)$value[0];
2649 }
2650 }
2651
2652 if ( $name === false && $email === false )
2653 {
2654 die( json_encode( array('error' => 'Name and email address not found') ) );
2655 }
2656
2657 $postdata = array(
2658 'post_excerpt' => '',
2659 'post_content' => '',
2660 'post_title' => utf8_encode(wp_strip_all_tags( $name )),
2661 'post_status' => 'publish',
2662 'post_type' => 'contact',
2663 'ping_status' => 'closed',
2664 'comment_status' => 'closed',
2665 );
2666
2667 $contact_post_id = wp_insert_post( $postdata, true );
2668
2669 if ( is_wp_error( $contact_post_id ) )
2670 {
2671 die( json_encode( array('error' => 'Error creating contact') ) );
2672 }
2673 elseif ( $contact_post_id == 0 )
2674 {
2675 die( json_encode( array('error' => 'Error creating contact') ) );
2676 }
2677
2678 update_post_meta( $enquiry_post_id, '_contact_id', $contact_post_id );
2679
2680 if ( $telephone !== FALSE ) {
2681 update_post_meta( $contact_post_id, '_telephone_number', ph_clean( ph_clean_telephone_number( $telephone ) ) );
2682 update_post_meta( $contact_post_id, '_telephone_number_clean', ph_clean( ph_clean_telephone_number($telephone) ) );
2683 }
2684
2685 if ( $email !== FALSE ) { update_post_meta( $contact_post_id, '_email_address', ph_clean( $email ) ); }
2686
2687 if ( $address !== FALSE )
2688 {
2689 if ( strpos(strtolower($address), ',') !== false )
2690 {
2691 // Split name/number and street by the first comma
2692 $address_parts = explode(',', $address, 2);
2693 update_post_meta( $contact_post_id, '_address_name_number', ph_clean( trim($address_parts[0]) ) );
2694 update_post_meta( $contact_post_id, '_address_street', ph_clean( trim($address_parts[1]) ) );
2695 }
2696 else
2697 {
2698 $address_parts = explode(' ', $address, 2);
2699 // If first "word" starts with a number (123, 1A etc), put it in name/number
2700 if ( is_numeric(substr($address_parts[0], 0, 1)) )
2701 {
2702 update_post_meta( $contact_post_id, '_address_name_number', ph_clean( trim($address_parts[0]) ) );
2703 update_post_meta( $contact_post_id, '_address_street', ph_clean( trim($address_parts[1]) ) );
2704 }
2705 else
2706 {
2707 update_post_meta( $contact_post_id, '_address_name_number', ph_clean( $address ) );
2708 }
2709 }
2710 }
2711
2712 if ( $postcode !== FALSE ) { update_post_meta( $contact_post_id, '_address_postcode', ph_clean( $postcode ) ); }
2713
2714 // Enquiry is related to a property, so create an applicant record for the contact
2715 if ( !empty( $property_id ) && get_post_type( $property_id ) == 'property' )
2716 {
2717 update_post_meta( $contact_post_id, '_applicant_profiles', '1' );
2718
2719 $applicant_profile = array();
2720 $applicant_profile['department'] = get_post_meta( $property_id, '_department', TRUE );
2721
2722 $base_department = $applicant_profile['department'];
2723 if ( !in_array( $base_department, array('residential-sales', 'residential-lettings', 'commercial') ) )
2724 {
2725 $base_department = ph_get_custom_department_based_on($base_department);
2726 }
2727
2728 if ( $base_department == 'residential-sales' )
2729 {
2730 $property_price = preg_replace("/[^0-9.]/", '', ph_clean(get_post_meta( $property_id, '_price', TRUE )));
2731
2732 if ( !empty($property_price) )
2733 {
2734 $applicant_profile['max_price'] = $property_price;
2735
2736 // Not used yet but could be if introducing currencies in the future.
2737 $applicant_profile['max_price_actual'] = $property_price;
2738
2739 $percentage_lower = get_option( 'propertyhive_applicant_match_price_range_percentage_lower', '' );
2740 $percentage_higher = get_option( 'propertyhive_applicant_match_price_range_percentage_higher', '' );
2741
2742 if ( $percentage_lower != '' && $percentage_higher != '' )
2743 {
2744 $applicant_profile['match_price_range_lower'] = $property_price - ( $property_price * ( $percentage_lower / 100 ) );
2745 $applicant_profile['match_price_range_lower_actual'] = $property_price - ( $property_price * ( $percentage_lower / 100 ) );
2746
2747 $applicant_profile['match_price_range_higher'] = $property_price + ( $property_price * ( $percentage_higher / 100 ) );
2748 $applicant_profile['match_price_range_higher_actual'] = $property_price + ( $property_price * ( $percentage_higher / 100 ) );
2749 }
2750 }
2751 }
2752 elseif ( $base_department == 'residential-lettings' )
2753 {
2754 $property_rent = preg_replace("/[^0-9.]/", '', ph_clean(get_post_meta( $property_id, '_rent', TRUE )));
2755 $property_rent_freq = get_post_meta( $property_id, '_rent_frequency', TRUE );
2756
2757 $applicant_profile['max_rent'] = $property_rent;
2758 $applicant_profile['rent_frequency'] = $property_rent_freq;
2759
2760 $price_actual = $property_rent; // Used for ordering properties. Stored in pcm
2761 switch ( $property_rent_freq )
2762 {
2763 case "pw": { $price_actual = ($property_rent * 52) / 12; break; }
2764 case "pcm": { $price_actual = $property_rent; break; }
2765 case "pq": { $price_actual = ($property_rent * 4) / 52; break; }
2766 case "pa": { $price_actual = ($property_rent / 52); break; }
2767 }
2768 $applicant_profile['max_price_actual'] = $price_actual;
2769 }
2770
2771 if ( $base_department == 'residential-sales' || $base_department == 'residential-lettings' )
2772 {
2773 $beds = preg_replace("/[^0-9]/", '', ph_clean(get_post_meta( $property_id, '_bedrooms', TRUE )));
2774 $applicant_profile['min_beds'] = $beds;
2775 }
2776
2777 if ( $base_department == 'commercial' )
2778 {
2779 $property_for_sale = get_post_meta( $property_id, '_for_sale', TRUE );
2780 $property_to_rent = get_post_meta( $property_id, '_to_rent', TRUE );
2781
2782 $available_as = array();
2783 if ( $property_for_sale == 'yes' )
2784 {
2785 $available_as[] = 'sale';
2786 }
2787 if ( $property_to_rent == 'yes' )
2788 {
2789 $available_as[] = 'rent';
2790 }
2791 $applicant_profile['available_as'] = $available_as;
2792 }
2793
2794 $applicant_profile['send_matching_properties'] = apply_filters( 'propertyhive_default_applicant_send_matching_properties', false ) === true ? 'yes' : '';
2795 $applicant_profile['auto_match_disabled'] = 'yes';
2796
2797 $applicant_profile['added_from_enquiry'] = 'yes';
2798
2799 update_post_meta( $contact_post_id, '_applicant_profile_0', $applicant_profile );
2800
2801 update_post_meta( $contact_post_id, '_contact_types', array( 'applicant' ) );
2802 }
2803
2804 do_action('propertyhive_create_contact_from_enquiry', $enquiry_post_id, $contact_post_id);
2805
2806 die( json_encode( array('success' => get_edit_post_link($contact_post_id, '')) ) );
2807 }
2808
2809 public function validate_save_contact()
2810 {
2811 global $post;
2812
2813 check_ajax_referer( 'contact-save-validation', 'security' );
2814
2815 $this->json_headers();
2816
2817 parse_str($_POST['form_data']);
2818
2819 $return = array('errors' => array());
2820
2821 if ( isset($_email_address) && $_email_address != '' )
2822 {
2823 $email_addresses = explode( ",", $_email_address );
2824
2825 foreach ( $email_addresses as $email_address )
2826 {
2827 $email_address = trim( $email_address );
2828
2829 if ( !is_email($email_address) )
2830 {
2831 $return['errors'][] = __( 'Email address is invalid', 'propertyhive' ) . ' - ' . $email_address;
2832 }
2833
2834 // Check it doesn't exist already
2835 $args = array(
2836 'post_type' => 'contact',
2837 'post_status' => 'any',
2838 'posts_per_page' => 1,
2839 'fields' => 'ids',
2840 'meta_query' => array(
2841 'relation' => 'OR',
2842 array(
2843 'key' => '_email_address',
2844 'value' => $email_address,
2845 'compare' => '='
2846 ),
2847 array(
2848 'key' => '_email_address',
2849 'value' => ',' . $email_address,
2850 'compare' => 'LIKE'
2851 )
2852 ,
2853 array(
2854 'key' => '_email_address',
2855 'value' => $email_address . ',',
2856 'compare' => 'LIKE'
2857 )
2858 )
2859 );
2860 if ( isset($post_ID) && $post_ID != '' )
2861 {
2862 $args['post__not_in'] = array( $post_ID );
2863 }
2864
2865 $contact_query = new WP_Query( $args );
2866
2867 if ( $contact_query->have_posts() )
2868 {
2869 while ( $contact_query->have_posts() )
2870 {
2871 $contact_query->the_post();
2872
2873 $return['errors'][] = __( 'A contact, ' . get_the_title() . ', already exists with email address', 'propertyhive' ) . ' ' . $email_address;
2874 }
2875 }
2876 }
2877 }
2878
2879 echo json_encode($return);
2880
2881 die();
2882 }
2883
2884 public function merge_contact_records()
2885 {
2886 $this->json_headers();
2887
2888 if ( ! isset( $_POST['nonce'] ) || ! check_ajax_referer( 'propertyhive_merge_contact', 'nonce', false ) )
2889 {
2890 $return = array('error' => 'Invalid nonce');
2891 echo json_encode( $return );
2892 die();
2893 }
2894
2895 if ( !isset( $_POST['contact_ids'] ) || empty( $_POST['contact_ids'] ) || !isset( $_POST['primary_contact_id'] ) || empty( $_POST['primary_contact_id'] ) )
2896 {
2897 $return = array('error' => 'Invalid parameters received');
2898 echo json_encode( $return );
2899 die();
2900 }
2901
2902 $contacts_to_merge = array_filter( array_map( 'absint', explode( '|', $_POST['contact_ids'] ) ) );
2903
2904 $primary_contact_id = absint( wp_unslash( $_POST['primary_contact_id'] ) );
2905
2906 if ( !is_array($contacts_to_merge) || !in_array( $primary_contact_id, $contacts_to_merge ) )
2907 {
2908 $return = array('error' => 'Invalid Contact IDs received');
2909 echo json_encode( $return );
2910 die();
2911 }
2912
2913 if ( get_post_type( $primary_contact_id ) !== 'contact' )
2914 {
2915 $return = array('error' => 'Primary contact ' . $primary_contact_id . ' is not a contact');
2916 echo json_encode( $return );
2917 die();
2918 }
2919
2920 if ( !current_user_can( 'edit_post', $primary_contact_id ) )
2921 {
2922 $return = array('error' => 'Insufficient permissions for primary contact');
2923 echo json_encode( $return );
2924 die();
2925 }
2926
2927 // Check each post ID passed through is in fact of post type 'contact'
2928 foreach ( $contacts_to_merge as $child_contact_id )
2929 {
2930 if ( get_post_type((int)$child_contact_id) !== 'contact' )
2931 {
2932 $return = array('error' => 'Contact ID ' . $child_contact_id . ' is not a contact');
2933 echo json_encode( $return );
2934 die();
2935 }
2936
2937 if ( !current_user_can( 'edit_post', $child_contact_id ) )
2938 {
2939 $return = array('error' => 'Insufficient permissions for contact ID ' . $child_contact_id );
2940 echo json_encode( $return );
2941 die();
2942 }
2943 }
2944
2945 // Remove primary from list
2946 unset($contacts_to_merge[array_search($primary_contact_id, $contacts_to_merge)]);
2947
2948 include_once( 'includes/class-ph-admin-merge-contacts.php' );
2949 $ph_admin_merge_contacts = new PH_Admin_Merge_Contacts();
2950 $ph_admin_merge_contacts->do_merge( $primary_contact_id, $contacts_to_merge );
2951
2952 echo json_encode( array('success' => true) );
2953 die();
2954 }
2955
2956 // Dashboard related functions
2957 public function get_news()
2958 {
2959 $this->json_headers();
2960
2961 include_once( ABSPATH . WPINC . '/feed.php' );
2962
2963 $return = array();
2964
2965 // Get a SimplePie feed object from the specified feed source.
2966 $rss = fetch_feed( 'https://wp-property-hive.com/category/property-hive-news/feed/' );
2967
2968 $maxitems = 0;
2969
2970 if ( ! is_wp_error( $rss ) ) : // Checks that the object is created correctly
2971
2972 // Figure out how many total items there are, but limit it to 5.
2973 $maxitems = $rss->get_item_quantity( 5 );
2974
2975 // Build an array of all the items, starting with element 0 (first element).
2976 $rss_items = $rss->get_items( 0, $maxitems );
2977
2978 foreach ( $rss_items as $item )
2979 {
2980 $return[] = array(
2981 'title' => esc_html( $item->get_title() ),
2982 'permalink' => esc_url( $item->get_permalink() ) . '?src=dashboard',
2983 'date' => $item->get_date('F d, Y')
2984 );
2985 }
2986
2987 endif;
2988
2989 echo json_encode($return);
2990
2991 die();
2992 }
2993
2994 public function get_viewings_awaiting_applicant_feedback()
2995 {
2996 global $post;
2997
2998 $this->json_headers();
2999
3000 $return = array();
3001
3002 $args = array(
3003 'post_type' => 'viewing',
3004 'fields' => 'ids',
3005 'post_status' => 'publish',
3006 'meta_query' => array(
3007 array(
3008 'key' => '_status',
3009 'value' => 'carried_out'
3010 ),
3011 array(
3012 'key' => '_feedback_status',
3013 'value' => ''
3014 )
3015 )
3016 );
3017
3018 $args = apply_filters( 'propertyhive_admin_dashboard_viewings_awaiting_applicant_feedback_args', $args );
3019
3020 $viewings_query = new WP_Query( $args );
3021
3022 if ( $viewings_query->have_posts() )
3023 {
3024 while ( $viewings_query->have_posts() )
3025 {
3026 $viewings_query->the_post();
3027
3028 $property_id = get_post_meta( get_the_ID(), '_property_id', TRUE );
3029 $property = new PH_Property((int)$property_id);
3030
3031 $applicant_contact_ids = get_post_meta( get_the_ID(), '_applicant_contact_id' );
3032
3033 $return[] = array(
3034 'ID' => get_the_ID(),
3035 'edit_link' => get_edit_post_link( get_the_ID() ),
3036 'start_date_time' => get_post_meta( get_the_ID(), '_start_date_time', TRUE ),
3037 'start_date_time_formatted_Hi_jSFY' => date("H:i jS F Y", strtotime(get_post_meta( get_the_ID(), '_start_date_time', TRUE ))),
3038 'property_id' => $property_id,
3039 'property_address' => $property->get_formatted_full_address(),
3040 'applicant_contact_id' => $applicant_contact_ids[0],
3041 'applicant_name' => get_the_title( $applicant_contact_ids[0] ),
3042 );
3043 }
3044 }
3045
3046 wp_reset_postdata();
3047
3048 echo json_encode($return);
3049
3050 die();
3051 }
3052
3053 public function get_my_upcoming_appointments()
3054 {
3055 global $post;
3056
3057 $this->json_headers();
3058
3059 $return = array();
3060
3061 $args = array(
3062 'post_type' => 'viewing',
3063 'fields' => 'ids',
3064 'post_status' => 'publish',
3065 'meta_query' => array(
3066 array(
3067 'key' => '_status',
3068 'value' => 'pending'
3069 ),
3070 array(
3071 'key' => '_start_date_time',
3072 'value' => date("Y-m-d H:i:s"),
3073 'compare' => '>='
3074 ),
3075 array(
3076 'key' => '_negotiator_id',
3077 'value' => get_current_user_id(),
3078 ),
3079 )
3080 );
3081
3082 $args = apply_filters( 'propertyhive_admin_dashboard_my_upcoming_appointments_viewing_args', $args );
3083 $args = apply_filters( 'propertyhive_admin_dashboard_my_upcoming_appointments_args', $args );
3084
3085 $viewings_query = new WP_Query( $args );
3086
3087 if ( $viewings_query->have_posts() )
3088 {
3089 while ( $viewings_query->have_posts() )
3090 {
3091 $viewings_query->the_post();
3092
3093 $property_id = get_post_meta( get_the_ID(), '_property_id', TRUE );
3094 $property = new PH_Property((int)$property_id);
3095
3096 $return[] = array(
3097 'ID' => get_the_ID(),
3098 'edit_link' => get_edit_post_link( get_the_ID() ),
3099 'start_date_time' => get_post_meta( get_the_ID(), '_start_date_time', TRUE ),
3100 'start_date_time_formatted_Hi_jSFY' => date("H:i jS F Y", strtotime(get_post_meta( get_the_ID(), '_start_date_time', TRUE ))),
3101 'start_date_time_timestamp' => strtotime(get_post_meta( get_the_ID(), '_start_date_time', TRUE )),
3102 'title' => 'Viewing at ' . $property->get_formatted_full_address(),
3103 );
3104 }
3105 }
3106
3107 wp_reset_postdata();
3108
3109 $args = array(
3110 'post_type' => 'appraisal',
3111 'fields' => 'ids',
3112 'post_status' => 'publish',
3113 'meta_query' => array(
3114 array(
3115 'key' => '_status',
3116 'value' => 'pending'
3117 ),
3118 array(
3119 'key' => '_start_date_time',
3120 'value' => date("Y-m-d H:i:s"),
3121 'compare' => '>='
3122 ),
3123 array(
3124 'key' => '_negotiator_id',
3125 'value' => get_current_user_id(),
3126 ),
3127 )
3128 );
3129
3130 $args = apply_filters( 'propertyhive_admin_dashboard_my_upcoming_appointments_appraisal_args', $args );
3131 $args = apply_filters( 'propertyhive_admin_dashboard_my_upcoming_appointments_args', $args );
3132
3133 $appraisals_query = new WP_Query( $args );
3134
3135 if ( $appraisals_query->have_posts() )
3136 {
3137 while ( $appraisals_query->have_posts() )
3138 {
3139 $appraisals_query->the_post();
3140
3141 $appraisal = new PH_Appraisal(get_the_ID());
3142
3143 $return[] = array(
3144 'ID' => get_the_ID(),
3145 'edit_link' => get_edit_post_link( get_the_ID() ),
3146 'start_date_time' => get_post_meta( get_the_ID(), '_start_date_time', TRUE ),
3147 'start_date_time_formatted_Hi_jSFY' => date("H:i jS F Y", strtotime(get_post_meta( get_the_ID(), '_start_date_time', TRUE ))),
3148 'start_date_time_timestamp' => strtotime(get_post_meta( get_the_ID(), '_start_date_time', TRUE )),
3149 'title' => 'Appraisal at ' . $appraisal->get_formatted_full_address(),
3150 );
3151 }
3152 }
3153
3154 wp_reset_postdata();
3155
3156 $return = apply_filters( 'propertyhive_dashboard_my_upcoming_appointments', $return );
3157
3158 if ( !empty($return) )
3159 {
3160 $sort = array();
3161 foreach ($return as $key => $part) {
3162 $sort[$key] = strtotime($part['start_date_time']);
3163 }
3164 array_multisort($sort, SORT_ASC, $return);
3165
3166 $return = array_slice($return, 0, 10);
3167 }
3168
3169 echo json_encode($return);
3170
3171 die();
3172 }
3173
3174 public function get_upcoming_overdue_key_dates()
3175 {
3176 global $post;
3177
3178 $this->json_headers();
3179
3180 $return = array();
3181
3182 $meta_query = array(
3183 array(
3184 'key' => '_key_date_status',
3185 'value' => 'pending',
3186 ),
3187 );
3188
3189 $upcoming_threshold = new DateTime('+ ' . apply_filters( 'propertyhive_key_date_upcoming_days', 7 ) . ' DAYS');
3190 $meta_query[] = array(
3191 'key' => '_date_due',
3192 'value' => $upcoming_threshold->format('Y-m-d'),
3193 'type' => 'date',
3194 'compare' => '<=',
3195 );
3196
3197 $args = array(
3198 'post_type' => 'key_date',
3199 'fields' => 'ids',
3200 'post_status' => 'publish',
3201 'meta_query' => $meta_query,
3202 'meta_key' => '_date_due',
3203 'orderby' => 'meta_value',
3204 'order' => 'ASC',
3205 );
3206
3207 $args = apply_filters( 'propertyhive_admin_dashboard_upcoming_overdue_key_dates_args', $args );
3208
3209 $key_dates_query = new WP_Query( $args );
3210
3211 if ( $key_dates_query->have_posts() )
3212 {
3213 while ( $key_dates_query->have_posts() )
3214 {
3215 $key_dates_query->the_post();
3216
3217 $key_date = new PH_Key_Date( get_post( get_the_ID() ) );
3218
3219 $property_id = get_post_meta( get_the_ID(), '_property_id', TRUE );
3220 $property_edit_link = '';
3221 $property_address = '';
3222 if ( !empty($property_id) )
3223 {
3224 $property = new PH_Property((int)$property_id);
3225 $property_edit_link = get_edit_post_link( $property_id );
3226 $property_address = $property->get_formatted_full_address();
3227 }
3228
3229 $tenancy_id = get_post_meta( get_the_ID(), '_tenancy_id', TRUE );
3230 if ( !empty($tenancy_id) )
3231 {
3232 $key_date_edit_link = get_edit_post_link( $tenancy_id ) . '#propertyhive-tenancy-management%7Cpropertyhive-management-dates';
3233 }
3234 else
3235 {
3236 $key_date_edit_link = $property_edit_link . '#propertyhive-property-tenancies%7Cpropertyhive-management-dates';
3237 }
3238
3239 $due_date = $key_date->date_due();
3240 $date_format = 'jS F Y';
3241 if ( $due_date->format('H:i') != '00:00' )
3242 {
3243 $date_format = 'H:i ' . $date_format;
3244 }
3245
3246 $return[] = array(
3247 'ID' => get_the_ID(),
3248 'key_date_edit_link' => $key_date_edit_link,
3249 'description' => $key_date->description(),
3250 'upcoming_overdue_status' => $key_date->status(),
3251 'property_edit_link' => $property_edit_link,
3252 'property_address' => $property_address,
3253 'due_date_time_formatted' => $due_date->format($date_format),
3254 );
3255 }
3256 }
3257
3258 wp_reset_postdata();
3259
3260 echo json_encode($return);
3261
3262 die();
3263 }
3264
3265 public function check_duplicate_reference_number()
3266 {
3267 check_ajax_referer( 'check-duplicate-reference-number', 'security' );
3268
3269 if ( !isset($_POST['reference_number']) || empty($_POST['reference_number']) )
3270 {
3271 echo '';
3272 die();
3273 }
3274
3275 $args = array(
3276 'post_type' => 'property',
3277 'post_status' => 'publish',
3278 'meta_query' => array(
3279 array(
3280 'key' => '_on_market',
3281 'value' => 'yes'
3282 ),
3283 array(
3284 'key' => '_reference_number',
3285 'value' => sanitize_text_field( wp_unslash( $_POST['reference_number'] ) )
3286 ),
3287 ),
3288 );
3289
3290 if ( isset($_POST['post_id']) && !empty($_POST['post_id']) )
3291 {
3292 $args['post__not_in'] = array((int)$_POST['post_id']);
3293 }
3294
3295 $property_query = new WP_Query($args);
3296
3297 if ( $property_query->have_posts() )
3298 {
3299 echo '1';
3300 die();
3301 }
3302
3303 echo '';
3304 die();
3305 }
3306
3307 public function osm_geocoding_request()
3308 {
3309 check_ajax_referer( 'osm_geocoding_request', 'security' );
3310
3311 $this->json_headers();
3312
3313 $lat = '';
3314 $lng = '';
3315 $error = '';
3316
3317 // Rate limit: 1 request/second
3318 $rate_key = 'ph_osm_geo_last_ts';
3319 $last_ts = (int)get_transient( $rate_key );
3320 $now = time();
3321
3322 if ( $last_ts && ($now - $last_ts) < 1 )
3323 {
3324 // Too soon: tell client to retry shortly
3325 $error = 'Too many geocoding requests. Please wait a second and try again.';
3326 json_encode(array('error' => $error));
3327 wp_die();
3328 }
3329
3330 // Set timestamp immediately to prevent stampedes
3331 set_transient( $rate_key, $now );
3332
3333 $request_url = "https://nominatim.openstreetmap.org/search?format=json&limit=1&countrycodes=" . strtolower(ph_clean($_POST['country'])) . "&addressdetails=1&q=" . urlencode(ph_clean($_POST['address']));
3334
3335 $response = wp_remote_get(
3336 $request_url,
3337 array(
3338 'headers' => array(
3339 'Referer' => home_url(),
3340 'User-Agent' => 'Property-Hive/' . PH_VERSION . ' (+https://wp-property-hive.com)',
3341 ),
3342 )
3343 );
3344
3345 if ( is_wp_error( $response ))
3346 {
3347 $error = $response->get_error_message();
3348 echo json_encode(array('error' => $error, 'lat' => $lat, 'lng' => $lng));
3349 die();
3350 }
3351
3352 if ( wp_remote_retrieve_response_code($response) !== 200 )
3353 {
3354 $error = wp_remote_retrieve_response_code($response) . ' response received when geocoding address ' . ph_clean($_POST['address']) . '. Error message: ' . wp_remote_retrieve_response_message($response);
3355 echo json_encode(array('error' => $error, 'lat' => $lat, 'lng' => $lng));
3356 die();
3357 }
3358
3359 if ( is_array( $response ) )
3360 {
3361 $body = wp_remote_retrieve_body( $response );
3362 $json = json_decode($body, true);
3363
3364 if ( !empty($json) && isset($json[0]['lat']) && isset($json[0]['lon']) )
3365 {
3366 $lat = $json[0]['lat'];
3367 $lng = $json[0]['lon'];
3368 }
3369 else
3370 {
3371 $error = 'No co-ordinates returned for the address provided ' . ph_clean($_POST['address']) . ': ' . print_r($body, true);
3372 }
3373 }
3374 else
3375 {
3376 $error = 'Failed to parse JSON response from OSM Geocoding service: ' . print_r($response, true);
3377 }
3378
3379 echo json_encode(array('error' => $error, 'lat' => $lat, 'lng' => $lng));
3380
3381 die();
3382 }
3383
3384 public function get_property_marketing_statistics_meta_box()
3385 {
3386 check_ajax_referer( 'get_property_marketing_statistics_meta_box', 'security' );
3387
3388 global $post;
3389
3390 echo '<div class="propertyhive_meta_box">';
3391
3392 echo '<div class="options_group">';
3393
3394 $view_statistics = get_post_meta( (int)$_POST['post_id'], '_view_statistics', TRUE );
3395 if ( !is_array($view_statistics) )
3396 {
3397 $view_statistics = array();
3398 }
3399
3400 $date_from = isset($_POST['statistics_date_from']) ? ph_clean($_POST['statistics_date_from']) : date("Y-m-d", strtotime('7 days ago'));
3401 $date_from = strtotime($date_from);
3402
3403 $date_to = isset($_POST['statistics_date_to']) ? ph_clean($_POST['statistics_date_to']) : date("Y-m-d");
3404 $date_to = strtotime($date_to);
3405
3406 $view_statistics_output = array();
3407 $total_views = 0;
3408
3409 for ($i = $date_from; $i <= $date_to; $i += 86400)
3410 {
3411 if ( isset($view_statistics[date("Y-m-d", $i)]) )
3412 {
3413 $view_statistics_output[] = array( $i * 1000, $view_statistics[date("Y-m-d", $i)] );
3414 $total_views += $view_statistics[date("Y-m-d", $i)];
3415 }
3416 else
3417 {
3418 $view_statistics_output[] = array( $i * 1000, 0 );
3419 }
3420 }
3421
3422 echo '<h3>' . esc_html(__( 'Views On Website', 'propertyhive' )) . ' (' . esc_html(number_format($total_views, 0)) . ')</h3>';
3423
3424 echo '<div id="marketing_statistics_website_view_graph" style="height:400px; width:100%;"></div>';
3425
3426 echo '</div>';
3427
3428 echo '</div>';
3429
3430 echo '<input type="hidden" name="marketing_statistics" id="marketing_statistics" value="' . esc_attr(json_encode($view_statistics_output)) . '">';
3431
3432 die();
3433 }
3434
3435 public function get_appraisal_details_meta_box()
3436 {
3437 global $post;
3438
3439 check_ajax_referer( 'appraisal-details-meta-box', 'security' );
3440
3441 $post = get_post((int)$_POST['appraisal_id']);
3442
3443 $appraisal = new PH_Appraisal((int)$_POST['appraisal_id']);
3444
3445 echo '<div class="propertyhive_meta_box">';
3446
3447 echo '<div class="options_group">';
3448
3449 echo '<p class="form-field">
3450
3451 <label for="">' . esc_html(__('Status', 'propertyhive')) . '</label>
3452
3453 ' . esc_html(ucwords(str_replace("_", " ", $appraisal->status)));
3454
3455 echo '</p>';
3456
3457 if ( $appraisal->status == 'cancelled' )
3458 {
3459 $args = array(
3460 'id' => '_cancelled_reason',
3461 'label' => __( 'Reason Cancelled', 'propertyhive' ),
3462 'desc_tip' => false,
3463 'class' => '',
3464 'value' => $appraisal->cancelled_reason,
3465 'custom_attributes' => array(
3466 'style' => 'width:95%; max-width:500px;'
3467 )
3468 );
3469 propertyhive_wp_textarea_input( $args );
3470 }
3471
3472 if ( $appraisal->status == 'carried_out' || $appraisal->status == 'won' || $appraisal->status == 'instructed' )
3473 {
3474 $ph_countries = new PH_Countries();
3475
3476 $currency = 'GBP';
3477 $currency_symbol = '&pound;';
3478
3479 $default_country = get_option( 'propertyhive_default_country', 'GB' );
3480 $countries = get_option( 'propertyhive_countries', array( $default_country ) );
3481 if ( count($countries) == 1 )
3482 {
3483 foreach ( $countries as $country )
3484 {
3485 $country = $ph_countries->get_country( $country );
3486
3487 $currency = $country['currency_code'];
3488 }
3489 }
3490
3491 $currency = $ph_countries->get_currency( $currency );
3492 if ( isset($currency['currency_symbol']) )
3493 {
3494 $currency_symbol = $currency['currency_symbol'];
3495 }
3496
3497 if ( $appraisal->department == 'residential-sales' )
3498 {
3499 $args = array(
3500 'id' => '_valued_price',
3501 'label' => __( 'Valued Price', 'propertyhive' ) . ' (' . $currency_symbol . ')',
3502 'desc_tip' => false,
3503 'class' => 'short',
3504 'value' => ph_display_price_field( $appraisal->valued_price ),
3505 );
3506 propertyhive_wp_text_input( $args );
3507 }
3508 elseif ( $appraisal->department == 'residential-lettings' )
3509 {
3510 $rent_frequency = $appraisal->valued_rent_frequency;
3511
3512 echo '<p class="form-field">
3513
3514 <label for="">' . esc_html(__('Valued Rent', 'propertyhive')) . ' (' . esc_html($currency_symbol) . ')</label>
3515
3516 <input type="text" class="" name="_valued_rent" id="_valued_rent" value="' . esc_attr(ph_display_price_field( $appraisal->valued_rent )) . '" placeholder="" style="width:10%; min-width:100px;">
3517
3518 <select id="_valued_rent_frequency" name="_valued_rent_frequency" class="select" style="width:auto">
3519 <option value="pd"' . ( ($rent_frequency == 'pd') ? ' selected' : '') . '>' . esc_html(__('Per Day', 'propertyhive')) . '</option>
3520 <option value="pppw"' . ( ($rent_frequency == 'pppw') ? ' selected' : '') . '>' . esc_html(__('Per Person Per Week', 'propertyhive')) . '</option>
3521 <option value="pw"' . ( ($rent_frequency == 'pw') ? ' selected' : '') . '>' . esc_html(__('Per Week', 'propertyhive')) . '</option>
3522 <option value="pcm"' . ( ($rent_frequency == 'pcm' || $rent_frequency == '') ? ' selected' : '') . '>' . esc_html(__('Per Calendar Month', 'propertyhive')) . '</option>
3523 <option value="pq"' . ( ($rent_frequency == 'pq') ? ' selected' : '') . '>' . esc_html(__('Per Quarter', 'propertyhive')) . '</option>
3524 <option value="pa"' . ( ($rent_frequency == 'pa') ? ' selected' : '') . '>' . esc_html(__('Per Annum', 'propertyhive')) . '</option>
3525 </select>
3526
3527 </p>';
3528 }
3529 }
3530
3531 if ( $appraisal->status == 'lost' )
3532 {
3533 $args = array(
3534 'id' => '_lost_reason',
3535 'label' => __( 'Reason Lost', 'propertyhive' ),
3536 'desc_tip' => false,
3537 'class' => '',
3538 'value' => $appraisal->lost_reason,
3539 'custom_attributes' => array(
3540 'style' => 'width:95%; max-width:500px;'
3541 )
3542 );
3543 propertyhive_wp_textarea_input( $args );
3544 }
3545
3546 do_action('propertyhive_appraisal_details_fields');
3547
3548 echo '</div>';
3549
3550 echo '</div>';
3551
3552 die();
3553 }
3554
3555 public function get_appraisal_actions()
3556 {
3557 check_ajax_referer( 'appraisal-actions', 'security' );
3558
3559 $post_id = (int)$_POST['appraisal_id'];
3560
3561 $status = get_post_meta( $post_id, '_status', TRUE );
3562 $department = get_post_meta( $post_id, '_department', TRUE );
3563
3564 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="propertyhive_appraisal_actions_meta_box">
3565
3566 <div class="options_group" style="padding-top:8px;">';
3567
3568 $show_cancelled_meta_boxes = false;
3569 $show_carried_out_meta_boxes = false;
3570 $show_instructed_meta_boxes = false;
3571 $show_lost_meta_boxes = false;
3572 $show_customise_confirmation_meta_boxes = false;
3573
3574 $actions = array();
3575
3576 if ( $status == 'pending' )
3577 {
3578 $owner_booking_confirmation_sent_at = get_post_meta( $post_id, '_owner_booking_confirmation_sent_at', TRUE );
3579
3580 $appraisal_department = get_post_meta( $post_id, '_department', TRUE );
3581 $owner_contact_id = get_post_meta( $post_id, '_property_owner_contact_id', TRUE );
3582 $owner_or_landlord = ( $appraisal_department == 'residential-lettings' ? 'Landlord' : 'Owner' );
3583
3584 if ( !empty($owner_contact_id) )
3585 {
3586 if ( get_option( 'propertyhive_customise_confirmation_emails', '' ) == 'yes' )
3587 {
3588 $actions[] = '<a
3589 href="#action_panel_appraisal_email_owner_booking_confirmation_customise"
3590 class="button appraisal-action"
3591 style="width:100%; margin-bottom:7px; text-align:center"
3592 >' . ( ( $owner_booking_confirmation_sent_at == '' ) ? esc_html(__('Email ' . $owner_or_landlord . ' Booking Confirmation', 'propertyhive')) : esc_html(__('Re-Email ' . $owner_or_landlord . ' Booking Confirmation', 'propertyhive') ) ) . '</a>';
3593
3594 $show_customise_confirmation_meta_boxes = true;
3595 }
3596 else
3597 {
3598 $actions[] = '<a
3599 href="#action_panel_appraisal_email_owner_booking_confirmation"
3600 class="button appraisal-action"
3601 style="width:100%; margin-bottom:7px; text-align:center"
3602 >' . ( ( $owner_booking_confirmation_sent_at == '' ) ? esc_html(__('Email ' . $owner_or_landlord . ' Booking Confirmation', 'propertyhive')) : esc_html(__('Re-Email ' . $owner_or_landlord . ' Booking Confirmation', 'propertyhive') )) . '</a>';
3603 }
3604
3605 $actions[] = '<div id="appraisal_owner_confirmation_date" style="text-align:center; font-size:12px; color:#999; margin-bottom:7px;' . ( ( $owner_booking_confirmation_sent_at == '' ) ? 'display:none' : '' ) . '">' . ( ( $owner_booking_confirmation_sent_at != '' ) ? 'Previously sent to ' . esc_html(strtolower($owner_or_landlord)) . ' on <span title="' . esc_attr($owner_booking_confirmation_sent_at) . '">' . esc_html(date("jS F", strtotime($owner_booking_confirmation_sent_at))) . '</span>' : '' ) . '</div>';
3606
3607 $actions[] = '<hr>';
3608 }
3609
3610 /*$actions[] = '<a
3611 href=""
3612 class="button"
3613 style="width:100%; margin-bottom:7px; text-align:center"
3614 >' . __('Print Market Appraisal Sheet', 'propertyhive') . '</a>';
3615 if ( get_option('propertyhive_module_disabled_contacts', '') != 'yes' )
3616 {
3617 $actions[] = '<a
3618 href=""
3619 class="button"
3620 style="width:100%; margin-bottom:7px; text-align:center"
3621 >' . __('Run Potential Applicant Match', 'propertyhive') . '</a>';
3622 }*/
3623 $actions[] = '<a
3624 href="#action_panel_appraisal_carried_out"
3625 class="button button-success appraisal-action"
3626 style="width:100%; margin-bottom:7px; text-align:center"
3627 >' . esc_html(__('Appraisal Carried Out', 'propertyhive')) . '</a>';
3628 $actions[] = '<a
3629 href="#action_panel_appraisal_cancelled"
3630 class="button appraisal-action"
3631 style="width:100%; margin-bottom:7px; text-align:center"
3632 >' . esc_html(__('Appraisal Cancelled', 'propertyhive')) . '</a>';
3633
3634 $show_cancelled_meta_boxes = true;
3635 $show_carried_out_meta_boxes = true;
3636 }
3637
3638 if ( $status == 'carried_out' )
3639 {
3640 $actions[] = '<a
3641 href="#action_panel_appraisal_won"
3642 class="button button-success appraisal-action"
3643 style="width:100%; margin-bottom:7px; text-align:center"
3644 >' . esc_html(__('Appraisal Won', 'propertyhive')) . '</a>';
3645
3646 $actions[] = '<a
3647 href="#action_panel_appraisal_lost"
3648 class="button button-danger appraisal-action"
3649 style="width:100%; margin-bottom:7px; text-align:center"
3650 >' . esc_html(__('Appraisal Lost', 'propertyhive')) . '</a>';
3651
3652 $show_lost_meta_boxes = true;
3653 }
3654
3655 if ( $status == 'won' )
3656 {
3657 $actions[] = '<a
3658 href="#action_panel_appraisal_instruct"
3659 class="button button-success appraisal-action"
3660 style="width:100%; margin-bottom:7px; text-align:center"
3661 >' . esc_html(__('Instruct Property', 'propertyhive')) . '</a>';
3662
3663 $show_instructed_meta_boxes = true;
3664 }
3665
3666 if ( $status == 'won' || $status == 'lost' )
3667 {
3668 $actions[] = '<a
3669 href="#action_panel_appraisal_revert_carried_out"
3670 class="button appraisal-action"
3671 style="width:100%; margin-bottom:7px; text-align:center"
3672 >' . esc_html(__('Revert To Carried Out', 'propertyhive')) . '</a>';
3673 }
3674
3675 if ( $status == 'instructed' )
3676 {
3677 $property_id = get_post_meta( $post_id, '_property_id', TRUE );
3678
3679 $actions[] = '<a
3680 href="' . esc_url(get_edit_post_link($property_id)) . '"
3681 class="button"
3682 style="width:100%; margin-bottom:7px; text-align:center"
3683 >' . esc_html(__('View Instructed Property', 'propertyhive')) . '</a>';
3684
3685 /*$actions[] = '<a
3686 href="#action_panel_appraisal_revert_won"
3687 class="button appraisal-action"
3688 style="width:100%; margin-bottom:7px; text-align:center"
3689 >' . __('Revert To Won', 'propertyhive') . '</a>';*/
3690 }
3691
3692 if ( $status == 'carried_out' || $status == 'cancelled' )
3693 {
3694 $actions[] = '<a
3695 href="#action_panel_appraisal_revert_pending"
3696 class="button appraisal-action"
3697 style="width:100%; margin-bottom:7px; text-align:center"
3698 >' . esc_html(__('Revert To Pending', 'propertyhive')) . '</a>';
3699 }
3700
3701 $actions = apply_filters( 'propertyhive_admin_appraisal_actions', $actions, $post_id );
3702 $actions = apply_filters( 'propertyhive_admin_post_actions', $actions, $post_id );
3703
3704 if ( !empty($actions) )
3705 {
3706 echo implode("", $actions);
3707 }
3708 else
3709 {
3710 echo '<div style="text-align:center">' . esc_html(__( 'No actions to display', 'propertyhive' )) . '</div>';
3711 }
3712
3713 echo '</div>
3714
3715 </div>';
3716
3717 // Success action panel
3718 echo '<div id="action_panel_success" class="propertyhive_meta_box propertyhive_meta_box_actions" style="display:none;">
3719
3720 <div class="options_group" style="padding-top:8px;">
3721
3722 <div id="success_actions"></div>
3723
3724 <a class="button action-cancel" style="width:100%;" href="#">' . esc_html(__( 'Back To Actions', 'propertyhive' )) . '</a>
3725
3726 </div>
3727
3728 </div>';
3729
3730 do_action( 'propertyhive_admin_appraisal_action_options', $post_id );
3731 do_action( 'propertyhive_admin_post_action_options', $post_id );
3732
3733 if ( $show_customise_confirmation_meta_boxes )
3734 {
3735 $subject = get_option( 'propertyhive_appraisal_owner_booking_confirmation_email_subject', '' );
3736 $body = get_option( 'propertyhive_appraisal_owner_booking_confirmation_email_body', '' );
3737
3738 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="action_panel_appraisal_email_owner_booking_confirmation_customise" style="display:none;">
3739
3740 <div class="options_group" style="padding-top:8px;">
3741
3742 <div class="form-field">
3743
3744 <label for="_owner_confirmation_email_subject">' . esc_html(__( 'Subject', 'propertyhive' )) . '</label>
3745
3746 <input id="_owner_confirmation_email_subject" name="_owner_confirmation_email_subject" style="width:100%;" value="' . esc_attr($subject) . '">
3747
3748 </div>
3749
3750 <div class="form-field">
3751
3752 <label for="_owner_confirmation_email_body">' . esc_html(__( 'Body', 'propertyhive' )) . '</label>
3753
3754 <textarea id="_owner_confirmation_email_body" name="_owner_confirmation_email_body" style="width:100%; height:100px;">' . esc_html($body) . '</textarea>
3755
3756 </div>
3757
3758 <a class="button action-cancel" href="#">' . esc_html(__( 'Cancel', 'propertyhive' )) . '</a>
3759 <a class="button button-primary owner-booking-confirmation-action-submit" href="#">' . esc_html(__( 'Send', 'propertyhive' )) . '</a>
3760
3761 </div>
3762
3763 </div>';
3764 }
3765
3766 if ( $show_cancelled_meta_boxes )
3767 {
3768 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="action_panel_appraisal_cancelled" style="display:none;">
3769
3770 <div class="options_group" style="padding-top:8px;">
3771
3772 <div class="form-field">
3773
3774 <label for="_appraisal_cancelled_reason">' . esc_html(__( 'Reason Cancelled', 'propertyhive' )) . '</label>
3775
3776 <textarea id="_cancelled_reason" name="_cancelled_reason" style="width:100%;">' . esc_html(get_post_meta( $post_id, '_cancelled_reason', TRUE )) . '</textarea>
3777
3778 </div>
3779
3780 <a class="button action-cancel" href="#">' . esc_html(__( 'Cancel', 'propertyhive' )) . '</a>
3781 <a class="button button-primary cancelled-reason-action-submit" href="#">' . esc_html(__( 'Save', 'propertyhive' )) . '</a>
3782
3783 </div>
3784
3785 </div>';
3786 }
3787
3788 if ( $show_carried_out_meta_boxes )
3789 {
3790 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="action_panel_appraisal_carried_out" style="display:none;">
3791
3792 <div class="options_group" style="padding-top:8px;">';
3793
3794 $ph_countries = new PH_Countries();
3795
3796 $currency = 'GBP';
3797 $currency_symbol = '&pound;';
3798
3799 $default_country = get_option( 'propertyhive_default_country', 'GB' );
3800 $countries = get_option( 'propertyhive_countries', array( $default_country ) );
3801 if ( count($countries) == 1 )
3802 {
3803 foreach ( $countries as $country )
3804 {
3805 $country = $ph_countries->get_country( $country );
3806
3807 $currency = $country['currency_code'];
3808 }
3809 }
3810
3811 $currency = $ph_countries->get_currency( $currency );
3812 if ( isset($currency['currency_symbol']) )
3813 {
3814 $currency_symbol = $currency['currency_symbol'];
3815 }
3816
3817 if ( $department == 'residential-sales' )
3818 {
3819 echo '<div class="form-field">
3820
3821 <label for="_price">' . esc_html(__( 'Valued Price (' . $currency_symbol . ')', 'propertyhive' )) . '</label>
3822
3823 <input type="text" id="_price" name="_price" style="width:100%;" value="' . esc_attr(get_post_meta( $post_id, '_valued_price', TRUE )) . '">
3824
3825 </div>';
3826 }
3827 else
3828 {
3829 $rent_frequency = get_post_meta( $post_id, '_valued_rent_frequency', TRUE );
3830 echo '<div class="form-field">
3831
3832 <label for="_price">' . esc_html(__( 'Valued Rent (' . $currency_symbol . ')', 'propertyhive' )) . '</label>
3833
3834 <input type="text" id="_price" name="_price" style="width:100%;" value="' . esc_attr(get_post_meta( $post_id, '_valued_rent', TRUE )) . '">
3835
3836 <select id="_rent_frequency" name="_rent_frequency" class="select" style="width:100%">
3837 <option value="pd"' . ( ($rent_frequency == 'pd') ? ' selected' : '') . '>' . esc_html(__('Per Day', 'propertyhive')) . '</option>
3838 <option value="pppw"' . ( ($rent_frequency == 'pppw') ? ' selected' : '') . '>' . esc_html(__('Per Person Per Week', 'propertyhive')) . '</option>
3839 <option value="pw"' . ( ($rent_frequency == 'pw') ? ' selected' : '') . '>' . esc_html(__('Per Week', 'propertyhive')) . '</option>
3840 <option value="pcm"' . ( ($rent_frequency == 'pcm' || $rent_frequency == '') ? ' selected' : '') . '>' . esc_html(__('Per Calendar Month', 'propertyhive')) . '</option>
3841 <option value="pq"' . ( ($rent_frequency == 'pq') ? ' selected' : '') . '>' . esc_html(__('Per Quarter', 'propertyhive')) . '</option>
3842 <option value="pa"' . ( ($rent_frequency == 'pa') ? ' selected' : '') . '>' . esc_html(__('Per Annum', 'propertyhive')) . '</option>
3843 </select>
3844
3845 </div>';
3846 }
3847
3848 echo '<a class="button action-cancel" href="#">' . esc_html(__( 'Cancel', 'propertyhive' )) . '</a>
3849 <a class="button button-primary carried-out-action-submit" href="#">' . esc_html(__( 'Save', 'propertyhive' )) . '</a>
3850
3851 </div>
3852
3853 </div>';
3854 }
3855
3856 if ( $show_instructed_meta_boxes )
3857 {
3858 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="action_panel_appraisal_instruct" style="display:none;">
3859
3860 <div class="options_group" style="padding-top:8px;">';
3861
3862 echo '<div style="margin-bottom:13px;">' . esc_html(__( 'Upon instruction a new property record will be created within the \'Properties\' area.', 'propertyhive' )) . '</div>';
3863
3864 echo '<a class="button action-cancel" href="#">' . esc_html(__( 'Cancel', 'propertyhive' )) . '</a>
3865 <a class="button button-primary instructed-action-submit" href="#">' . esc_html(__( 'OK', 'propertyhive' )) . '</a>
3866
3867 </div>
3868
3869 </div>';
3870 }
3871
3872 if ( $show_lost_meta_boxes )
3873 {
3874 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="action_panel_appraisal_lost" style="display:none;">
3875
3876 <div class="options_group" style="padding-top:8px;">
3877
3878 <div class="form-field">
3879
3880 <label for="_lost_reason">' . esc_html(__( 'Reason Lost', 'propertyhive' )) . '</label>
3881
3882 <textarea id="_lost_reason" name="_lost_reason" style="width:100%;">' . esc_html(get_post_meta( $post_id, '_lost_reason', TRUE )) . '</textarea>
3883
3884 </div>
3885
3886 <a class="button action-cancel" href="#">' . esc_html(__( 'Cancel', 'propertyhive' )) . '</a>
3887 <a class="button button-primary lost-reason-action-submit" href="#">' . esc_html( __( 'Save Reason Lost', 'propertyhive' ) ) . '</a>
3888
3889 </div>
3890
3891 </div>';
3892 }
3893
3894 die();
3895 }
3896
3897 public function appraisal_carried_out()
3898 {
3899 check_ajax_referer( 'appraisal-actions', 'security' );
3900
3901 $post_id = (int)$_POST['appraisal_id'];
3902
3903 $status = get_post_meta( $post_id, '_status', TRUE );
3904
3905 if ( $status == 'pending' )
3906 {
3907 update_post_meta( $post_id, '_status', 'carried_out' );
3908
3909 if ( get_post_meta( $post_id, '_department', TRUE ) == 'residential-sales' )
3910 {
3911 $price = preg_replace("/[^0-9.]/", '', ph_clean($_POST['price']));
3912 update_post_meta( $post_id, '_valued_price', $price );
3913 update_post_meta( $post_id, '_valued_price_actual', $price );
3914 }
3915 elseif ( get_post_meta( $post_id, '_department', TRUE ) == 'residential-lettings' )
3916 {
3917 $rent = preg_replace("/[^0-9.]/", '', ph_clean($_POST['rent']));
3918 update_post_meta( $post_id, '_valued_rent', $rent );
3919
3920 update_post_meta( $post_id, '_valued_rent_frequency', ph_clean($_POST['rent_frequency']) );
3921
3922 switch (ph_clean($_POST['rent_frequency']))
3923 {
3924 case "pd": { $price = ($rent * 365) / 12; break; }
3925 case "pppw":
3926 {
3927 $bedrooms = get_post_meta( $postID, '_bedrooms', true );
3928 if ( ( $bedrooms !== FALSE && $bedrooms != 0 && $bedrooms != '' ) && apply_filters( 'propertyhive_pppw_to_consider_bedrooms', true ) == true )
3929 {
3930 $price = (($rent * 52) / 12) * $bedrooms;
3931 }
3932 else
3933 {
3934 $price = ($rent * 52) / 12;
3935 }
3936 break;
3937 }
3938 case "pw": { $price = ($rent * 52) / 12; break; }
3939 case "pcm": { $price = $rent; break; }
3940 case "pq": { $price = ($rent * 4) / 12; break; }
3941 case "pa": { $price = ($rent / 12); break; }
3942 }
3943 update_post_meta( $post_id, '_valued_price_actual', $price );
3944 }
3945
3946 // Add note/comment to appraisal
3947 $comment = array(
3948 'note_type' => 'action',
3949 'action' => 'appraisal_carried_out',
3950 );
3951
3952 PH_Comments::insert_note( $post_id, $comment );
3953
3954 wp_send_json_success();
3955 }
3956
3957 wp_send_json_success();
3958 }
3959
3960 public function appraisal_cancelled()
3961 {
3962 check_ajax_referer( 'appraisal-actions', 'security' );
3963
3964 $post_id = (int)$_POST['appraisal_id'];
3965
3966 $status = get_post_meta( $post_id, '_status', TRUE );
3967
3968 if ( $status == 'pending' )
3969 {
3970 update_post_meta( $post_id, '_status', 'cancelled' );
3971 update_post_meta( $post_id, '_cancelled_reason', sanitize_textarea_field( $_POST['cancelled_reason'] ) );
3972
3973 // Add note/comment to appraisal
3974 $comment = array(
3975 'note_type' => 'action',
3976 'action' => 'appraisal_cancelled',
3977 );
3978
3979 PH_Comments::insert_note( $post_id, $comment );
3980
3981 wp_send_json_success();
3982 }
3983
3984 wp_send_json_error();
3985 }
3986
3987 public function appraisal_won()
3988 {
3989 check_ajax_referer( 'appraisal-actions', 'security' );
3990
3991 $post_id = (int)$_POST['appraisal_id'];
3992
3993 $status = get_post_meta( $post_id, '_status', TRUE );
3994
3995 if ( $status == 'carried_out' )
3996 {
3997 update_post_meta( $post_id, '_status', 'won' );
3998
3999 // Add note/comment to appraisal
4000 $comment = array(
4001 'note_type' => 'action',
4002 'action' => 'appraisal_won',
4003 );
4004
4005 PH_Comments::insert_note( $post_id, $comment );
4006
4007 wp_send_json_success();
4008 }
4009
4010 wp_send_json_error();
4011 }
4012
4013 public function appraisal_lost_reason()
4014 {
4015 check_ajax_referer( 'appraisal-actions', 'security' );
4016
4017 $post_id = (int)$_POST['appraisal_id'];
4018
4019 $status = get_post_meta( $post_id, '_status', TRUE );
4020
4021 if ( $status == 'carried_out' )
4022 {
4023 update_post_meta( $post_id, '_status', 'lost' );
4024 update_post_meta( $post_id, '_lost_reason', sanitize_textarea_field( $_POST['lost_reason'] ) );
4025
4026 // Add note/comment to appraisal
4027 $comment = array(
4028 'note_type' => 'action',
4029 'action' => 'appraisal_lost',
4030 );
4031
4032 PH_Comments::insert_note( $post_id, $comment );
4033
4034 wp_send_json_success();
4035 }
4036
4037 wp_send_json_error();
4038 }
4039
4040 public function appraisal_instructed()
4041 {
4042 check_ajax_referer( 'appraisal-actions', 'security' );
4043
4044 $post_id = (int)$_POST['appraisal_id'];
4045
4046 $status = get_post_meta( $post_id, '_status', TRUE );
4047
4048 if ( $status == 'won' )
4049 {
4050 // Create property record and copy everything over
4051 $display_address = array();
4052 if ( get_post_meta( $post_id, '_address_street', TRUE ) != '' )
4053 {
4054 $display_address[] = get_post_meta( $post_id, '_address_street', TRUE );
4055 }
4056 if ( get_post_meta( $post_id, '_address_two', TRUE ) != '' )
4057 {
4058 $display_address[] = get_post_meta( $post_id, '_address_two', TRUE );
4059 }
4060 if ( get_post_meta( $post_id, '_address_three', TRUE ) != '' )
4061 {
4062 $display_address[] = get_post_meta( $post_id, '_address_three', TRUE );
4063 }
4064 else
4065 {
4066 if ( get_post_meta( $post_id, '_address_four', TRUE ) != '' )
4067 {
4068 $display_address[] = get_post_meta( $post_id, '_address_four', TRUE );
4069 }
4070 }
4071 $display_address = implode(", ", $display_address);
4072
4073 $property_post = array(
4074 'post_title' => ph_clean($display_address),
4075 'post_content' => '',
4076 'post_type' => 'property',
4077 'post_status' => 'publish',
4078 'comment_status' => 'closed',
4079 'ping_status' => 'closed',
4080 );
4081
4082 // Insert the post into the database
4083 $property_post_id = wp_insert_post( $property_post );
4084
4085 if ( is_wp_error($property_post_id) || $property_post_id == 0 )
4086 {
4087 // Failed. Don't really know at the moment how to handle this
4088
4089 $return = array('error' => 'Failed to create property post. Please try again');
4090 echo json_encode( $return );
4091 die();
4092 }
4093 else
4094 {
4095 // Successfully added property post
4096
4097 $department = get_post_meta( $post_id, '_department', TRUE );
4098
4099 $reference_number = '';
4100 if ( get_option( 'propertyhive_auto_incremental_reference_numbers' ) == 'yes' )
4101 {
4102 $next = get_option( 'propertyhive_auto_incremental_next', '' );
4103 if ( $next == '' || (int)$next == 0 )
4104 {
4105 $next = 1;
4106 }
4107 $reference_number = $next;
4108
4109 $next_auto_increment = $next + 1;
4110
4111 update_option( 'propertyhive_auto_incremental_next', $next_auto_increment );
4112 }
4113 update_post_meta( $property_post_id, '_reference_number', $reference_number );
4114
4115 update_post_meta( $property_post_id, '_address_name_number', get_post_meta( $post_id, '_address_name_number', TRUE ) );
4116 update_post_meta( $property_post_id, '_address_street', get_post_meta( $post_id, '_address_street', TRUE ) );
4117 update_post_meta( $property_post_id, '_address_two', get_post_meta( $post_id, '_address_two', TRUE ) );
4118 update_post_meta( $property_post_id, '_address_three', get_post_meta( $post_id, '_address_three', TRUE ) );
4119 update_post_meta( $property_post_id, '_address_four', get_post_meta( $post_id, '_address_four', TRUE ) );
4120 update_post_meta( $property_post_id, '_address_postcode', get_post_meta( $post_id, '_address_postcode', TRUE ) );
4121 update_post_meta( $property_post_id, '_address_country', get_post_meta( $post_id, '_address_country', TRUE ) );
4122
4123 if ( ini_get('allow_url_fopen') )
4124 {
4125 // No lat lng. Let's get it
4126 $address_to_geocode = array();
4127 if ( get_post_meta( $post_id, '_address_name_number', TRUE ) != '' ) { $address_to_geocode[] = get_post_meta( $post_id, '_address_name_number', TRUE ); }
4128 if ( get_post_meta( $post_id, '_address_street', TRUE ) != '' ) { $address_to_geocode[] = get_post_meta( $post_id, '_address_street', TRUE ); }
4129 if ( get_post_meta( $post_id, '_address_two', TRUE ) != '' ) { $address_to_geocode[] = get_post_meta( $post_id, '_address_two', TRUE ); }
4130 if ( get_post_meta( $post_id, '_address_three', TRUE ) != '' ) { $address_to_geocode[] = get_post_meta( $post_id, '_address_three', TRUE ); }
4131 if ( get_post_meta( $post_id, '_address_four', TRUE ) != '' ) { $address_to_geocode[] = get_post_meta( $post_id, '_address_four', TRUE ); }
4132 if ( get_post_meta( $post_id, '_address_postcode', TRUE ) ) { $address_to_geocode[] = get_post_meta( $post_id, '_address_postcode', TRUE ); }
4133
4134 $country = get_option( 'propertyhive_default_country', 'GB' );
4135
4136 if ( get_option('propertyhive_geocoding_provider') == 'osm' )
4137 {
4138 $request_url = "https://nominatim.openstreetmap.org/search?format=json&limit=1&countrycodes=" . strtolower($country) . "&addressdetails=1&q=" . urlencode(implode( ", ", $address_to_geocode ));
4139 $response = wp_remote_get(
4140 $request_url,
4141 array(
4142 'headers' => array(
4143 'Referer' => home_url(),
4144 'User-Agent' => 'Property-Hive/' . PH_VERSION . ' (+https://wp-property-hive.com)',
4145 ),
4146 )
4147 );
4148 if ( is_array( $response ) )
4149 {
4150 $body = wp_remote_retrieve_body( $response );
4151 $json = json_decode($body, true);
4152
4153 if ( !empty($json) && isset($json[0]['lat']) && isset($json[0]['lon']) )
4154 {
4155 $lat = $json[0]['lat'];
4156 $lng = $json[0]['lon'];
4157
4158 if ($lat != '' && $lng != '')
4159 {
4160 update_post_meta( $property_post_id, '_latitude', $lat );
4161 update_post_meta( $property_post_id, '_longitude', $lng );
4162 }
4163 }
4164 }
4165 }
4166 else
4167 {
4168 $request_url = "https://maps.googleapis.com/maps/api/geocode/xml?address=" . urlencode( implode( ", ", $address_to_geocode ) ) . "&sensor=false&region=" . strtolower($country); // the request URL you'll send to google to get back your XML feed
4169
4170 $api_key = get_option('propertyhive_google_maps_api_key', '');
4171 if ( $api_key != '' ) { $request_url .= "&key=" . $api_key; }
4172
4173 $response = wp_remote_get($request_url);
4174
4175 if ( is_array( $response ) && !is_wp_error( $response ) )
4176 {
4177 $header = $response['headers']; // array of http header lines
4178 $body = $response['body']; // use the content
4179
4180 $xml = simplexml_load_string($body);
4181
4182 if ( $xml !== FALSE )
4183 {
4184 $status = $xml->status; // Get the request status as google's api can return several responses
4185
4186 if ($status == "OK")
4187 {
4188 //request returned completed time to get lat / lng for storage
4189 $lat = (string)$xml->result->geometry->location->lat;
4190 $lng = (string)$xml->result->geometry->location->lng;
4191
4192 if ($lat != '' && $lng != '')
4193 {
4194 update_post_meta( $property_post_id, '_latitude', $lat );
4195 update_post_meta( $property_post_id, '_longitude', $lng );
4196 }
4197 }
4198 }
4199 }
4200 }
4201 }
4202
4203 update_post_meta( $property_post_id, '_department', $department );
4204
4205 switch ( $department )
4206 {
4207 case "residential-sales":
4208 {
4209 update_post_meta( $property_post_id, '_currency', 'GBP' );
4210
4211 $price = preg_replace("/[^0-9.]/", '', get_post_meta( $post_id, '_valued_price', TRUE ));
4212 update_post_meta( $property_post_id, '_price', $price );
4213
4214 break;
4215 }
4216 case "residential-lettings":
4217 {
4218 update_post_meta( $property_post_id, '_currency', 'GBP' );
4219
4220 $rent = preg_replace("/[^0-9.]/", '', get_post_meta( $post_id, '_valued_rent', TRUE ));
4221 update_post_meta( $property_post_id, '_rent', $rent );
4222 update_post_meta( $property_post_id, '_rent_frequency', get_post_meta( $post_id, '_valued_rent_frequency', TRUE ) );
4223
4224 break;
4225 }
4226 }
4227
4228 // Store price in common currency (GBP) used for ordering
4229 $ph_countries = new PH_Countries();
4230 $ph_countries->update_property_price_actual( $property_post_id );
4231
4232 update_post_meta( $property_post_id, '_bedrooms', get_post_meta( $post_id, '_bedrooms', TRUE ) );
4233 update_post_meta( $property_post_id, '_bathrooms', get_post_meta( $post_id, '_bathrooms', TRUE ) );
4234 update_post_meta( $property_post_id, '_reception_rooms', get_post_meta( $post_id, '_reception_rooms', TRUE ) );
4235
4236 update_post_meta( $property_post_id, '_on_market', '' );
4237 update_post_meta( $property_post_id, '_featured', '' );
4238
4239 // Taxonomies
4240 wp_set_object_terms( $property_post_id, wp_get_object_terms( $post_id, 'property_type', array("fields" => "ids") ), 'property_type' );
4241 wp_set_object_terms( $property_post_id, wp_get_object_terms( $post_id, 'parking', array("fields" => "ids") ), 'parking' );
4242 wp_set_object_terms( $property_post_id, wp_get_object_terms( $post_id, 'outside_space', array("fields" => "ids") ), 'outside_space' );
4243
4244 update_post_meta( $property_post_id, '_council_tax_band', get_post_meta( $post_id, '_council_tax_band', TRUE ) );
4245
4246 $owner_contact_ids = get_post_meta( $post_id, '_property_owner_contact_id', TRUE );
4247 if ( !is_array($owner_contact_ids) )
4248 {
4249 $owner_contact_ids = array($owner_contact_ids);
4250 }
4251 update_post_meta( $property_post_id, '_owner_contact_id', $owner_contact_ids );
4252
4253 // Make updates to appraisal
4254 update_post_meta( $post_id, '_status', 'instructed' );
4255 update_post_meta( $post_id, '_property_id', $property_post_id );
4256
4257 //Update owner(s)
4258 foreach ( $owner_contact_ids as $owner_contact_id )
4259 {
4260 $contact_types = get_post_meta( $owner_contact_id, '_contact_types', TRUE );
4261
4262 if ( !in_array('owner', $contact_types) )
4263 {
4264 $contact_types[] = 'owner';
4265 }
4266
4267 // get appraisals where this is the owner and where not instructed
4268 $args = array(
4269 'post_type' => 'appraisal',
4270 'nopaging' => true,
4271 'meta_query' => array(
4272 array(
4273 'key' => '_property_owner_contact_id',
4274 'value' => $owner_contact_id,
4275 'compare' => '='
4276 ),
4277 array(
4278 'key' => '_status',
4279 'value' => 'instructed',
4280 'compare' => '!='
4281 )
4282 )
4283 );
4284
4285 $appraisal_query = new WP_Query($args);
4286
4287 if (!$appraisal_query->have_posts())
4288 {
4289 // no longer a potential owner.
4290 if (($key = array_search('potentialowner', $contact_types)) !== false)
4291 {
4292 unset($contact_types[$key]);
4293 }
4294 }
4295 wp_reset_postdata();
4296
4297 update_post_meta( $owner_contact_id, '_contact_types', $contact_types );
4298 }
4299
4300 // Add note/comment to appraisal
4301 $comment = array(
4302 'note_type' => 'action',
4303 'action' => 'appraisal_instructed',
4304 );
4305
4306 PH_Comments::insert_note( $post_id, $comment );
4307
4308 wp_send_json_success();
4309 }
4310 }
4311
4312 wp_send_json_error();
4313 }
4314
4315 public function appraisal_email_owner_booking_confirmation()
4316 {
4317 check_ajax_referer( 'appraisal-actions', 'security' );
4318
4319 $post_id = (int)$_POST['appraisal_id'];
4320
4321 $appraisal = new PH_Appraisal($post_id);
4322
4323 $owner_contact_id = $appraisal->property_owner_contact_id;
4324
4325 if ( !is_array($owner_contact_id) ) { $owner_contact_id = array($owner_contact_id); }
4326
4327 if ( !empty($owner_contact_id) )
4328 {
4329 $owner_emails = array();
4330 $owner_names = array();
4331 $owner_dears = array();
4332
4333 foreach ($owner_contact_id as $owner_id)
4334 {
4335 $owner_contact = new PH_Contact($owner_id);
4336
4337 $owner_email = sanitize_email( $owner_contact->email_address );
4338 $owner_name = $owner_contact->post_title;
4339 $owner_dear = $owner_contact->dear();
4340
4341 if( ! empty($owner_email) ) array_push($owner_emails, $owner_email);
4342 if( ! empty($owner_name) ) array_push($owner_names, $owner_name);
4343 if( ! empty($owner_dear) ) array_push($owner_dears, $owner_dear);
4344 }
4345
4346 $owner_names_string = $this->get_list_string($owner_names);
4347 $owner_dears_string = $this->get_list_string($owner_dears);
4348
4349 $negotiator_names = array();
4350 $negotiator_names_string = '';
4351
4352 $negotiator_email_addresses = array();
4353 $negotiator_email_addresses_string = '';
4354
4355 $negotiator_telephone_numbers = array();
4356 $negotiator_telephone_numbers_string = '';
4357
4358 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
4359 if ( !empty($negotiator_ids) )
4360 {
4361 foreach ( $negotiator_ids as $negotiator_id )
4362 {
4363 $negotiator = get_user_by( 'id', $negotiator_id );
4364 if ( $negotiator !== false )
4365 {
4366 if ( isset($negotiator->display_name) && !empty($negotiator->display_name) )
4367 {
4368 $negotiator_names[] = $negotiator->display_name;
4369 }
4370
4371 if ( isset($negotiator->user_email) && !empty($negotiator->user_email) )
4372 {
4373 $negotiator_email_addresses[] = $negotiator->user_email;
4374 }
4375
4376 $telephone_number = get_user_meta( $negotiator_id, 'telephone_number', true );
4377 if ( !empty($telephone_number) )
4378 {
4379 $negotiator_telephone_numbers[] = $telephone_number;
4380 }
4381 }
4382 }
4383 }
4384 if ( !empty($negotiator_names) )
4385 {
4386 $last = array_slice($negotiator_names, -1);
4387 $first = join(', ', array_slice($negotiator_names, 0, -1));
4388 $both = array_filter(array_merge(array($first), $last), 'strlen');
4389 $negotiator_names_string = join(' and ', $both);
4390 }
4391 if ( !empty($negotiator_email_addresses) )
4392 {
4393 $last = array_slice($negotiator_email_addresses, -1);
4394 $first = join(', ', array_slice($negotiator_email_addresses, 0, -1));
4395 $both = array_filter(array_merge(array($first), $last), 'strlen');
4396 $negotiator_email_addresses_string = join(' and ', $both);
4397 }
4398 if ( !empty($negotiator_telephone_numbers) )
4399 {
4400 $last = array_slice($negotiator_telephone_numbers, -1);
4401 $first = join(', ', array_slice($negotiator_telephone_numbers, 0, -1));
4402 $both = array_filter(array_merge(array($first), $last), 'strlen');
4403 $negotiator_telephone_numbers_string = join(' and ', $both);
4404 }
4405
4406 $to = implode(",", $owner_emails);
4407
4408 $subject = isset($_POST['subject']) ? sanitize_text_field($_POST['subject']) : get_option( 'propertyhive_appraisal_owner_booking_confirmation_email_subject', '' );
4409 $body = isset($_POST['body']) ? sanitize_textarea_field($_POST['body']) : get_option( 'propertyhive_appraisal_owner_booking_confirmation_email_body', '' );
4410
4411 $appraisal_date_timestamp = strtotime($appraisal->start_date_time);
4412
4413 $subject = str_replace('[property_address]', $appraisal->get_formatted_full_address(), $subject);
4414 $subject = str_replace('[owner_name]', $owner_names_string, $subject);
4415 $subject = str_replace('[appraisal_time]', date("H:i", $appraisal_date_timestamp), $subject);
4416 $subject = str_replace('[appraisal_date]', date("l jS F Y", $appraisal_date_timestamp), $subject);
4417 $subject = str_replace('[negotiator_name]', $negotiator_names_string, $subject);
4418 $subject = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $subject);
4419 $subject = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $subject);
4420
4421 $subject = apply_filters( 'appraisal_owner_booking_confirmation_email_subject', $subject, $post_id );
4422
4423 $body = str_replace('[property_address]', $appraisal->get_formatted_full_address(), $body);
4424 $body = str_replace('[owner_name]', $owner_names_string, $body);
4425 $body = str_replace('[owner_dear]', $owner_dears_string, $body);
4426 $body = str_replace('[appraisal_time]', date("H:i", $appraisal_date_timestamp), $body);
4427 $body = str_replace('[appraisal_date]', date("l jS F Y", $appraisal_date_timestamp), $body);
4428 $body = str_replace('[negotiator_name]', $negotiator_names_string, $body);
4429 $body = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $body);
4430 $body = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $body);
4431
4432 $body = html_entity_decode($body);
4433
4434 $body = apply_filters( 'appraisal_owner_booking_confirmation_email_body', $body, $post_id );
4435
4436 $from = '';
4437 $from_setting = get_option( 'propertyhive_confirmations_default_from', '' );
4438 if ( $from_setting == 'user' )
4439 {
4440 $current_user = wp_get_current_user();
4441 $from = ( isset($current_user->user_email) ? $current_user->user_email : '' );
4442 }
4443 if ( $from == '' )
4444 {
4445 $from = get_option('propertyhive_email_from_address', '');
4446 }
4447 if ( $from == '' )
4448 {
4449 $from = get_bloginfo('admin_email');
4450 }
4451
4452 $headers = array();
4453 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
4454 $headers[] = 'Reply-To: ' . sanitize_email($from);
4455 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
4456
4457 $headers = apply_filters( 'propertyhive_appraisal_owner_booking_confirmation_email_headers', $headers );
4458
4459 $sent = wp_mail($to, $subject, $body, $headers);
4460
4461 if ( !$sent )
4462 {
4463 wp_send_json_error('Failed to send email');
4464 }
4465
4466 if ( apply_filters( 'propertyhive_log_booking_confirmation_emails', false ) === true )
4467 {
4468 // Add note/comment to appraisal
4469 $comment = array(
4470 'note_type' => 'action',
4471 'action' => 'appraisal_owner_booking_confirmation_email',
4472 );
4473
4474 PH_Comments::insert_note( $post_id, $comment );
4475 }
4476
4477 update_post_meta( $post_id, '_owner_booking_confirmation_sent_at', date("Y-m-d H:i:s") );
4478
4479 wp_send_json_success();
4480 }
4481 else
4482 {
4483 wp_send_json_error('No owner recipients found');
4484 }
4485
4486 wp_die();
4487 }
4488
4489 public function appraisal_revert_pending()
4490 {
4491 check_ajax_referer( 'appraisal-actions', 'security' );
4492
4493 $post_id = (int)$_POST['appraisal_id'];
4494
4495 $status = get_post_meta( $post_id, '_status', TRUE );
4496
4497 if ( $status == 'carried_out' || $status == 'cancelled' )
4498 {
4499 update_post_meta( $post_id, '_status', 'pending' );
4500
4501 // Add note/comment to appraisal
4502 $comment = array(
4503 'note_type' => 'action',
4504 'action' => 'appraisal_revert_pending',
4505 );
4506
4507 PH_Comments::insert_note( $post_id, $comment );
4508
4509 wp_send_json_success();
4510 }
4511
4512 wp_send_json_error();
4513 }
4514
4515 public function appraisal_revert_carried_out()
4516 {
4517 check_ajax_referer( 'appraisal-actions', 'security' );
4518
4519 $post_id = (int)$_POST['appraisal_id'];
4520
4521 $status = get_post_meta( $post_id, '_status', TRUE );
4522
4523 if ( $status == 'won' || $status == 'lost' )
4524 {
4525 update_post_meta( $post_id, '_status', 'carried_out' );
4526
4527 // Add note/comment to appraisal
4528 $comment = array(
4529 'note_type' => 'action',
4530 'action' => 'appraisal_revert_carried_out',
4531 );
4532
4533 PH_Comments::insert_note( $post_id, $comment );
4534
4535 wp_send_json_success();
4536 }
4537
4538 wp_send_json_error();
4539 }
4540
4541 public function appraisal_revert_won()
4542 {
4543 check_ajax_referer( 'appraisal-actions', 'security' );
4544
4545 $post_id = (int)$_POST['appraisal_id'];
4546
4547 $status = get_post_meta( $post_id, '_status', TRUE );
4548
4549 if ( $status == 'instructed' )
4550 {
4551 update_post_meta( $post_id, '_status', 'won' );
4552
4553 // Add note/comment to appraisal
4554 $comment = array(
4555 'note_type' => 'action',
4556 'action' => 'appraisal_revert_won',
4557 );
4558
4559 PH_Comments::insert_note( $post_id, $comment );
4560
4561 wp_send_json_success();
4562 }
4563
4564 wp_send_json_error();
4565 }
4566
4567 // Viewing related functions
4568 public function book_viewing_property()
4569 {
4570 check_ajax_referer( 'book-viewing', 'security' );
4571
4572 $this->json_headers();
4573
4574 // TO DO: Should do validation on server side also
4575 if (empty($_POST['property_id']))
4576 {
4577 $return = array('error' => 'No property selected');
4578 echo json_encode( $return );
4579 die();
4580 }
4581
4582 $property = new PH_Property((int)$_POST['property_id']);
4583
4584 $applicant_contact_ids = array();
4585
4586 // Create applicant record if required
4587 if (empty($_POST['applicant_ids']) && !empty($_POST['applicant_name']))
4588 {
4589 // Need to create contact/applicant
4590 $contact_post = array(
4591 'post_title' => ph_clean($_POST['applicant_name']),
4592 'post_content' => '',
4593 'post_type' => 'contact',
4594 'post_status' => 'publish',
4595 'comment_status' => 'closed',
4596 'ping_status' => 'closed',
4597 );
4598
4599 // Insert the post into the database
4600 $contact_post_id = wp_insert_post( $contact_post );
4601
4602 if ( is_wp_error($contact_post_id) || $contact_post_id == 0 )
4603 {
4604 $return = array('error' => 'Failed to create contact post. Please try again');
4605 echo json_encode( $return );
4606 die();
4607 }
4608
4609 update_post_meta( $contact_post_id, '_contact_types', array('applicant') );
4610
4611 $email_address = isset($_POST['applicant_email_address']) ? sanitize_email($_POST['applicant_email_address']) : '';
4612 $telephone_number = isset($_POST['applicant_telephone_number']) ? sanitize_text_field($_POST['applicant_telephone_number']) : '';
4613 update_post_meta( $contact_post_id, '_email_address', $email_address );
4614 update_post_meta( $contact_post_id, '_telephone_number', $telephone_number );
4615 update_post_meta( $contact_post_id, '_telephone_number_clean', ph_clean( ph_clean_telephone_number($telephone_number) ) );
4616
4617 if ( isset($_POST['applicant_address']) && !empty(sanitize_textarea_field($_POST['applicant_address'])) )
4618 {
4619 $address = ph_split_address_into_fields( sanitize_textarea_field($_POST['applicant_address']) );
4620
4621 update_post_meta( $contact_post_id, '_address_name_number', $address['address_name_number'] );
4622 update_post_meta( $contact_post_id, '_address_street', $address['address_street'] );
4623 update_post_meta( $contact_post_id, '_address_two', $address['address_two'] );
4624 update_post_meta( $contact_post_id, '_address_three', $address['address_three'] );
4625 update_post_meta( $contact_post_id, '_address_four', $address['address_four'] );
4626 update_post_meta( $contact_post_id, '_address_postcode', $address['address_postcode'] );
4627 update_post_meta( $contact_post_id, '_address_country', get_option( 'propertyhive_default_country', 'GB' ) );
4628 }
4629
4630 update_post_meta( $contact_post_id, '_applicant_profiles', 1 );
4631 update_post_meta( $contact_post_id, '_applicant_profile_0', array( 'department' => $property->department, 'send_matching_properties' => '' ) );
4632
4633 $applicant_contact_ids[] = $contact_post_id;
4634 }
4635
4636 if (!empty($_POST['applicant_ids']) && empty($_POST['applicant_name']))
4637 {
4638 // This is an existing contact
4639 if ( !is_array($_POST['applicant_ids']) )
4640 {
4641 $_POST['applicant_ids'] = array(ph_clean($_POST['applicant_ids']));
4642 }
4643
4644 foreach ( $_POST['applicant_ids'] as $applicant_id )
4645 {
4646 $applicant_contact_ids[] = (int)$applicant_id;
4647 }
4648 }
4649
4650 $applicant_contact_ids = array_unique($applicant_contact_ids);
4651
4652 if ( empty($applicant_contact_ids) )
4653 {
4654 $return = array('error' => 'No applicant selected, or unable to create applicant record');
4655 echo json_encode( $return );
4656 die();
4657 }
4658
4659 // Make sure each of the contacts has an applicant profile of the correct department
4660 /*foreach ( $applicant_contact_ids as $applicant_contact_id )
4661 {
4662 $has_correct_profile = false;
4663
4664 // Get all existing profiles
4665 $existing_contact_types = get_post_meta( $applicant_contact_id, '_contact_types', TRUE );
4666 if ( $existing_contact_types == '' || !is_array($existing_contact_types) )
4667 {
4668 $existing_contact_types = array();
4669 }
4670 if ( in_array( 'applicant', $existing_contact_types ) )
4671 {
4672 $num_applicant_profiles = get_post_meta( $applicant_contact_id, '_applicant_profiles', TRUE );
4673 if ( $num_applicant_profiles == '' )
4674 {
4675 $num_applicant_profiles = 0;
4676 }
4677
4678 if ( $num_applicant_profiles > 0 )
4679 {
4680 for ( $i = 0; $i < $num_applicant_profiles; ++$i )
4681 {
4682 $applicant_profile = get_post_meta( $applicant_contact_id, '_applicant_profile_' . $i, TRUE );
4683 if ( $applicant_profile['department'] == $property->department )
4684 {
4685 $has_correct_profile = true;
4686 }
4687 }
4688 }
4689 }
4690
4691 if ( !$has_correct_profile )
4692 {
4693 if ( in_array( 'applicant', $existing_contact_types ) )
4694 {
4695 // Already an applicant. Just need to add profile
4696 }
4697 else
4698 {
4699 $existing_contact_types[] = 'applicant';
4700 update_post_meta( $applicant_contact_id, '_contact_types', $existing_contact_types );
4701 }
4702
4703 $num_applicant_profiles = get_post_meta( $applicant_contact_id, '_applicant_profiles', TRUE );
4704 if ( $num_applicant_profiles == '' )
4705 {
4706 $num_applicant_profiles = 0;
4707 }
4708
4709 update_post_meta( $applicant_contact_id, '_applicant_profiles', $num_applicant_profiles + 1 );
4710 update_post_meta( $applicant_contact_id, '_applicant_profile_' . $num_applicant_profiles, array( 'department' => $property->department ) );
4711 }
4712 }*/
4713
4714 // Insert viewing record
4715 $viewing_post = array(
4716 'post_title' => '',
4717 'post_content' => '',
4718 'post_type' => 'viewing',
4719 'post_status' => 'publish',
4720 'comment_status' => 'closed',
4721 'ping_status' => 'closed',
4722 );
4723
4724 // Insert the post into the database
4725 $viewing_post_id = wp_insert_post( $viewing_post );
4726
4727 if ( is_wp_error($viewing_post_id) || $viewing_post_id == 0 )
4728 {
4729 $return = array('error' => 'Failed to create viewing post. Please try again');
4730 echo json_encode( $return );
4731 die();
4732 }
4733
4734 add_post_meta( $viewing_post_id, '_start_date_time', ph_clean($_POST['start_date']) . ' ' . ph_clean($_POST['start_time']) );
4735 add_post_meta( $viewing_post_id, '_duration', 30 * 60 ); // Stored in seconds. Default to 30 mins
4736 add_post_meta( $viewing_post_id, '_property_id', (int)$_POST['property_id'] );
4737
4738 $applicant_contacts = array();
4739 foreach ($applicant_contact_ids as $applicant_contact_id)
4740 {
4741 add_post_meta( $viewing_post_id, '_applicant_contact_id', $applicant_contact_id );
4742
4743 $applicant_contacts[] = array(
4744 'ID' => $applicant_contact_id,
4745 'post_title' => get_the_title($applicant_contact_id),
4746 'edit_link' => get_edit_post_link( $applicant_contact_id, '' ),
4747 );
4748 }
4749
4750 add_post_meta( $viewing_post_id, '_status', 'pending' );
4751 add_post_meta( $viewing_post_id, '_feedback_status', '' );
4752 add_post_meta( $viewing_post_id, '_feedback', '' );
4753 add_post_meta( $viewing_post_id, '_feedback_passed_on', '' );
4754
4755 if ( !empty($_POST['negotiator_ids']) )
4756 {
4757 foreach ( $_POST['negotiator_ids'] as $negotiator_id )
4758 {
4759 add_post_meta( $viewing_post_id, '_negotiator_id', (int)$negotiator_id );
4760 }
4761 }
4762
4763 $return = array('success' => array(
4764 'viewing' => array(
4765 'ID' => $viewing_post_id,
4766 'edit_link' => get_edit_post_link( $viewing_post_id, '' ),
4767 ),
4768 'applicant_contacts' => $applicant_contacts,
4769 ));
4770
4771 echo json_encode( $return );
4772
4773 die();
4774 }
4775
4776 public function book_viewing_contact()
4777 {
4778 check_ajax_referer( 'book-viewing', 'security' );
4779
4780 $this->json_headers();
4781
4782 // TO DO: Should do validation on server side also
4783 if (empty($_POST['contact_id']))
4784 {
4785 $return = array('error' => 'No contact selected');
4786 echo json_encode( $return );
4787 die();
4788 }
4789
4790 if (empty($_POST['property_ids']))
4791 {
4792 $return = array('error' => 'No property selected');
4793 echo json_encode( $return );
4794 die();
4795 }
4796
4797 // Loop through contacts and create one viewing each
4798 // At the moment it's a 1-to-1 relationship, but might support multiple in the future
4799 foreach ( $_POST['property_ids'] as $property_id )
4800 {
4801 // Insert viewing record
4802 $viewing_post = array(
4803 'post_title' => '',
4804 'post_content' => '',
4805 'post_type' => 'viewing',
4806 'post_status' => 'publish',
4807 'comment_status' => 'closed',
4808 'ping_status' => 'closed',
4809 );
4810
4811 // Insert the post into the database
4812 $viewing_post_id = wp_insert_post( $viewing_post );
4813
4814 if ( is_wp_error($viewing_post_id) || $viewing_post_id == 0 )
4815 {
4816 $return = array('error' => 'Failed to create viewing post. Please try again');
4817 echo json_encode( $return );
4818 die();
4819 }
4820
4821 add_post_meta( $viewing_post_id, '_start_date_time', ph_clean($_POST['start_date']) . ' ' . ph_clean($_POST['start_time']) );
4822 add_post_meta( $viewing_post_id, '_duration', 30 * 60 ); // Stored in seconds. Default to 30 mins
4823 add_post_meta( $viewing_post_id, '_property_id', (int)$property_id );
4824 add_post_meta( $viewing_post_id, '_applicant_contact_id', (int)$_POST['contact_id'] );
4825 add_post_meta( $viewing_post_id, '_status', 'pending' );
4826 add_post_meta( $viewing_post_id, '_feedback_status', '' );
4827 add_post_meta( $viewing_post_id, '_feedback', '' );
4828 add_post_meta( $viewing_post_id, '_feedback_passed_on', '' );
4829
4830 if ( !empty($_POST['negotiator_ids']) )
4831 {
4832 foreach ( $_POST['negotiator_ids'] as $negotiator_id )
4833 {
4834 add_post_meta( $viewing_post_id, '_negotiator_id', (int)$negotiator_id );
4835 }
4836 }
4837 }
4838
4839 $properties = array();
4840 foreach ( $_POST['property_ids'] as $property_id )
4841 {
4842 $properties[] = array(
4843 'ID' => (int)$property_id,
4844 'post_title' => get_the_title((int)$property_id),
4845 'edit_link' => get_edit_post_link( (int)$property_id, '' ),
4846 );
4847 }
4848
4849 $return = array('success' => array(
4850 'viewing' => array(
4851 'ID' => $viewing_post_id,
4852 'edit_link' => get_edit_post_link( $viewing_post_id, '' ),
4853 ),
4854 'properties' => $properties,
4855 ));
4856
4857 echo json_encode( $return );
4858
4859 die();
4860 }
4861
4862 public function get_viewing_details_meta_box()
4863 {
4864 global $post;
4865
4866 check_ajax_referer( 'viewing-details-meta-box', 'security' );
4867
4868 $post = get_post((int)$_POST['viewing_id']);
4869
4870 $viewing = new PH_Viewing((int)$_POST['viewing_id']);
4871
4872 $readonly = isset($_POST['readonly']) ? filter_var($_POST['readonly'], FILTER_VALIDATE_BOOLEAN) : false;
4873
4874 include( PH()->plugin_path() . '/includes/admin/views/html-viewing-details-meta-box.php' );
4875
4876 die();
4877 }
4878
4879 public function get_viewing_actions()
4880 {
4881 check_ajax_referer( 'viewing-actions', 'security' );
4882
4883 $post_id = (int)$_POST['viewing_id'];
4884
4885 include( PH()->plugin_path() . '/includes/admin/views/html-viewing-actions.php' );
4886
4887 die();
4888 }
4889
4890 public function get_viewing_lightbox()
4891 {
4892 global $post;
4893
4894 $post_id = $_GET['post_id'];
4895
4896 $post = get_post((int)$post_id);
4897
4898 $viewing = new PH_Viewing($post_id);
4899
4900 include( PH()->plugin_path() . '/includes/admin/views/html-viewing-details-lightbox.php' );
4901
4902 die();
4903 }
4904
4905 public function viewing_carried_out()
4906 {
4907 check_ajax_referer( 'viewing-actions', 'security' );
4908
4909 $post_id = (int)$_POST['viewing_id'];
4910
4911 $status = get_post_meta( $post_id, '_status', TRUE );
4912
4913 if ( $status == 'pending' )
4914 {
4915 update_post_meta( $post_id, '_status', 'carried_out' );
4916
4917 // Add note/comment to viewing
4918 $comment = array(
4919 'note_type' => 'action',
4920 'action' => 'viewing_carried_out',
4921 );
4922
4923 PH_Comments::insert_note( $post_id, $comment );
4924
4925 wp_send_json_success();
4926 }
4927
4928 wp_send_json_error();
4929 }
4930
4931 public function viewing_no_show()
4932 {
4933 check_ajax_referer( 'viewing-actions', 'security' );
4934
4935 $post_id = (int)$_POST['viewing_id'];
4936
4937 $status = get_post_meta( $post_id, '_status', TRUE );
4938
4939 if ( $status == 'pending' )
4940 {
4941 update_post_meta( $post_id, '_status', 'no_show' );
4942
4943 // Add note/comment to viewing
4944 $comment = array(
4945 'note_type' => 'action',
4946 'action' => 'viewing_applicant_no_show',
4947 );
4948
4949 PH_Comments::insert_note( $post_id, $comment );
4950
4951 wp_send_json_success();
4952 }
4953
4954 wp_send_json_error();
4955 }
4956
4957 public function viewing_cancelled()
4958 {
4959 check_ajax_referer( 'viewing-actions', 'security' );
4960
4961 $post_id = (int)$_POST['viewing_id'];
4962
4963 $status = get_post_meta( $post_id, '_status', TRUE );
4964
4965 if ( $status == 'pending' )
4966 {
4967 update_post_meta( $post_id, '_status', 'cancelled' );
4968 update_post_meta( $post_id, '_cancelled_reason', sanitize_textarea_field( $_POST['cancelled_reason'] ) );
4969 update_post_meta( $post_id, '_cancelled_reason_public', isset($_POST['cancelled_reason_public']) && $_POST['cancelled_reason_public'] == 'yes' ? 'yes' : '' );
4970
4971 // Add note/comment to viewing
4972 $comment = array(
4973 'note_type' => 'action',
4974 'action' => 'viewing_cancelled',
4975 );
4976
4977 PH_Comments::insert_note( $post_id, $comment );
4978
4979 wp_send_json_success();
4980 }
4981
4982 wp_send_json_error();
4983 }
4984
4985 public function viewing_email_applicant_booking_confirmation()
4986 {
4987 check_ajax_referer( 'viewing-actions', 'security' );
4988
4989 $post_id = (int)$_POST['viewing_id'];
4990
4991 $applicant_contact_ids = get_post_meta( $post_id, '_applicant_contact_id' );
4992 $property_id = get_post_meta( $post_id, '_property_id', TRUE );
4993
4994 if ( !is_array($applicant_contact_ids) || (int)$property_id == '' || count($applicant_contact_ids) == 0 || (int)$property_id == 0 )
4995 {
4996 wp_send_json_error('Missing contact or property');
4997 }
4998
4999 $property = new PH_Property((int)$property_id);
5000
5001 $to = array();
5002 foreach ($applicant_contact_ids as $applicant_contact_id)
5003 {
5004 $applicant_email_address = get_post_meta( $applicant_contact_id, '_email_address', TRUE );
5005 $explode_applicant_email_address = explode( ",", $applicant_email_address );
5006 foreach ( $explode_applicant_email_address as $email_address )
5007 {
5008 $to[] = sanitize_email($email_address);
5009 }
5010 }
5011
5012 $to = array_filter($to);
5013
5014 if ( !empty(implode($to)) )
5015 {
5016 $subject = isset($_POST['subject']) ? sanitize_text_field($_POST['subject']) : get_option( 'propertyhive_viewing_applicant_booking_confirmation_email_subject', '' );
5017 $body = isset($_POST['body']) ? sanitize_textarea_field($_POST['body']) : get_option( 'propertyhive_viewing_applicant_booking_confirmation_email_body', '' );
5018
5019 $applicant_names = array();
5020 $applicant_dears = array();
5021 foreach ($applicant_contact_ids as $applicant_contact_id)
5022 {
5023 $applicant_contact = new PH_Contact($applicant_contact_id);
5024 $applicant_names[] = $applicant_contact->post_title;
5025 $applicant_dears[] = $applicant_contact->dear();
5026 }
5027 $applicant_names = array_filter($applicant_names);
5028 $applicant_dears = array_filter($applicant_dears);
5029
5030 $applicant_names_string = $this->get_list_string($applicant_names);
5031 $applicant_dears_string = $this->get_list_string($applicant_dears);
5032
5033 $negotiator_names = array();
5034 $negotiator_names_string = '';
5035
5036 $negotiator_email_addresses = array();
5037 $negotiator_email_addresses_string = '';
5038
5039 $negotiator_telephone_numbers = array();
5040 $negotiator_telephone_numbers_string = '';
5041
5042 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
5043 if ( !empty($negotiator_ids) )
5044 {
5045 foreach ( $negotiator_ids as $negotiator_id )
5046 {
5047 $negotiator = get_user_by( 'id', $negotiator_id );
5048 if ( $negotiator !== false )
5049 {
5050 if ( isset($negotiator->display_name) && !empty($negotiator->display_name) )
5051 {
5052 $negotiator_names[] = $negotiator->display_name;
5053 }
5054
5055 if ( isset($negotiator->user_email) && !empty($negotiator->user_email) )
5056 {
5057 $negotiator_email_addresses[] = $negotiator->user_email;
5058 }
5059
5060 $telephone_number = get_user_meta( $negotiator_id, 'telephone_number', true );
5061 if ( !empty($telephone_number) )
5062 {
5063 $negotiator_telephone_numbers[] = $telephone_number;
5064 }
5065 }
5066 }
5067 }
5068 if ( !empty($negotiator_names) )
5069 {
5070 $last = array_slice($negotiator_names, -1);
5071 $first = join(', ', array_slice($negotiator_names, 0, -1));
5072 $both = array_filter(array_merge(array($first), $last), 'strlen');
5073 $negotiator_names_string = join(' and ', $both);
5074 }
5075 if ( !empty($negotiator_email_addresses) )
5076 {
5077 $last = array_slice($negotiator_email_addresses, -1);
5078 $first = join(', ', array_slice($negotiator_email_addresses, 0, -1));
5079 $both = array_filter(array_merge(array($first), $last), 'strlen');
5080 $negotiator_email_addresses_string = join(' and ', $both);
5081 }
5082 if ( !empty($negotiator_telephone_numbers) )
5083 {
5084 $last = array_slice($negotiator_telephone_numbers, -1);
5085 $first = join(', ', array_slice($negotiator_telephone_numbers, 0, -1));
5086 $both = array_filter(array_merge(array($first), $last), 'strlen');
5087 $negotiator_telephone_numbers_string = join(' and ', $both);
5088 }
5089
5090 $subject = str_replace('[property_address]', $property->get_formatted_full_address(), $subject);
5091 $subject = str_replace('[applicant_name]', $applicant_names_string, $subject);
5092 $subject = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5093 $subject = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5094 $subject = str_replace('[negotiator_name]', $negotiator_names_string, $subject);
5095 $subject = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $subject);
5096 $subject = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $subject);
5097
5098 $subject = apply_filters( 'viewing_applicant_booking_confirmation_email_subject', $subject, $post_id, $property_id );
5099
5100 $body = str_replace('[property_address]', $property->get_formatted_full_address(), $body);
5101 $body = str_replace('[applicant_name]', $applicant_names_string, $body);
5102 $body = str_replace('[applicant_dear]', $applicant_dears_string, $body);
5103 $body = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5104 $body = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5105 $body = str_replace('[negotiator_name]', $negotiator_names_string, $body);
5106 $body = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $body);
5107 $body = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $body);
5108
5109 $body = html_entity_decode($body);
5110
5111 $body = apply_filters( 'viewing_applicant_booking_confirmation_email_body', $body, $post_id, $property_id );
5112
5113 $from = '';
5114 $from_setting = get_option( 'propertyhive_confirmations_default_from', '' );
5115 if ( $from_setting == 'user' )
5116 {
5117 $current_user = wp_get_current_user();
5118 $from = ( isset($current_user->user_email) ? $current_user->user_email : '' );
5119
5120 if ( $from == '' )
5121 {
5122 $from = $property->office_email_address;
5123 }
5124 }
5125 if ( $from_setting == 'office' )
5126 {
5127 $from = $property->office_email_address;
5128 }
5129 if ( $from == '' )
5130 {
5131 $from = get_option('propertyhive_email_from_address', '');
5132 }
5133 if ( $from == '' )
5134 {
5135 $from = get_bloginfo('admin_email');
5136 }
5137
5138 $attachments = array();
5139 if ( isset($_FILES['attachments']) && !empty($_FILES['attachments']['name'][0]) )
5140 {
5141 $uploaded_files = $_FILES['attachments'];
5142
5143 // Handle each file upload
5144 foreach ($uploaded_files['name'] as $key => $value)
5145 {
5146 if ($uploaded_files['name'][$key])
5147 {
5148 $file = array(
5149 'name' => $uploaded_files['name'][$key],
5150 'type' => $uploaded_files['type'][$key],
5151 'tmp_name' => $uploaded_files['tmp_name'][$key],
5152 'error' => $uploaded_files['error'][$key],
5153 'size' => $uploaded_files['size'][$key]
5154 );
5155
5156 // Move the file to a temporary location
5157 $upload_overrides = array('test_form' => false);
5158 $movefile = wp_handle_upload($file, $upload_overrides);
5159
5160 if ($movefile && !isset($movefile['error']))
5161 {
5162 // Add the file path to attachments array
5163 $attachments[] = $movefile['file'];
5164 }
5165 else
5166 {
5167 // Handle error in file upload
5168 wp_send_json_error($movefile['error']);
5169 }
5170 }
5171 }
5172 }
5173
5174 $headers = array();
5175 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
5176 $headers[] = 'Reply-To: ' . sanitize_email($from);
5177 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
5178
5179 $headers = apply_filters( 'propertyhive_viewing_applicant_booking_confirmation_email_headers', $headers );
5180
5181 $sent = wp_mail($to, $subject, $body, $headers, $attachments);
5182
5183 foreach ($attachments as $temp_file)
5184 {
5185 @unlink($temp_file);
5186 }
5187
5188 if ( !$sent )
5189 {
5190 wp_send_json_error('Failed to send email');
5191 }
5192
5193 update_post_meta( $post_id, '_applicant_booking_confirmation_sent_at', date("Y-m-d H:i:s") );
5194
5195 if ( apply_filters( 'propertyhive_log_booking_confirmation_emails', false ) === true )
5196 {
5197 // Add note/comment to viewing
5198 $comment = array(
5199 'note_type' => 'action',
5200 'action' => 'viewing_applicant_booking_confirmation_email',
5201 );
5202
5203 PH_Comments::insert_note( $post_id, $comment );
5204 }
5205
5206 wp_send_json_success();
5207 }
5208 else
5209 {
5210 wp_send_json_error('No valid recipient email addresses');
5211 }
5212
5213 wp_die();
5214 }
5215
5216 public function viewing_email_owner_booking_confirmation()
5217 {
5218 check_ajax_referer( 'viewing-actions', 'security' );
5219
5220 $post_id = (int)$_POST['viewing_id'];
5221
5222 $property_id = get_post_meta( $post_id, '_property_id', TRUE );
5223 $property_department = get_post_meta( $property_id, '_department' );
5224
5225 $applicant_contact_ids = get_post_meta( $post_id, '_applicant_contact_id' );
5226 $owner_contact_ids = get_post_meta( $property_id, '_owner_contact_id', TRUE );
5227
5228 if ( $owner_contact_ids > 0 ) {
5229
5230 $owner_emails = array();
5231 $owner_names = array();
5232 $owner_dears = array();
5233
5234 foreach ($owner_contact_ids as $owner_id)
5235 {
5236 $owner_contact = new PH_Contact($owner_id);
5237
5238 $owner_name = $owner_contact->post_title;
5239 $owner_dear = $owner_contact->dear();
5240
5241 if( ! empty($owner_name) ) array_push($owner_names, $owner_name);
5242 if( ! empty($owner_dear) ) array_push($owner_dears, $owner_dear);
5243
5244 $owner_email = $owner_contact->email_address;
5245 $explode_owner_email = explode( ",", $owner_email );
5246 foreach ( $explode_owner_email as $email_address )
5247 {
5248 $owner_emails[] = sanitize_email($email_address);
5249 }
5250 }
5251
5252 $owner_names_string = $this->get_list_string($owner_names);
5253 $owner_dears_string = $this->get_list_string($owner_dears);
5254
5255 if ( !empty($applicant_contact_ids) )
5256 {
5257 $applicant_names = array();
5258 $applicant_dears = array();
5259 foreach ($applicant_contact_ids as $applicant_contact_id)
5260 {
5261 $applicant_contact = new PH_Contact($applicant_contact_id);
5262 $applicant_names[] = $applicant_contact->post_title;
5263 $applicant_dears[] = $applicant_contact->dear();
5264 }
5265 $applicant_names = array_filter($applicant_names);
5266 $applicant_dears = array_filter($applicant_dears);
5267 }
5268
5269 $applicant_names_string = $this->get_list_string($applicant_names);
5270 $applicant_dears_string = $this->get_list_string($applicant_dears);
5271
5272 $negotiator_names = array();
5273 $negotiator_names_string = '';
5274
5275 $negotiator_email_addresses = array();
5276 $negotiator_email_addresses_string = '';
5277
5278 $negotiator_telephone_numbers = array();
5279 $negotiator_telephone_numbers_string = '';
5280
5281 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
5282 if ( !empty($negotiator_ids) )
5283 {
5284 foreach ( $negotiator_ids as $negotiator_id )
5285 {
5286 $negotiator = get_user_by( 'id', $negotiator_id );
5287 if ( $negotiator !== false )
5288 {
5289 if ( isset($negotiator->display_name) && !empty($negotiator->display_name) )
5290 {
5291 $negotiator_names[] = $negotiator->display_name;
5292 }
5293
5294 if ( isset($negotiator->user_email) && !empty($negotiator->user_email) )
5295 {
5296 $negotiator_email_addresses[] = $negotiator->user_email;
5297 }
5298
5299 $telephone_number = get_user_meta( $negotiator_id, 'telephone_number', true );
5300 if ( !empty($telephone_number) )
5301 {
5302 $negotiator_telephone_numbers[] = $telephone_number;
5303 }
5304 }
5305 }
5306 }
5307 if ( !empty($negotiator_names) )
5308 {
5309 $last = array_slice($negotiator_names, -1);
5310 $first = join(', ', array_slice($negotiator_names, 0, -1));
5311 $both = array_filter(array_merge(array($first), $last), 'strlen');
5312 $negotiator_names_string = join(' and ', $both);
5313 }
5314 if ( !empty($negotiator_email_addresses) )
5315 {
5316 $last = array_slice($negotiator_email_addresses, -1);
5317 $first = join(', ', array_slice($negotiator_email_addresses, 0, -1));
5318 $both = array_filter(array_merge(array($first), $last), 'strlen');
5319 $negotiator_email_addresses_string = join(' and ', $both);
5320 }
5321 if ( !empty($negotiator_telephone_numbers) )
5322 {
5323 $last = array_slice($negotiator_telephone_numbers, -1);
5324 $first = join(', ', array_slice($negotiator_telephone_numbers, 0, -1));
5325 $both = array_filter(array_merge(array($first), $last), 'strlen');
5326 $negotiator_telephone_numbers_string = join(' and ', $both);
5327 }
5328
5329 $property = new PH_Property((int)$property_id);
5330
5331 $to = implode(",", $owner_emails);
5332
5333 $subject = isset($_POST['subject']) ? sanitize_text_field($_POST['subject']) : get_option( 'propertyhive_viewing_owner_booking_confirmation_email_subject', '' );
5334 $body = isset($_POST['body']) ? sanitize_textarea_field($_POST['body']) : get_option( 'propertyhive_viewing_owner_booking_confirmation_email_body', '' );
5335
5336 $subject = str_replace('[property_address]', $property->get_formatted_full_address(), $subject);
5337 $subject = str_replace('[owner_name]', $owner_names_string, $subject);
5338 $subject = str_replace('[applicant_name]', $applicant_names_string, $subject);
5339 $subject = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5340 $subject = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5341 $subject = str_replace('[negotiator_name]', $negotiator_names_string, $subject);
5342 $subject = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $subject);
5343 $subject = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $subject);
5344
5345 $subject = apply_filters( 'viewing_owner_booking_confirmation_email_subject', $subject, $post_id, $property_id );
5346
5347 $body = str_replace('[property_address]', $property->get_formatted_full_address(), $body);
5348 $body = str_replace('[owner_name]', $owner_names_string, $body);
5349 $body = str_replace('[owner_dear]', $owner_dears_string, $body);
5350 $body = str_replace('[applicant_name]', $applicant_names_string, $body);
5351 $body = str_replace('[applicant_dear]', $applicant_dears_string, $body);
5352 $body = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5353 $body = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5354 $body = str_replace('[negotiator_name]', $negotiator_names_string, $body);
5355 $body = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $body);
5356 $body = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $body);
5357
5358 $body = html_entity_decode($body);
5359
5360 $body = apply_filters( 'viewing_owner_booking_confirmation_email_body', $body, $post_id, $property_id );
5361
5362 $from = '';
5363 $from_setting = get_option( 'propertyhive_confirmations_default_from', '' );
5364 if ( $from_setting == 'user' )
5365 {
5366 $current_user = wp_get_current_user();
5367 $from = ( isset($current_user->user_email) ? $current_user->user_email : '' );
5368
5369 if ( $from == '' )
5370 {
5371 $from = $property->office_email_address;
5372 }
5373 }
5374 if ( $from_setting == 'office' )
5375 {
5376 $from = $property->office_email_address;
5377 }
5378 if ( $from == '' )
5379 {
5380 $from = get_option('propertyhive_email_from_address', '');
5381 }
5382 if ( $from == '' )
5383 {
5384 $from = get_bloginfo('admin_email');
5385 }
5386
5387 $attachments = array();
5388 if ( isset($_FILES['attachments']) && !empty($_FILES['attachments']['name'][0]) )
5389 {
5390 $uploaded_files = $_FILES['attachments'];
5391
5392 // Handle each file upload
5393 foreach ($uploaded_files['name'] as $key => $value)
5394 {
5395 if ($uploaded_files['name'][$key])
5396 {
5397 $file = array(
5398 'name' => $uploaded_files['name'][$key],
5399 'type' => $uploaded_files['type'][$key],
5400 'tmp_name' => $uploaded_files['tmp_name'][$key],
5401 'error' => $uploaded_files['error'][$key],
5402 'size' => $uploaded_files['size'][$key]
5403 );
5404
5405 // Move the file to a temporary location
5406 $upload_overrides = array('test_form' => false);
5407 $movefile = wp_handle_upload($file, $upload_overrides);
5408
5409 if ($movefile && !isset($movefile['error']))
5410 {
5411 // Add the file path to attachments array
5412 $attachments[] = $movefile['file'];
5413 }
5414 else
5415 {
5416 // Handle error in file upload
5417 wp_send_json_error($movefile['error']);
5418 }
5419 }
5420 }
5421 }
5422
5423 $headers = array();
5424 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
5425 $headers[] = 'Reply-To: ' . sanitize_email($from);
5426 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
5427
5428 $headers = apply_filters( 'propertyhive_viewing_owner_booking_confirmation_email_headers', $headers );
5429
5430 $sent = wp_mail($to, $subject, $body, $headers, $attachments);
5431
5432 foreach ($attachments as $temp_file)
5433 {
5434 @unlink($temp_file);
5435 }
5436
5437 if ( !$sent )
5438 {
5439 wp_send_json_error('Failed to send email');
5440 }
5441
5442 if ( apply_filters( 'propertyhive_log_booking_confirmation_emails', false ) === true )
5443 {
5444 // Add note/comment to viewing
5445 $comment = array(
5446 'note_type' => 'action',
5447 'action' => 'viewing_owner_booking_confirmation_email',
5448 );
5449
5450 PH_Comments::insert_note( $post_id, $comment );
5451 }
5452
5453 update_post_meta( $post_id, '_owner_booking_confirmation_sent_at', date("Y-m-d H:i:s") );
5454
5455 wp_send_json_success();
5456 }
5457 else
5458 {
5459 wp_send_json_error('No owner recipients');
5460 }
5461
5462 wp_die();
5463 }
5464
5465 public function viewing_email_attending_negotiator_booking_confirmation()
5466 {
5467 check_ajax_referer( 'viewing-actions', 'security' );
5468
5469 $post_id = (int)$_POST['viewing_id'];
5470 $property_id = get_post_meta( $post_id, '_property_id', TRUE );
5471
5472 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
5473
5474 $applicant_contact_ids = get_post_meta( $post_id, '_applicant_contact_id' );
5475 $owner_contact_ids = get_post_meta( $property_id, '_owner_contact_id', TRUE );
5476
5477 if ( !empty($negotiator_ids) ) {
5478
5479 $tos = array();
5480 foreach ($negotiator_ids as $negotiator_id)
5481 {
5482 $user_info = get_userdata((int)$negotiator_id);
5483 $tos[] = sanitize_email($user_info->user_email);
5484 }
5485 $to = implode(",", $tos);
5486
5487 $owner_emails = array();
5488 $owner_names = array();
5489 $owner_dears = array();
5490 $owner_details = array();
5491
5492 if ( !empty($owner_contact_ids) )
5493 {
5494 foreach ($owner_contact_ids as $owner_id)
5495 {
5496 $owner_contact = new PH_Contact($owner_id);
5497
5498 $owner_name = $owner_contact->post_title;
5499 $owner_dear = $owner_contact->dear();
5500
5501 if( ! empty($owner_name) ) array_push($owner_names, $owner_name);
5502 if( ! empty($owner_dear) ) array_push($owner_dears, $owner_dear);
5503
5504 $owner_email = $owner_contact->email_address;
5505 $explode_owner_email = explode( ",", $owner_email );
5506 foreach ( $explode_owner_email as $email_address )
5507 {
5508 $owner_emails[] = sanitize_email($email_address);
5509 }
5510
5511 $owner_details[] = $owner_contact->post_title . "\nT: " . $owner_contact->telephone_number . "\nE: " . $owner_contact->email_address;
5512 }
5513 }
5514
5515 $owner_details = implode("\n\n", $owner_details);
5516
5517 $owner_names_string = $this->get_list_string($owner_names);
5518 $owner_dears_string = $this->get_list_string($owner_dears);
5519
5520 $applicant_names = array();
5521 $applicant_dears = array();
5522 $applicant_details = array();
5523
5524 if ( !empty($applicant_contact_ids) )
5525 {
5526 foreach ($applicant_contact_ids as $applicant_contact_id)
5527 {
5528 $applicant_contact = new PH_Contact($applicant_contact_id);
5529 $applicant_names[] = $applicant_contact->post_title;
5530 $applicant_dears[] = $applicant_contact->dear();
5531
5532 $applicant_details[] = $applicant_contact->post_title . "\nT: " . $applicant_contact->telephone_number . "\nE: " . $applicant_contact->email_address;
5533 }
5534 }
5535
5536 $applicant_details = implode("\n\n", $applicant_details);
5537
5538 $applicant_names = array_filter($applicant_names);
5539 $applicant_dears = array_filter($applicant_dears);
5540
5541 $applicant_names_string = $this->get_list_string($applicant_names);
5542 $applicant_dears_string = $this->get_list_string($applicant_dears);
5543
5544 $negotiator_names = array();
5545 $negotiator_names_string = '';
5546
5547 $negotiator_email_addresses = array();
5548 $negotiator_email_addresses_string = '';
5549
5550 $negotiator_telephone_numbers = array();
5551 $negotiator_telephone_numbers_string = '';
5552
5553 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
5554 if ( !empty($negotiator_ids) )
5555 {
5556 foreach ( $negotiator_ids as $negotiator_id )
5557 {
5558 $negotiator = get_user_by( 'id', $negotiator_id );
5559 if ( $negotiator !== false )
5560 {
5561 if ( isset($negotiator->display_name) && !empty($negotiator->display_name) )
5562 {
5563 $negotiator_names[] = $negotiator->display_name;
5564 }
5565
5566 if ( isset($negotiator->user_email) && !empty($negotiator->user_email) )
5567 {
5568 $negotiator_email_addresses[] = $negotiator->user_email;
5569 }
5570
5571 $telephone_number = get_user_meta( $negotiator_id, 'telephone_number', true );
5572 if ( !empty($telephone_number) )
5573 {
5574 $negotiator_telephone_numbers[] = $telephone_number;
5575 }
5576 }
5577 }
5578 }
5579 if ( !empty($negotiator_names) )
5580 {
5581 $last = array_slice($negotiator_names, -1);
5582 $first = join(', ', array_slice($negotiator_names, 0, -1));
5583 $both = array_filter(array_merge(array($first), $last), 'strlen');
5584 $negotiator_names_string = join(' and ', $both);
5585 }
5586 if ( !empty($negotiator_email_addresses) )
5587 {
5588 $last = array_slice($negotiator_email_addresses, -1);
5589 $first = join(', ', array_slice($negotiator_email_addresses, 0, -1));
5590 $both = array_filter(array_merge(array($first), $last), 'strlen');
5591 $negotiator_email_addresses_string = join(' and ', $both);
5592 }
5593 if ( !empty($negotiator_telephone_numbers) )
5594 {
5595 $last = array_slice($negotiator_telephone_numbers, -1);
5596 $first = join(', ', array_slice($negotiator_telephone_numbers, 0, -1));
5597 $both = array_filter(array_merge(array($first), $last), 'strlen');
5598 $negotiator_telephone_numbers_string = join(' and ', $both);
5599 }
5600
5601 $property = new PH_Property((int)$property_id);
5602
5603 $subject = isset($_POST['subject']) ? sanitize_text_field($_POST['subject']) : get_option( 'propertyhive_viewing_attending_negotiator_booking_confirmation_email_subject', '' );
5604 $body = isset($_POST['body']) ? sanitize_textarea_field($_POST['body']) : get_option( 'propertyhive_viewing_attending_negotiator_booking_confirmation_email_body', '' );
5605
5606 $subject = str_replace('[property_address]', $property->get_formatted_full_address(), $subject);
5607 $subject = str_replace('[owner_name]', $owner_names_string, $subject);
5608 $subject = str_replace('[applicant_name]', $applicant_names_string, $subject);
5609 $subject = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5610 $subject = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5611 $subject = str_replace('[negotiator_name]', $negotiator_names_string, $subject);
5612 $subject = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $subject);
5613 $subject = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $subject);
5614
5615 $subject = apply_filters( 'viewing_attending_negotiator_booking_confirmation_email_subject', $subject, $post_id, $property_id );
5616
5617 $body = str_replace('[property_address]', $property->get_formatted_full_address(), $body);
5618 $body = str_replace('[owner_name]', $owner_names_string, $body);
5619 $body = str_replace('[owner_dear]', $owner_dears_string, $body);
5620 $body = str_replace('[owner_details]', $owner_details, $body);
5621 $body = str_replace('[applicant_name]', $applicant_names_string, $body);
5622 $body = str_replace('[applicant_dear]', $applicant_dears_string, $body);
5623 $body = str_replace('[applicant_details]', $applicant_details, $body);
5624 $body = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5625 $body = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5626 $body = str_replace('[negotiator_name]', $negotiator_names_string, $body);
5627 $body = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $body);
5628 $body = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $body);
5629
5630 $body = html_entity_decode($body);
5631
5632 $body = apply_filters( 'viewing_attending_negotiator_booking_confirmation_email_body', $body, $post_id, $property_id );
5633
5634 $from = '';
5635 $from_setting = get_option( 'propertyhive_confirmations_default_from', '' );
5636 if ( $from_setting == 'user' )
5637 {
5638 $current_user = wp_get_current_user();
5639 $from = ( isset($current_user->user_email) ? $current_user->user_email : '' );
5640
5641 if ( $from == '' )
5642 {
5643 $from = $property->office_email_address;
5644 }
5645 }
5646 if ( $from_setting == 'office' )
5647 {
5648 $from = $property->office_email_address;
5649 }
5650 if ( $from == '' )
5651 {
5652 $from = get_option('propertyhive_email_from_address', '');
5653 }
5654 if ( $from == '' )
5655 {
5656 $from = get_bloginfo('admin_email');
5657 }
5658
5659 $attachments = array();
5660 if ( isset($_FILES['attachments']) && !empty($_FILES['attachments']['name'][0]) )
5661 {
5662 $uploaded_files = $_FILES['attachments'];
5663
5664 // Handle each file upload
5665 foreach ($uploaded_files['name'] as $key => $value)
5666 {
5667 if ($uploaded_files['name'][$key])
5668 {
5669 $file = array(
5670 'name' => $uploaded_files['name'][$key],
5671 'type' => $uploaded_files['type'][$key],
5672 'tmp_name' => $uploaded_files['tmp_name'][$key],
5673 'error' => $uploaded_files['error'][$key],
5674 'size' => $uploaded_files['size'][$key]
5675 );
5676
5677 // Move the file to a temporary location
5678 $upload_overrides = array('test_form' => false);
5679 $movefile = wp_handle_upload($file, $upload_overrides);
5680
5681 if ($movefile && !isset($movefile['error']))
5682 {
5683 // Add the file path to attachments array
5684 $attachments[] = $movefile['file'];
5685 }
5686 else
5687 {
5688 // Handle error in file upload
5689 wp_send_json_error($movefile['error']);
5690 }
5691 }
5692 }
5693 }
5694
5695 $headers = array();
5696 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
5697 $headers[] = 'Reply-To: ' . sanitize_email($from);
5698 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
5699
5700 $headers = apply_filters( 'propertyhive_viewing_attending_negotiator_booking_confirmation_email_headers', $headers );
5701
5702 $sent = wp_mail($to, $subject, $body, $headers, $attachments);
5703
5704 foreach ($attachments as $temp_file)
5705 {
5706 @unlink($temp_file);
5707 }
5708
5709 if ( !$sent )
5710 {
5711 wp_send_json_error('Failed to send email');
5712 }
5713
5714 // Add note/comment to viewing
5715 if ( apply_filters( 'propertyhive_log_booking_confirmation_emails', false ) === true )
5716 {
5717 $comment = array(
5718 'note_type' => 'action',
5719 'action' => 'viewing_attending_negotiator_booking_confirmation_email',
5720 );
5721
5722 PH_Comments::insert_note( $post_id, $comment );
5723 }
5724
5725 update_post_meta( $post_id, '_attending_negotiator_booking_confirmation_sent_at', date("Y-m-d H:i:s") );
5726
5727 wp_send_json_success();
5728 }
5729 else
5730 {
5731 wp_send_json_error('No attending negotiator recipients');
5732 }
5733
5734 wp_die();
5735 }
5736
5737 public function viewing_email_applicant_cancellation_notification()
5738 {
5739 check_ajax_referer( 'viewing-actions', 'security' );
5740
5741 $post_id = (int)$_POST['viewing_id'];
5742
5743 $applicant_contact_ids = get_post_meta( $post_id, '_applicant_contact_id' );
5744 $property_id = get_post_meta( $post_id, '_property_id', TRUE );
5745
5746 if ( !is_array($applicant_contact_ids) || (int)$property_id == '' || count($applicant_contact_ids) == 0 || (int)$property_id == 0 )
5747 {
5748 wp_send_json_error('Missing contact or property');
5749 }
5750
5751 $property = new PH_Property((int)$property_id);
5752
5753 $to = array();
5754 foreach ($applicant_contact_ids as $applicant_contact_id)
5755 {
5756 $applicant_email_address = get_post_meta( $applicant_contact_id, '_email_address', TRUE );
5757 $explode_applicant_email_address = explode( ",", $applicant_email_address );
5758 foreach ( $explode_applicant_email_address as $email_address )
5759 {
5760 $to[] = sanitize_email($email_address);
5761 }
5762 }
5763
5764 $to = array_filter($to);
5765
5766 if ( !empty(implode($to)) )
5767 {
5768 $subject = isset($_POST['subject']) ? sanitize_text_field($_POST['subject']) : get_option( 'propertyhive_viewing_applicant_cancellation_notification_email_subject', '' );
5769 $body = isset($_POST['body']) ? sanitize_textarea_field($_POST['body']) : get_option( 'propertyhive_viewing_applicant_cancellation_notification_email_body', '' );
5770
5771 $applicant_names = array();
5772 $applicant_dears = array();
5773 foreach ($applicant_contact_ids as $applicant_contact_id)
5774 {
5775 $applicant_contact = new PH_Contact($applicant_contact_id);
5776 $applicant_names[] = $applicant_contact->post_title;
5777 $applicant_dears[] = $applicant_contact->dear();
5778 }
5779 $applicant_names = array_filter($applicant_names);
5780 $applicant_dears = array_filter($applicant_dears);
5781
5782 $applicant_names_string = $this->get_list_string($applicant_names);
5783 $applicant_dears_string = $this->get_list_string($applicant_dears);
5784
5785 $negotiator_names = array();
5786 $negotiator_names_string = '';
5787
5788 $negotiator_email_addresses = array();
5789 $negotiator_email_addresses_string = '';
5790
5791 $negotiator_telephone_numbers = array();
5792 $negotiator_telephone_numbers_string = '';
5793
5794 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
5795 if ( !empty($negotiator_ids) )
5796 {
5797 foreach ( $negotiator_ids as $negotiator_id )
5798 {
5799 $negotiator = get_user_by( 'id', $negotiator_id );
5800 if ( $negotiator !== false )
5801 {
5802 if ( isset($negotiator->display_name) && !empty($negotiator->display_name) )
5803 {
5804 $negotiator_names[] = $negotiator->display_name;
5805 }
5806
5807 if ( isset($negotiator->user_email) && !empty($negotiator->user_email) )
5808 {
5809 $negotiator_email_addresses[] = $negotiator->user_email;
5810 }
5811
5812 $telephone_number = get_user_meta( $negotiator_id, 'telephone_number', true );
5813 if ( !empty($telephone_number) )
5814 {
5815 $negotiator_telephone_numbers[] = $telephone_number;
5816 }
5817 }
5818 }
5819 }
5820 if ( !empty($negotiator_names) )
5821 {
5822 $last = array_slice($negotiator_names, -1);
5823 $first = join(', ', array_slice($negotiator_names, 0, -1));
5824 $both = array_filter(array_merge(array($first), $last), 'strlen');
5825 $negotiator_names_string = join(' and ', $both);
5826 }
5827 if ( !empty($negotiator_email_addresses) )
5828 {
5829 $last = array_slice($negotiator_email_addresses, -1);
5830 $first = join(', ', array_slice($negotiator_email_addresses, 0, -1));
5831 $both = array_filter(array_merge(array($first), $last), 'strlen');
5832 $negotiator_email_addresses_string = join(' and ', $both);
5833 }
5834 if ( !empty($negotiator_telephone_numbers) )
5835 {
5836 $last = array_slice($negotiator_telephone_numbers, -1);
5837 $first = join(', ', array_slice($negotiator_telephone_numbers, 0, -1));
5838 $both = array_filter(array_merge(array($first), $last), 'strlen');
5839 $negotiator_telephone_numbers_string = join(' and ', $both);
5840 }
5841
5842 $subject = str_replace('[property_address]', $property->get_formatted_full_address(), $subject);
5843 $subject = str_replace('[applicant_name]', $applicant_names_string, $subject);
5844 $subject = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5845 $subject = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
5846 $subject = str_replace('[negotiator_name]', $negotiator_names_string, $subject);
5847 $subject = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $subject);
5848 $subject = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $subject);
5849
5850 $subject = apply_filters( 'viewing_applicant_cancellation_notification_email_subject', $subject, $post_id, $property_id );
5851
5852 $body = str_replace('[property_address]', $property->get_formatted_full_address(), $body);
5853 $body = str_replace('[applicant_name]', $applicant_names_string, $body);
5854 $body = str_replace('[applicant_dear]', $applicant_dears_string, $body);
5855 $body = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5856 $body = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
5857 $body = str_replace('[negotiator_name]', $negotiator_names_string, $body);
5858 $body = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $body);
5859 $body = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $body);
5860
5861 $cancelled_reason = '';
5862 if (
5863 get_post_meta( $post_id, '_cancelled_reason_public', true ) == 'yes' &&
5864 get_post_meta( $post_id, '_cancelled_reason', true ) != ''
5865 )
5866 {
5867 $cancelled_reason .= "\n\nReason: " . get_post_meta( $post_id, '_cancelled_reason', true );
5868 }
5869 $body = str_replace('[cancelled_reason]', $cancelled_reason, $body);
5870
5871 $body = html_entity_decode($body);
5872
5873 $body = apply_filters( 'viewing_applicant_cancellation_notification_email_body', $body, $post_id, $property_id );
5874
5875 $from = '';
5876 $from_setting = get_option( 'propertyhive_confirmations_default_from', '' );
5877 if ( $from_setting == 'user' )
5878 {
5879 $current_user = wp_get_current_user();
5880 $from = ( isset($current_user->user_email) ? $current_user->user_email : '' );
5881
5882 if ( $from == '' )
5883 {
5884 $from = $property->office_email_address;
5885 }
5886 }
5887 if ( $from_setting == 'office' )
5888 {
5889 $from = $property->office_email_address;
5890 }
5891 if ( $from == '' )
5892 {
5893 $from = get_option('propertyhive_email_from_address', '');
5894 }
5895 if ( $from == '' )
5896 {
5897 $from = get_bloginfo('admin_email');
5898 }
5899
5900 $attachments = array();
5901 if ( isset($_FILES['attachments']) && !empty($_FILES['attachments']['name'][0]) )
5902 {
5903 $uploaded_files = $_FILES['attachments'];
5904
5905 // Handle each file upload
5906 foreach ($uploaded_files['name'] as $key => $value)
5907 {
5908 if ($uploaded_files['name'][$key])
5909 {
5910 $file = array(
5911 'name' => $uploaded_files['name'][$key],
5912 'type' => $uploaded_files['type'][$key],
5913 'tmp_name' => $uploaded_files['tmp_name'][$key],
5914 'error' => $uploaded_files['error'][$key],
5915 'size' => $uploaded_files['size'][$key]
5916 );
5917
5918 // Move the file to a temporary location
5919 $upload_overrides = array('test_form' => false);
5920 $movefile = wp_handle_upload($file, $upload_overrides);
5921
5922 if ($movefile && !isset($movefile['error']))
5923 {
5924 // Add the file path to attachments array
5925 $attachments[] = $movefile['file'];
5926 }
5927 else
5928 {
5929 // Handle error in file upload
5930 wp_send_json_error($movefile['error']);
5931 }
5932 }
5933 }
5934 }
5935
5936 $headers = array();
5937 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
5938 $headers[] = 'Reply-To: ' . sanitize_email($from);
5939 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
5940
5941 $headers = apply_filters( 'propertyhive_viewing_applicant_cancellation_notification_email_headers', $headers );
5942
5943 $sent = wp_mail($to, $subject, $body, $headers, $attachments);
5944
5945 foreach ($attachments as $temp_file)
5946 {
5947 @unlink($temp_file);
5948 }
5949
5950 if ( !$sent )
5951 {
5952 wp_send_json_error('Failed to send email');
5953 }
5954
5955 update_post_meta( $post_id, '_applicant_cancellation_notification_sent_at', date("Y-m-d H:i:s") );
5956
5957 if ( apply_filters( 'propertyhive_log_cancellation_notification_emails', false ) === true )
5958 {
5959 // Add note/comment to viewing
5960 $comment = array(
5961 'note_type' => 'action',
5962 'action' => 'viewing_applicant_cancellation_notification_email',
5963 );
5964
5965 PH_Comments::insert_note( $post_id, $comment );
5966 }
5967
5968 wp_send_json_success();
5969 }
5970 else
5971 {
5972 wp_send_json_error('No valid recipient email addresses');
5973 }
5974
5975 wp_die();
5976 }
5977
5978 public function viewing_email_owner_cancellation_notification()
5979 {
5980 check_ajax_referer( 'viewing-actions', 'security' );
5981
5982 $post_id = (int)$_POST['viewing_id'];
5983
5984 $property_id = get_post_meta( $post_id, '_property_id', TRUE );
5985 $property_department = get_post_meta( $property_id, '_department' );
5986
5987 $applicant_contact_ids = get_post_meta( $post_id, '_applicant_contact_id' );
5988 $owner_contact_ids = get_post_meta( $property_id, '_owner_contact_id', TRUE );
5989
5990 if ( $owner_contact_ids > 0 ) {
5991
5992 $owner_emails = array();
5993 $owner_names = array();
5994 $owner_dears = array();
5995
5996 foreach ($owner_contact_ids as $owner_id)
5997 {
5998 $owner_contact = new PH_Contact($owner_id);
5999
6000 $owner_name = $owner_contact->post_title;
6001 $owner_dear = $owner_contact->dear();
6002
6003 if( ! empty($owner_name) ) array_push($owner_names, $owner_name);
6004 if( ! empty($owner_dear) ) array_push($owner_dears, $owner_dear);
6005
6006 $owner_email = $owner_contact->email_address;
6007 $explode_owner_email = explode( ",", $owner_email );
6008 foreach ( $explode_owner_email as $email_address )
6009 {
6010 $owner_emails[] = sanitize_email($email_address);
6011 }
6012 }
6013
6014 $owner_names_string = $this->get_list_string($owner_names);
6015 $owner_dears_string = $this->get_list_string($owner_dears);
6016
6017 if ( !empty($applicant_contact_ids) )
6018 {
6019 $applicant_names = array();
6020 $applicant_dears = array();
6021 foreach ($applicant_contact_ids as $applicant_contact_id)
6022 {
6023 $applicant_contact = new PH_Contact($applicant_contact_id);
6024 $applicant_names[] = $applicant_contact->post_title;
6025 $applicant_dears[] = $applicant_contact->dear();
6026 }
6027 $applicant_names = array_filter($applicant_names);
6028 $applicant_dears = array_filter($applicant_dears);
6029 }
6030
6031 $applicant_names_string = $this->get_list_string($applicant_names);
6032 $applicant_dears_string = $this->get_list_string($applicant_dears);
6033
6034 $negotiator_names = array();
6035 $negotiator_names_string = '';
6036
6037 $negotiator_email_addresses = array();
6038 $negotiator_email_addresses_string = '';
6039
6040 $negotiator_telephone_numbers = array();
6041 $negotiator_telephone_numbers_string = '';
6042
6043 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
6044 if ( !empty($negotiator_ids) )
6045 {
6046 foreach ( $negotiator_ids as $negotiator_id )
6047 {
6048 $negotiator = get_user_by( 'id', $negotiator_id );
6049 if ( $negotiator !== false )
6050 {
6051 if ( isset($negotiator->display_name) && !empty($negotiator->display_name) )
6052 {
6053 $negotiator_names[] = $negotiator->display_name;
6054 }
6055
6056 if ( isset($negotiator->user_email) && !empty($negotiator->user_email) )
6057 {
6058 $negotiator_email_addresses[] = $negotiator->user_email;
6059 }
6060
6061 $telephone_number = get_user_meta( $negotiator_id, 'telephone_number', true );
6062 if ( !empty($telephone_number) )
6063 {
6064 $negotiator_telephone_numbers[] = $telephone_number;
6065 }
6066 }
6067 }
6068 }
6069 if ( !empty($negotiator_names) )
6070 {
6071 $last = array_slice($negotiator_names, -1);
6072 $first = join(', ', array_slice($negotiator_names, 0, -1));
6073 $both = array_filter(array_merge(array($first), $last), 'strlen');
6074 $negotiator_names_string = join(' and ', $both);
6075 }
6076 if ( !empty($negotiator_email_addresses) )
6077 {
6078 $last = array_slice($negotiator_email_addresses, -1);
6079 $first = join(', ', array_slice($negotiator_email_addresses, 0, -1));
6080 $both = array_filter(array_merge(array($first), $last), 'strlen');
6081 $negotiator_email_addresses_string = join(' and ', $both);
6082 }
6083 if ( !empty($negotiator_telephone_numbers) )
6084 {
6085 $last = array_slice($negotiator_telephone_numbers, -1);
6086 $first = join(', ', array_slice($negotiator_telephone_numbers, 0, -1));
6087 $both = array_filter(array_merge(array($first), $last), 'strlen');
6088 $negotiator_telephone_numbers_string = join(' and ', $both);
6089 }
6090
6091 $property = new PH_Property((int)$property_id);
6092
6093 $to = implode(",", $owner_emails);
6094
6095 $subject = isset($_POST['subject']) ? sanitize_text_field($_POST['subject']) : get_option( 'propertyhive_viewing_owner_cancellation_notification_email_subject', '' );
6096 $body = isset($_POST['body']) ? sanitize_textarea_field($_POST['body']) : get_option( 'propertyhive_viewing_owner_cancellation_notification_email_body', '' );
6097
6098 $subject = str_replace('[property_address]', $property->get_formatted_full_address(), $subject);
6099 $subject = str_replace('[owner_name]', $owner_names_string, $subject);
6100 $subject = str_replace('[applicant_name]', $applicant_names_string, $subject);
6101 $subject = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
6102 $subject = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
6103 $subject = str_replace('[negotiator_name]', $negotiator_names_string, $subject);
6104 $subject = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $subject);
6105 $subject = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $subject);
6106
6107 $subject = apply_filters( 'viewing_owner_cancellation_notification_email_subject', $subject, $post_id, $property_id );
6108
6109 $body = str_replace('[property_address]', $property->get_formatted_full_address(), $body);
6110 $body = str_replace('[owner_name]', $owner_names_string, $body);
6111 $body = str_replace('[owner_dear]', $owner_dears_string, $body);
6112 $body = str_replace('[applicant_name]', $applicant_names_string, $body);
6113 $body = str_replace('[applicant_dear]', $applicant_dears_string, $body);
6114 $body = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
6115 $body = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
6116 $body = str_replace('[negotiator_name]', $negotiator_names_string, $body);
6117 $body = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $body);
6118 $body = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $body);
6119
6120 $cancelled_reason = '';
6121 if (
6122 get_post_meta( $post_id, '_cancelled_reason_public', true ) == 'yes' &&
6123 get_post_meta( $post_id, '_cancelled_reason', true ) != ''
6124 )
6125 {
6126 $cancelled_reason .= "\n\nReason: " . get_post_meta( $post_id, '_cancelled_reason', true );
6127 }
6128 $body = str_replace('[cancelled_reason]', $cancelled_reason, $body);
6129
6130 $body = html_entity_decode($body);
6131
6132 $body = apply_filters( 'viewing_owner_cancellation_notification_email_body', $body, $post_id, $property_id );
6133
6134 $from = '';
6135 $from_setting = get_option( 'propertyhive_confirmations_default_from', '' );
6136 if ( $from_setting == 'user' )
6137 {
6138 $current_user = wp_get_current_user();
6139 $from = ( isset($current_user->user_email) ? $current_user->user_email : '' );
6140
6141 if ( $from == '' )
6142 {
6143 $from = $property->office_email_address;
6144 }
6145 }
6146 if ( $from_setting == 'office' )
6147 {
6148 $from = $property->office_email_address;
6149 }
6150 if ( $from == '' )
6151 {
6152 $from = get_option('propertyhive_email_from_address', '');
6153 }
6154 if ( $from == '' )
6155 {
6156 $from = get_bloginfo('admin_email');
6157 }
6158
6159 $attachments = array();
6160 if ( isset($_FILES['attachments']) && !empty($_FILES['attachments']['name'][0]) )
6161 {
6162 $uploaded_files = $_FILES['attachments'];
6163
6164 // Handle each file upload
6165 foreach ($uploaded_files['name'] as $key => $value)
6166 {
6167 if ($uploaded_files['name'][$key])
6168 {
6169 $file = array(
6170 'name' => $uploaded_files['name'][$key],
6171 'type' => $uploaded_files['type'][$key],
6172 'tmp_name' => $uploaded_files['tmp_name'][$key],
6173 'error' => $uploaded_files['error'][$key],
6174 'size' => $uploaded_files['size'][$key]
6175 );
6176
6177 // Move the file to a temporary location
6178 $upload_overrides = array('test_form' => false);
6179 $movefile = wp_handle_upload($file, $upload_overrides);
6180
6181 if ($movefile && !isset($movefile['error']))
6182 {
6183 // Add the file path to attachments array
6184 $attachments[] = $movefile['file'];
6185 }
6186 else
6187 {
6188 // Handle error in file upload
6189 wp_send_json_error($movefile['error']);
6190 }
6191 }
6192 }
6193 }
6194
6195 $headers = array();
6196 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
6197 $headers[] = 'Reply-To: ' . sanitize_email($from);
6198 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
6199
6200 $headers = apply_filters( 'propertyhive_viewing_owner_cancellation_notification_email_headers', $headers );
6201
6202 $sent = wp_mail($to, $subject, $body, $headers, $attachments);
6203
6204 foreach ($attachments as $temp_file)
6205 {
6206 @unlink($temp_file);
6207 }
6208
6209 if ( !$sent )
6210 {
6211 wp_send_json_error('Failed to send email');
6212 }
6213
6214 if ( apply_filters( 'propertyhive_log_cancellation_notification_emails', false ) === true )
6215 {
6216 // Add note/comment to viewing
6217 $comment = array(
6218 'note_type' => 'action',
6219 'action' => 'viewing_owner_cancellation_notification_email',
6220 );
6221
6222 PH_Comments::insert_note( $post_id, $comment );
6223 }
6224
6225 update_post_meta( $post_id, '_owner_cancellation_notification_sent_at', date("Y-m-d H:i:s") );
6226
6227 wp_send_json_success();
6228 }
6229 else
6230 {
6231 wp_send_json_error('No owner recipients');
6232 }
6233
6234 wp_die();
6235 }
6236
6237 public function viewing_email_attending_negotiator_cancellation_notification()
6238 {
6239 check_ajax_referer( 'viewing-actions', 'security' );
6240
6241 $post_id = (int)$_POST['viewing_id'];
6242 $property_id = get_post_meta( $post_id, '_property_id', TRUE );
6243
6244 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
6245
6246 $applicant_contact_ids = get_post_meta( $post_id, '_applicant_contact_id' );
6247 $owner_contact_ids = get_post_meta( $property_id, '_owner_contact_id', TRUE );
6248
6249 if ( !empty($negotiator_ids) ) {
6250
6251 $tos = array();
6252 foreach ($negotiator_ids as $negotiator_id)
6253 {
6254 $user_info = get_userdata((int)$negotiator_id);
6255 $tos[] = sanitize_email($user_info->user_email);
6256 }
6257 $to = implode(",", $tos);
6258
6259 $owner_emails = array();
6260 $owner_names = array();
6261 $owner_dears = array();
6262 $owner_details = array();
6263
6264 if ( !empty($owner_contact_ids) )
6265 {
6266 foreach ($owner_contact_ids as $owner_id)
6267 {
6268 $owner_contact = new PH_Contact($owner_id);
6269
6270 $owner_name = $owner_contact->post_title;
6271 $owner_dear = $owner_contact->dear();
6272
6273 if( ! empty($owner_name) ) array_push($owner_names, $owner_name);
6274 if( ! empty($owner_dear) ) array_push($owner_dears, $owner_dear);
6275
6276 $owner_email = $owner_contact->email_address;
6277 $explode_owner_email = explode( ",", $owner_email );
6278 foreach ( $explode_owner_email as $email_address )
6279 {
6280 $owner_emails[] = sanitize_email($email_address);
6281 }
6282
6283 $owner_details[] = $owner_contact->post_title . "\nT: " . $owner_contact->telephone_number . "\nE: " . $owner_contact->email_address;
6284 }
6285 }
6286
6287 $owner_details = implode("\n\n", $owner_details);
6288
6289 $owner_names_string = $this->get_list_string($owner_names);
6290 $owner_dears_string = $this->get_list_string($owner_dears);
6291
6292 $applicant_names = array();
6293 $applicant_dears = array();
6294 $applicant_details = array();
6295
6296 if ( !empty($applicant_contact_ids) )
6297 {
6298 foreach ($applicant_contact_ids as $applicant_contact_id)
6299 {
6300 $applicant_contact = new PH_Contact($applicant_contact_id);
6301 $applicant_names[] = $applicant_contact->post_title;
6302 $applicant_dears[] = $applicant_contact->dear();
6303
6304 $applicant_details[] = $applicant_contact->post_title . "\nT: " . $applicant_contact->telephone_number . "\nE: " . $applicant_contact->email_address;
6305 }
6306 }
6307
6308 $applicant_details = implode("\n\n", $applicant_details);
6309
6310 $applicant_names = array_filter($applicant_names);
6311 $applicant_dears = array_filter($applicant_dears);
6312
6313 $applicant_names_string = $this->get_list_string($applicant_names);
6314 $applicant_dears_string = $this->get_list_string($applicant_dears);
6315
6316 $negotiator_names = array();
6317 $negotiator_names_string = '';
6318
6319 $negotiator_email_addresses = array();
6320 $negotiator_email_addresses_string = '';
6321
6322 $negotiator_telephone_numbers = array();
6323 $negotiator_telephone_numbers_string = '';
6324
6325 $negotiator_ids = get_post_meta( $post_id, '_negotiator_id' );
6326 if ( !empty($negotiator_ids) )
6327 {
6328 foreach ( $negotiator_ids as $negotiator_id )
6329 {
6330 $negotiator = get_user_by( 'id', $negotiator_id );
6331 if ( $negotiator !== false )
6332 {
6333 if ( isset($negotiator->display_name) && !empty($negotiator->display_name) )
6334 {
6335 $negotiator_names[] = $negotiator->display_name;
6336 }
6337
6338 if ( isset($negotiator->user_email) && !empty($negotiator->user_email) )
6339 {
6340 $negotiator_email_addresses[] = $negotiator->user_email;
6341 }
6342
6343 $telephone_number = get_user_meta( $negotiator_id, 'telephone_number', true );
6344 if ( !empty($telephone_number) )
6345 {
6346 $negotiator_telephone_numbers[] = $telephone_number;
6347 }
6348 }
6349 }
6350 }
6351 if ( !empty($negotiator_names) )
6352 {
6353 $last = array_slice($negotiator_names, -1);
6354 $first = join(', ', array_slice($negotiator_names, 0, -1));
6355 $both = array_filter(array_merge(array($first), $last), 'strlen');
6356 $negotiator_names_string = join(' and ', $both);
6357 }
6358 if ( !empty($negotiator_email_addresses) )
6359 {
6360 $last = array_slice($negotiator_email_addresses, -1);
6361 $first = join(', ', array_slice($negotiator_email_addresses, 0, -1));
6362 $both = array_filter(array_merge(array($first), $last), 'strlen');
6363 $negotiator_email_addresses_string = join(' and ', $both);
6364 }
6365 if ( !empty($negotiator_telephone_numbers) )
6366 {
6367 $last = array_slice($negotiator_telephone_numbers, -1);
6368 $first = join(', ', array_slice($negotiator_telephone_numbers, 0, -1));
6369 $both = array_filter(array_merge(array($first), $last), 'strlen');
6370 $negotiator_telephone_numbers_string = join(' and ', $both);
6371 }
6372
6373 $property = new PH_Property((int)$property_id);
6374
6375 $subject = isset($_POST['subject']) ? sanitize_text_field($_POST['subject']) : get_option( 'propertyhive_viewing_attending_negotiator_cancellation_notification_email_subject', '' );
6376 $body = isset($_POST['body']) ? sanitize_textarea_field($_POST['body']) : get_option( 'propertyhive_viewing_attending_negotiator_cancellation_notification_email_body', '' );
6377
6378 $subject = str_replace('[property_address]', $property->get_formatted_full_address(), $subject);
6379 $subject = str_replace('[owner_name]', $owner_names_string, $subject);
6380 $subject = str_replace('[applicant_name]', $applicant_names_string, $subject);
6381 $subject = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
6382 $subject = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $subject);
6383 $subject = str_replace('[negotiator_name]', $negotiator_names_string, $subject);
6384 $subject = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $subject);
6385 $subject = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $subject);
6386
6387 $subject = apply_filters( 'viewing_attending_negotiator_cancellation_notification_email_subject', $subject, $post_id, $property_id );
6388
6389 $body = str_replace('[property_address]', $property->get_formatted_full_address(), $body);
6390 $body = str_replace('[owner_name]', $owner_names_string, $body);
6391 $body = str_replace('[owner_dear]', $owner_dears_string, $body);
6392 $body = str_replace('[owner_details]', $owner_details, $body);
6393 $body = str_replace('[applicant_name]', $applicant_names_string, $body);
6394 $body = str_replace('[applicant_dear]', $applicant_dears_string, $body);
6395 $body = str_replace('[applicant_details]', $applicant_details, $body);
6396 $body = str_replace('[viewing_time]', date("H:i", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
6397 $body = str_replace('[viewing_date]', date("l jS F Y", strtotime(get_post_meta( $post_id, '_start_date_time', true ))), $body);
6398 $body = str_replace('[negotiator_name]', $negotiator_names_string, $body);
6399 $body = str_replace('[negotiator_email_address]', $negotiator_email_addresses_string, $body);
6400 $body = str_replace('[negotiator_telephone_number]', $negotiator_telephone_numbers_string, $body);
6401
6402 $cancelled_reason = '';
6403 if (
6404 get_post_meta( $post_id, '_cancelled_reason_public', true ) == 'yes' &&
6405 get_post_meta( $post_id, '_cancelled_reason', true ) != ''
6406 )
6407 {
6408 $cancelled_reason .= "\n\nReason: " . get_post_meta( $post_id, '_cancelled_reason', true );
6409 }
6410 $body = str_replace('[cancelled_reason]', $cancelled_reason, $body);
6411
6412 $body = html_entity_decode($body);
6413
6414 $body = apply_filters( 'viewing_attending_negotiator_cancellation_notification_email_body', $body, $post_id, $property_id );
6415
6416 $from = '';
6417 $from_setting = get_option( 'propertyhive_confirmations_default_from', '' );
6418 if ( $from_setting == 'user' )
6419 {
6420 $current_user = wp_get_current_user();
6421 $from = ( isset($current_user->user_email) ? $current_user->user_email : '' );
6422
6423 if ( $from == '' )
6424 {
6425 $from = $property->office_email_address;
6426 }
6427 }
6428 if ( $from_setting == 'office' )
6429 {
6430 $from = $property->office_email_address;
6431 }
6432 if ( $from == '' )
6433 {
6434 $from = get_option('propertyhive_email_from_address', '');
6435 }
6436 if ( $from == '' )
6437 {
6438 $from = get_bloginfo('admin_email');
6439 }
6440
6441 $attachments = array();
6442 if ( isset($_FILES['attachments']) && !empty($_FILES['attachments']['name'][0]) )
6443 {
6444 $uploaded_files = $_FILES['attachments'];
6445
6446 // Handle each file upload
6447 foreach ($uploaded_files['name'] as $key => $value)
6448 {
6449 if ($uploaded_files['name'][$key])
6450 {
6451 $file = array(
6452 'name' => $uploaded_files['name'][$key],
6453 'type' => $uploaded_files['type'][$key],
6454 'tmp_name' => $uploaded_files['tmp_name'][$key],
6455 'error' => $uploaded_files['error'][$key],
6456 'size' => $uploaded_files['size'][$key]
6457 );
6458
6459 // Move the file to a temporary location
6460 $upload_overrides = array('test_form' => false);
6461 $movefile = wp_handle_upload($file, $upload_overrides);
6462
6463 if ($movefile && !isset($movefile['error']))
6464 {
6465 // Add the file path to attachments array
6466 $attachments[] = $movefile['file'];
6467 }
6468 else
6469 {
6470 // Handle error in file upload
6471 wp_send_json_error($movefile['error']);
6472 }
6473 }
6474 }
6475 }
6476
6477 $headers = array();
6478 $headers[] = 'From: ' . html_entity_decode(get_bloginfo('name')) . ' <' . sanitize_email($from) . '>';
6479 $headers[] = 'Reply-To: ' . sanitize_email($from);
6480 $headers[] = 'Content-Type: text/plain; charset=UTF-8';
6481
6482 $headers = apply_filters( 'propertyhive_viewing_attending_negotiator_cancellation_notification_email_headers', $headers );
6483
6484 $sent = wp_mail($to, $subject, $body, $headers, $attachments);
6485
6486 foreach ($attachments as $temp_file)
6487 {
6488 @unlink($temp_file);
6489 }
6490
6491 if ( !$sent )
6492 {
6493 wp_send_json_error('Failed to send email');
6494 }
6495
6496 // Add note/comment to viewing
6497 if ( apply_filters( 'propertyhive_log_cancellation_notification_emails', false ) === true )
6498 {
6499 $comment = array(
6500 'note_type' => 'action',
6501 'action' => 'viewing_attending_negotiator_cancellation_notification_email',
6502 );
6503
6504 PH_Comments::insert_note( $post_id, $comment );
6505 }
6506
6507 update_post_meta( $post_id, '_attending_negotiator_cancellation_notification_sent_at', date("Y-m-d H:i:s") );
6508
6509 wp_send_json_success();
6510 }
6511 else
6512 {
6513 wp_send_json_error('No attending negotiator recipients');
6514 }
6515
6516 wp_die();
6517 }
6518
6519 public function viewing_interested_feedback()
6520 {
6521 check_ajax_referer( 'viewing-actions', 'security' );
6522
6523 $post_id = (int)$_POST['viewing_id'];
6524
6525 $status = get_post_meta( $post_id, '_status', TRUE );
6526
6527 if ( $status == 'carried_out' )
6528 {
6529 update_post_meta( $post_id, '_feedback_status', 'interested' );
6530 update_post_meta( $post_id, '_feedback', sanitize_textarea_field( $_POST['feedback'] ) );
6531
6532 // Add note/comment to viewing
6533 $comment = array(
6534 'note_type' => 'action',
6535 'action' => 'viewing_applicant_interested',
6536 );
6537
6538 PH_Comments::insert_note( $post_id, $comment );
6539
6540 wp_send_json_success();
6541 }
6542
6543 wp_send_json_error();
6544 }
6545
6546 public function viewing_not_interested_feedback()
6547 {
6548 check_ajax_referer( 'viewing-actions', 'security' );
6549
6550 $post_id = (int)$_POST['viewing_id'];
6551
6552 $status = get_post_meta( $post_id, '_status', TRUE );
6553
6554 if ( $status == 'carried_out' )
6555 {
6556 update_post_meta( $post_id, '_feedback_status', 'not_interested' );
6557 update_post_meta( $post_id, '_feedback', sanitize_textarea_field( $_POST['feedback'] ) );
6558
6559 // Add note/comment to viewing
6560 $comment = array(
6561 'note_type' => 'action',
6562 'action' => 'viewing_applicant_not_interested',
6563 );
6564
6565 PH_Comments::insert_note( $post_id, $comment );
6566
6567 wp_send_json_success();
6568 }
6569
6570 wp_send_json_error();
6571 }
6572
6573 public function viewing_feedback_not_required()
6574 {
6575 check_ajax_referer( 'viewing-actions', 'security' );
6576
6577 $post_id = (int)$_POST['viewing_id'];
6578
6579 $status = get_post_meta( $post_id, '_status', TRUE );
6580
6581 if ( $status == 'carried_out' )
6582 {
6583 update_post_meta( $post_id, '_feedback_status', 'not_required' );
6584
6585 // Add note/comment to viewing
6586 $comment = array(
6587 'note_type' => 'action',
6588 'action' => 'viewing_feedback_not_required',
6589 );
6590
6591 PH_Comments::insert_note( $post_id, $comment );
6592
6593 wp_send_json_success();
6594 }
6595
6596 wp_send_json_error();
6597 }
6598
6599 public function viewing_revert_feedback_pending()
6600 {
6601 check_ajax_referer( 'viewing-actions', 'security' );
6602
6603 $post_id = (int)$_POST['viewing_id'];
6604
6605 $status = get_post_meta( $post_id, '_status', TRUE );
6606
6607 if ( $status == 'carried_out' )
6608 {
6609 update_post_meta( $post_id, '_feedback_status', '' );
6610 update_post_meta( $post_id, '_feedback_passed_on', '' );
6611 delete_post_meta( $post_id, '_feedback_received_date' );
6612
6613 // Add note/comment to viewing
6614 $comment = array(
6615 'note_type' => 'action',
6616 'action' => 'viewing_revert_feedback_pending',
6617 );
6618
6619 PH_Comments::insert_note( $post_id, $comment );
6620
6621 wp_send_json_success();
6622 }
6623
6624 wp_send_json_error();
6625 }
6626
6627 public function viewing_revert_pending()
6628 {
6629 check_ajax_referer( 'viewing-actions', 'security' );
6630
6631 $post_id = (int)$_POST['viewing_id'];
6632
6633 $status = get_post_meta( $post_id, '_status', TRUE );
6634
6635 if ( in_array( $status, array('carried_out', 'cancelled', 'no_show') ) )
6636 {
6637 update_post_meta( $post_id, '_status', 'pending' );
6638 update_post_meta( $post_id, '_feedback_status', '' );
6639 delete_post_meta( $post_id, '_feedback_received_date' );
6640
6641 // Add note/comment to viewing
6642 $comment = array(
6643 'note_type' => 'action',
6644 'action' => 'viewing_revert_pending',
6645 );
6646
6647 PH_Comments::insert_note( $post_id, $comment );
6648
6649 wp_send_json_success();
6650 }
6651
6652 wp_send_json_error();
6653 }
6654
6655 public function viewing_feedback_passed_on()
6656 {
6657 check_ajax_referer( 'viewing-actions', 'security' );
6658
6659 $post_id = (int)$_POST['viewing_id'];
6660
6661 $status = get_post_meta( $post_id, '_status', TRUE );
6662
6663 if ( $status == 'carried_out' )
6664 {
6665 update_post_meta( $post_id, '_feedback_passed_on', 'yes' );
6666
6667 // Add note/comment to viewing
6668 $comment = array(
6669 'note_type' => 'action',
6670 'action' => 'viewing_feedback_passed_on',
6671 );
6672
6673 PH_Comments::insert_note( $post_id, $comment );
6674
6675 wp_send_json_success();
6676 }
6677
6678 wp_send_json_error();
6679 }
6680
6681 public function get_property_viewings_meta_box()
6682 {
6683 $post_id = $_POST['post_id'];
6684
6685 $selected_status = '';
6686 if ( isset($_POST['selected_status']) )
6687 {
6688 $selected_status = ph_clean($_POST['selected_status']);
6689 }
6690
6691 include( PH()->plugin_path() . '/includes/admin/views/html-property-viewings-meta-box.php' );
6692
6693 do_action('propertyhive_property_viewings_fields');
6694
6695 // Quit out
6696 die();
6697 }
6698
6699 public function get_contact_viewings_meta_box()
6700 {
6701 $post_id = $_POST['post_id'];
6702
6703 $selected_status = '';
6704 if ( isset($_POST['selected_status']) )
6705 {
6706 $selected_status = ph_clean($_POST['selected_status']);
6707 }
6708
6709 include( PH()->plugin_path() . '/includes/admin/views/html-contact-viewings-meta-box.php' );
6710
6711 do_action('propertyhive_contact_viewings_fields');
6712
6713 // Quit out
6714 die();
6715 }
6716
6717 // Offer related functions
6718 public function record_offer_property()
6719 {
6720 check_ajax_referer( 'record-offer', 'security' );
6721
6722 $this->json_headers();
6723
6724 // TO DO: Should do validation on server side also
6725 if (empty($_POST['property_id']))
6726 {
6727 $return = array('error' => 'No property selected');
6728 echo json_encode( $return );
6729 die();
6730 }
6731
6732 $property = new PH_Property((int)$_POST['property_id']);
6733
6734 $applicant_contact_ids = array();
6735
6736 // Create applicant record if required
6737 if (empty($_POST['applicant_ids']) && !empty($_POST['applicant_name']))
6738 {
6739 // Need to create contact/applicant
6740 $contact_post = array(
6741 'post_title' => ph_clean($_POST['applicant_name']),
6742 'post_content' => '',
6743 'post_type' => 'contact',
6744 'post_status' => 'publish',
6745 'comment_status' => 'closed',
6746 'ping_status' => 'closed',
6747 );
6748
6749 // Insert the post into the database
6750 $contact_post_id = wp_insert_post( $contact_post );
6751
6752 if ( is_wp_error($contact_post_id) || $contact_post_id == 0 )
6753 {
6754 $return = array('error' => 'Failed to create contact post. Please try again');
6755 echo json_encode( $return );
6756 die();
6757 }
6758
6759 update_post_meta( $contact_post_id, '_contact_types', array('applicant') );
6760
6761 $email_address = isset($_POST['applicant_email_address']) ? sanitize_email($_POST['applicant_email_address']) : '';
6762 $telephone_number = isset($_POST['applicant_telephone_number']) ? sanitize_text_field($_POST['applicant_telephone_number']) : '';
6763 update_post_meta( $contact_post_id, '_email_address', $email_address );
6764 update_post_meta( $contact_post_id, '_telephone_number', $telephone_number );
6765 update_post_meta( $contact_post_id, '_telephone_number_clean', ph_clean( ph_clean_telephone_number($telephone_number) ) );
6766
6767 if ( isset($_POST['applicant_address']) && !empty(sanitize_textarea_field($_POST['applicant_address'])) )
6768 {
6769 $address = ph_split_address_into_fields( sanitize_textarea_field($_POST['applicant_address']) );
6770
6771 update_post_meta( $contact_post_id, '_address_name_number', $address['address_name_number'] );
6772 update_post_meta( $contact_post_id, '_address_street', $address['address_street'] );
6773 update_post_meta( $contact_post_id, '_address_two', $address['address_two'] );
6774 update_post_meta( $contact_post_id, '_address_three', $address['address_three'] );
6775 update_post_meta( $contact_post_id, '_address_four', $address['address_four'] );
6776 update_post_meta( $contact_post_id, '_address_postcode', $address['address_postcode'] );
6777 update_post_meta( $contact_post_id, '_address_country', get_option( 'propertyhive_default_country', 'GB' ) );
6778 }
6779
6780 update_post_meta( $contact_post_id, '_applicant_profiles', 1 );
6781 update_post_meta( $contact_post_id, '_applicant_profile_0', array( 'department' => $property->department, 'send_matching_properties' => '' ) );
6782
6783 $applicant_contact_ids[] = $contact_post_id;
6784 }
6785
6786 if (!empty($_POST['applicant_ids']) && empty($_POST['applicant_name']))
6787 {
6788 // This is an existing contact
6789 if ( !is_array($_POST['applicant_ids']) )
6790 {
6791 $_POST['applicant_ids'] = array($_POST['applicant_ids']);
6792 }
6793
6794 foreach ( $_POST['applicant_ids'] as $applicant_id )
6795 {
6796 $applicant_contact_ids[] = (int)$applicant_id;
6797 }
6798 }
6799
6800 $applicant_contact_ids = array_unique($applicant_contact_ids);
6801
6802 if ( empty($applicant_contact_ids) )
6803 {
6804 $return = array('error' => 'No applicant selected, or unable to create applicant record');
6805 echo json_encode( $return );
6806 die();
6807 }
6808
6809 // Loop through contacts and create one offer each
6810 // At the moment it's a 1-to-1 relationship, but might support multiple in the future
6811 foreach ( $applicant_contact_ids as $applicant_contact_id )
6812 {
6813 // Insert offer record
6814 $offer_post = array(
6815 'post_title' => '',
6816 'post_content' => '',
6817 'post_type' => 'offer',
6818 'post_status' => 'publish',
6819 'comment_status' => 'closed',
6820 'ping_status' => 'closed',
6821 );
6822
6823 // Insert the post into the database
6824 $offer_post_id = wp_insert_post( $offer_post );
6825
6826 if ( is_wp_error($offer_post_id) || $offer_post_id == 0 )
6827 {
6828 $return = array('error' => 'Failed to create offer post. Please try again');
6829 echo json_encode( $return );
6830 die();
6831 }
6832
6833 $amount = preg_replace("/[^0-9.]/", '', $_POST['amount']);
6834
6835 add_post_meta( $offer_post_id, '_offer_date_time', ph_clean($_POST['offer_date']) . ' ' . ph_clean($_POST['offer_time']) );
6836 add_post_meta( $offer_post_id, '_property_id', (int)$_POST['property_id'] );
6837 add_post_meta( $offer_post_id, '_applicant_contact_id', $applicant_contact_id );
6838 add_post_meta( $offer_post_id, '_amount', $amount );
6839 add_post_meta( $offer_post_id, '_status', 'pending' );
6840
6841 $applicant_solicitor_contact_id = get_post_meta( $applicant_contact_id, '_contact_solicitor_contact_id', TRUE );
6842 if ( !empty($applicant_solicitor_contact_id) )
6843 {
6844 add_post_meta( $offer_post_id, '_applicant_solicitor_contact_id', (int)$applicant_solicitor_contact_id );
6845 }
6846
6847 $owner_contact_ids = get_post_meta((int)$_POST['property_id'], '_owner_contact_id', TRUE);
6848 if ( !empty($owner_contact_ids) )
6849 {
6850 foreach ( $owner_contact_ids as $owner_contact_id )
6851 {
6852 $property_owner_solicitor_contact_id = get_post_meta( (int)$owner_contact_id, '_contact_solicitor_contact_id', TRUE );
6853 if ( !empty($property_owner_solicitor_contact_id) )
6854 {
6855 add_post_meta( $offer_post_id, '_property_owner_solicitor_contact_id', (int)$property_owner_solicitor_contact_id );
6856 }
6857 }
6858 }
6859 }
6860
6861 $applicant_contacts = array();
6862 foreach ( $applicant_contact_ids as $applicant_contact_id )
6863 {
6864 $applicant_contacts[] = array(
6865 'ID' => $applicant_contact_id,
6866 'post_title' => get_the_title($applicant_contact_id),
6867 'edit_link' => get_edit_post_link( $applicant_contact_id, '' ),
6868 );
6869 }
6870
6871 $return = array('success' => array(
6872 'offer' => array(
6873 'ID' => $offer_post_id,
6874 'edit_link' => get_edit_post_link( $offer_post_id, '' ),
6875 ),
6876 'applicant_contacts' => $applicant_contacts,
6877 ));
6878
6879 echo json_encode( $return );
6880
6881 die();
6882 }
6883
6884 public function record_offer_contact()
6885 {
6886 check_ajax_referer( 'record-offer', 'security' );
6887
6888 $this->json_headers();
6889
6890 // TO DO: Should do validation on server side also
6891 if (empty($_POST['contact_id']))
6892 {
6893 $return = array('error' => 'No contact selected');
6894 echo json_encode( $return );
6895 die();
6896 }
6897
6898 if (empty($_POST['property_ids']))
6899 {
6900 $return = array('error' => 'No property selected');
6901 echo json_encode( $return );
6902 die();
6903 }
6904
6905 // Loop through contacts and create one offer each
6906 // At the moment it's a 1-to-1 relationship, but might support multiple in the future
6907 foreach ( $_POST['property_ids'] as $property_id )
6908 {
6909 // Insert offer record
6910 $offer_post = array(
6911 'post_title' => '',
6912 'post_content' => '',
6913 'post_type' => 'offer',
6914 'post_status' => 'publish',
6915 'comment_status' => 'closed',
6916 'ping_status' => 'closed',
6917 );
6918
6919 // Insert the post into the database
6920 $offer_post_id = wp_insert_post( $offer_post );
6921
6922 if ( is_wp_error($offer_post_id) || $offer_post_id == 0 )
6923 {
6924 $return = array('error' => 'Failed to create offer post. Please try again');
6925 echo json_encode( $return );
6926 die();
6927 }
6928
6929 $amount = preg_replace("/[^0-9.]/", '', ph_clean($_POST['amount']));
6930
6931 add_post_meta( $offer_post_id, '_offer_date_time', ph_clean($_POST['offer_date']) . ' ' . ph_clean($_POST['offer_time']) );
6932 add_post_meta( $offer_post_id, '_property_id', (int)$property_id );
6933 add_post_meta( $offer_post_id, '_applicant_contact_id', (int)$_POST['contact_id'] );
6934 add_post_meta( $offer_post_id, '_amount', $amount );
6935 add_post_meta( $offer_post_id, '_status', 'pending' );
6936
6937 $applicant_solicitor_contact_id = get_post_meta( (int)$_POST['contact_id'], '_contact_solicitor_contact_id', TRUE );
6938 if ( !empty($applicant_solicitor_contact_id) )
6939 {
6940 add_post_meta( $offer_post_id, '_applicant_solicitor_contact_id', (int)$applicant_solicitor_contact_id );
6941 }
6942
6943 $owner_contact_ids = get_post_meta($property_id, '_owner_contact_id', TRUE);
6944 if ( !empty($owner_contact_ids) )
6945 {
6946 foreach ( $owner_contact_ids as $owner_contact_id )
6947 {
6948 $property_owner_solicitor_contact_id = get_post_meta( (int)$owner_contact_id, '_contact_solicitor_contact_id', TRUE );
6949 if ( !empty($property_owner_solicitor_contact_id) )
6950 {
6951 add_post_meta( $offer_post_id, '_property_owner_solicitor_contact_id', (int)$property_owner_solicitor_contact_id );
6952 }
6953 }
6954 }
6955 }
6956
6957 $properties = array();
6958 foreach ( $_POST['property_ids'] as $property_id )
6959 {
6960 $properties[] = array(
6961 'ID' => (int)$property_id,
6962 'post_title' => get_the_title((int)$property_id),
6963 'edit_link' => get_edit_post_link( (int)$property_id, '' ),
6964 );
6965 }
6966
6967 $return = array('success' => array(
6968 'offer' => array(
6969 'ID' => $offer_post_id,
6970 'edit_link' => get_edit_post_link( $offer_post_id, '' ),
6971 ),
6972 'properties' => $properties,
6973 ));
6974
6975 echo json_encode( $return );
6976
6977 die();
6978 }
6979
6980 public function get_offer_details_meta_box()
6981 {
6982 global $post;
6983
6984 check_ajax_referer( 'offer-details-meta-box', 'security' );
6985
6986 $post = get_post((int)$_POST['offer_id']);
6987
6988 $offer = new PH_Offer((int)$_POST['offer_id']);
6989
6990 echo '<div class="propertyhive_meta_box">';
6991
6992 echo '<div class="options_group">';
6993
6994 if ( $offer->status != '' )
6995 {
6996 echo '<p class="form-field">
6997
6998 <label for="">' . esc_html(__('Status', 'propertyhive')) . '</label>
6999
7000 ' . esc_html(__( ucwords(str_replace("_", " ", $offer->status)), 'propertyhive' )) . '
7001
7002 </p>';
7003 }
7004
7005 $offer_date_time = $offer->offer_date_time;
7006 if ( empty($offer_date_time) )
7007 {
7008 $offer_date_time = date("Y-m-d H:i:s");
7009 }
7010
7011 echo '<p class="form-field offer_date_time_field">
7012
7013 <label for="_offer_date">' . esc_html(__('Offer Date / Time', 'propertyhive')) . '</label>
7014
7015 <input type="date" class="small" name="_offer_date" id="_offer_date" value="' . esc_attr(date("Y-m-d", strtotime($offer_date_time))) . '" placeholder="">
7016 <select id="_offer_time_hours" name="_offer_time_hours" class="select short" style="width:55px">';
7017
7018 if ( empty($offer_date_time) )
7019 {
7020 $value = date("H");
7021 }
7022 else
7023 {
7024 $value = date( "H", strtotime( $offer_date_time ) );
7025 }
7026 for ( $i = 0; $i < 23; ++$i )
7027 {
7028 $j = str_pad($i, 2, '0', STR_PAD_LEFT);
7029 echo '<option value="' . esc_attr($j) . '"';
7030 if ($i == $value) { echo ' selected'; }
7031 echo '>' . esc_html($j) . '</option>';
7032 }
7033
7034 echo '</select>
7035 :
7036 <select id="_offer_time_minutes" name="_offer_time_minutes" class="select short" style="width:55px">';
7037
7038 if ( empty($offer_date_time) )
7039 {
7040 $value = '';
7041 }
7042 else
7043 {
7044 $value = date( "i", strtotime( $offer_date_time ) );
7045 }
7046 for ( $i = 0; $i < 60; $i+=5 )
7047 {
7048 $j = str_pad($i, 2, '0', STR_PAD_LEFT);
7049 echo '<option value="' . esc_attr($j) . '"';
7050 if ($i == $value) { echo ' selected'; }
7051 echo '>' . esc_html($j) . '</option>';
7052 }
7053
7054 echo '</select>
7055
7056 </p>';
7057
7058 $args = array(
7059 'id' => '_amount',
7060 'label' => __( 'Offer Amount', 'propertyhive' ) . ' (&pound;)',
7061 'desc_tip' => false,
7062 'class' => 'short',
7063 'value' => ( is_numeric($offer->amount) ? ph_display_price_field( $offer->amount ) : '' ),
7064 'custom_attributes' => array(
7065 //'style' => 'width:95%; max-width:500px;'
7066 )
7067 );
7068 propertyhive_wp_text_input( $args );
7069
7070 do_action('propertyhive_offer_details_fields');
7071
7072 echo '</div>';
7073
7074 echo '</div>';
7075
7076 die();
7077 }
7078
7079 public function get_offer_actions()
7080 {
7081 check_ajax_referer( 'offer-actions', 'security' );
7082
7083 $post_id = (int)$_POST['offer_id'];
7084
7085 $status = get_post_meta( $post_id, '_status', TRUE );
7086
7087 // Success action panel
7088 echo '<div id="action_panel_success" class="propertyhive_meta_box propertyhive_meta_box_actions" style="display:none;">
7089
7090 <div class="options_group" style="padding-top:8px;">
7091
7092 <div id="success_actions"></div>
7093
7094 <a class="button action-cancel" style="width:100%;" href="#">' . esc_html(__( 'Back To Actions', 'propertyhive' )) . '</a>
7095
7096 </div>
7097
7098 </div>';
7099
7100 do_action( 'propertyhive_admin_offer_action_options', $post_id );
7101 do_action( 'propertyhive_admin_post_action_options', $post_id );
7102
7103 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="propertyhive_offer_actions_meta_box">
7104
7105 <div class="options_group" style="padding-top:8px;">';
7106
7107 $actions = array();
7108
7109 if ( $status == 'pending' )
7110 {
7111 $actions[] = '<a
7112 href="#action_panel_offer_accepted"
7113 class="button button-success offer-action"
7114 style="width:100%; margin-bottom:7px; text-align:center"
7115 >' . wp_kses_post( __('Accept Offer', 'propertyhive') ) . '</a>';
7116 $actions[] = '<a
7117 href="#action_panel_offer_declined"
7118 class="button button-danger offer-action"
7119 style="width:100%; margin-bottom:7px; text-align:center"
7120 >' . wp_kses_post( __('Decline Offer', 'propertyhive') ) . '</a>';
7121 $actions[] = '<a
7122 href="#action_panel_offer_withdrawn"
7123 class="button offer-action"
7124 style="width:100%; margin-bottom:7px; text-align:center"
7125 >' . wp_kses_post( __('Withdraw Offer', 'propertyhive') ) . '</a>';
7126 }
7127
7128 if ( $status == 'accepted' )
7129 {
7130 // See if a sale has this offer id associated with it
7131 $sale_id = get_post_meta( $post_id, '_sale_id', TRUE );
7132 if ( $sale_id != '' && get_post_status($sale_id) != 'publish' )
7133 {
7134 $sale_id = '';
7135 }
7136
7137 if ( $sale_id != '' )
7138 {
7139 $actions[] = '<a
7140 href="' . esc_url(get_edit_post_link( $sale_id, '' )) . '"
7141 class="button"
7142 style="width:100%; margin-bottom:7px; text-align:center"
7143 >' . wp_kses_post( __('View Sale', 'propertyhive') ) . '</a>';
7144 }
7145 else
7146 {
7147 $actions[] = '<a
7148 href="' . esc_url(wp_nonce_url( admin_url( 'post.php?post=' . $post_id . '&action=edit' ), '1', 'create_sale' )) . '"
7149 class="button button-success button-create-sale"
7150 style="width:100%; margin-bottom:7px; text-align:center"
7151 onclick="setTimeout(function() { jQuery(\'.button-create-sale\').attr(\'href\', \'#\'); jQuery(\'.button-create-sale\').attr(\'disabled\', \'disabled\'); jQuery(\'.button-create-sale\').html(\'Creating...\'); }, 50);"
7152 >' . wp_kses_post( __('Create Sale', 'propertyhive') ) . '</a>';
7153 $actions[] = '<a
7154 href="#action_panel_offer_withdrawn"
7155 class="button offer-action"
7156 style="width:100%; margin-bottom:7px; text-align:center"
7157 >' . wp_kses_post( __('Withdraw Offer', 'propertyhive') ) . '</a>';
7158 }
7159 }
7160
7161 if ( $status == 'accepted' || $status == 'declined' || $status == 'withdrawn' )
7162 {
7163 $actions[] = '<a
7164 href="#action_panel_offer_revert_pending"
7165 class="button offer-action"
7166 style="width:100%; margin-bottom:7px; text-align:center"
7167 >' . wp_kses_post( __('Revert To Pending', 'propertyhive') ) . '</a>';
7168 }
7169
7170 $actions = apply_filters( 'propertyhive_admin_offer_actions', $actions, $post_id );
7171 $actions = apply_filters( 'propertyhive_admin_post_actions', $actions, $post_id );
7172
7173 if ( !empty($actions) )
7174 {
7175 echo implode("", $actions);
7176 }
7177 else
7178 {
7179 echo '<div style="text-align:center">' . esc_html(__( 'No actions to display', 'propertyhive' )) . '</div>';
7180 }
7181
7182 echo '</div>
7183
7184 </div>';
7185
7186 die();
7187 }
7188
7189 public function offer_accepted()
7190 {
7191 check_ajax_referer( 'offer-actions', 'security' );
7192
7193 $post_id = (int)$_POST['offer_id'];
7194
7195 $status = get_post_meta( $post_id, '_status', TRUE );
7196
7197 if ( $status == 'pending' )
7198 {
7199 update_post_meta( $post_id, '_status', 'accepted' );
7200
7201 // Add note/comment to offer
7202 $comment = array(
7203 'note_type' => 'action',
7204 'action' => 'offer_accepted',
7205 );
7206
7207 PH_Comments::insert_note( $post_id, $comment );
7208
7209 wp_send_json_success();
7210 }
7211
7212 wp_send_json_error();
7213 }
7214
7215 public function offer_declined()
7216 {
7217 check_ajax_referer( 'offer-actions', 'security' );
7218
7219 $post_id = (int)$_POST['offer_id'];
7220
7221 $status = get_post_meta( $post_id, '_status', TRUE );
7222
7223 if ( $status == 'pending' )
7224 {
7225 update_post_meta( $post_id, '_status', 'declined' );
7226
7227 // Add note/comment to offer
7228 $comment = array(
7229 'note_type' => 'action',
7230 'action' => 'offer_declined',
7231 );
7232
7233 PH_Comments::insert_note( $post_id, $comment );
7234
7235 wp_send_json_success();
7236 }
7237
7238 wp_send_json_error();
7239 }
7240
7241 public function offer_withdrawn()
7242 {
7243 check_ajax_referer( 'offer-actions', 'security' );
7244
7245 $post_id = (int)$_POST['offer_id'];
7246
7247 $status = get_post_meta( $post_id, '_status', TRUE );
7248
7249 if ( $status == 'pending' || $status == 'accepted' )
7250 {
7251 update_post_meta( $post_id, '_status', 'withdrawn' );
7252
7253 // Add note/comment to offer
7254 $comment = array(
7255 'note_type' => 'action',
7256 'action' => 'offer_withdrawn',
7257 );
7258
7259 PH_Comments::insert_note( $post_id, $comment );
7260
7261 wp_send_json_success();
7262 }
7263
7264 wp_send_json_error();
7265 }
7266
7267 public function offer_revert_pending()
7268 {
7269 check_ajax_referer( 'offer-actions', 'security' );
7270
7271 $post_id = (int)$_POST['offer_id'];
7272
7273 $status = get_post_meta( $post_id, '_status', TRUE );
7274
7275 if ( $status == 'accepted' || $status == 'declined' || $status == 'withdrawn' )
7276 {
7277 update_post_meta( $post_id, '_status', 'pending' );
7278
7279 // Add note/comment to offer
7280 $comment = array(
7281 'note_type' => 'action',
7282 'action' => 'offer_revert_pending',
7283 );
7284
7285 PH_Comments::insert_note( $post_id, $comment );
7286
7287 wp_send_json_success();
7288 }
7289
7290 wp_send_json_error();
7291 }
7292
7293 public function get_property_offers_meta_box()
7294 {
7295 $post_id = $_POST['post_id'];
7296
7297 $selected_status = '';
7298 if ( isset($_POST['selected_status']) )
7299 {
7300 $selected_status = ph_clean($_POST['selected_status']);
7301 }
7302
7303 include( PH()->plugin_path() . '/includes/admin/views/html-property-offers-meta-box.php' );
7304
7305 do_action('propertyhive_property_offers_fields');
7306
7307 // Quit out
7308 die();
7309 }
7310
7311 public function get_contact_offers_meta_box()
7312 {
7313 $post_id = $_POST['post_id'];
7314
7315 $selected_status = '';
7316 if ( isset($_POST['selected_status']) )
7317 {
7318 $selected_status = ph_clean($_POST['selected_status']);
7319 }
7320
7321 include( PH()->plugin_path() . '/includes/admin/views/html-contact-offers-meta-box.php' );
7322
7323 do_action('propertyhive_contact_offers_fields');
7324
7325 // Quit out
7326 die();
7327 }
7328
7329 // Sale related functions
7330 public function get_sale_details_meta_box()
7331 {
7332 global $post;
7333
7334 check_ajax_referer( 'sale-details-meta-box', 'security' );
7335
7336 $post = get_post((int)$_POST['sale_id']);
7337
7338 $sale = new PH_Offer((int)$_POST['sale_id']);
7339
7340 echo '<div class="propertyhive_meta_box">';
7341
7342 echo '<div class="options_group">';
7343
7344 if ( $sale->status != '' )
7345 {
7346 echo '<p class="form-field">
7347
7348 <label for="">' . esc_html(__('Status', 'propertyhive')) . '</label>
7349
7350 ' . esc_html(__( ucwords(str_replace("_", " ", $sale->status)), 'propertyhive' )) . '
7351
7352 </p>';
7353 }
7354
7355 $sale_date_time = $sale->sale_date_time;
7356 if ( empty($sale_date_time) )
7357 {
7358 $sale_date_time = date("Y-m-d H:i:s");
7359 }
7360
7361 echo '<p class="form-field sale_date_field">
7362
7363 <label for="_sale_date">' . esc_html(__('Sale Date', 'propertyhive')) . '</label>
7364
7365 <input type="date" class="small" name="_sale_date" id="_sale_date" value="' . esc_attr(date("Y-m-d", strtotime($sale_date_time))) . '" placeholder="">
7366
7367 </p>';
7368
7369 $args = array(
7370 'id' => '_amount',
7371 'label' => __( 'Sale Amount', 'propertyhive' ) . ' (&pound;)',
7372 'desc_tip' => false,
7373 'class' => 'short',
7374 'value' => ( is_numeric($sale->amount) ? ph_display_price_field( $sale->amount ) : '' ),
7375 'custom_attributes' => array(
7376 //'style' => 'width:95%; max-width:500px;'
7377 )
7378 );
7379 propertyhive_wp_text_input( $args );
7380
7381 do_action('propertyhive_sale_details_fields');
7382
7383 echo '</div>';
7384
7385 echo '</div>';
7386
7387 die();
7388 }
7389
7390 public function get_sale_actions()
7391 {
7392 check_ajax_referer( 'sale-actions', 'security' );
7393
7394 $post_id = (int)$_POST['sale_id'];
7395
7396 $status = get_post_meta( $post_id, '_status', TRUE );
7397
7398 // Success action panel
7399 echo '<div id="action_panel_success" class="propertyhive_meta_box propertyhive_meta_box_actions" style="display:none;">
7400
7401 <div class="options_group" style="padding-top:8px;">
7402
7403 <div id="success_actions"></div>
7404
7405 <a class="button action-cancel" style="width:100%;" href="#">' . __( 'Back To Actions', 'propertyhive' ) . '</a>
7406
7407 </div>
7408
7409 </div>';
7410
7411 do_action( 'propertyhive_admin_sale_action_options', $post_id );
7412 do_action( 'propertyhive_admin_post_action_options', $post_id );
7413
7414 echo '<div class="propertyhive_meta_box propertyhive_meta_box_actions" id="propertyhive_sale_actions_meta_box">
7415
7416 <div class="options_group" style="padding-top:8px;">';
7417
7418 $actions = array();
7419
7420 if ( $status == 'current' )
7421 {
7422 $actions[] = '<a
7423 href="#action_panel_sale_exchanged"
7424 class="button button-success sale-action"
7425 style="width:100%; margin-bottom:7px; text-align:center"
7426 >' . esc_html(__('Sale Exchanged', 'propertyhive')) . '</a>';
7427
7428 }
7429
7430 if ( $status == 'exchanged' )
7431 {
7432 $actions[] = '<a
7433 href="#action_panel_sale_completed"
7434 class="button button-success sale-action"
7435 style="width:100%; margin-bottom:7px; text-align:center"
7436 >' . esc_html(__('Sale Completed', 'propertyhive')) . '</a>';
7437 }
7438
7439 if ( $status == 'completed' )
7440 {
7441
7442 }
7443
7444 if ( $status == 'current' || $status == 'exchanged' )
7445 {
7446 $actions[] = '<a
7447 href="#action_panel_sale_fallen_through"
7448 class="button sale-action"
7449 style="width:100%; margin-bottom:7px; text-align:center"
7450 >' . esc_html(__('Sale Fallen Through', 'propertyhive')) . '</a>';
7451 }
7452
7453 $actions = apply_filters( 'propertyhive_admin_sale_actions', $actions, $post_id );
7454 $actions = apply_filters( 'propertyhive_admin_post_actions', $actions, $post_id );
7455
7456 if ( !empty($actions) )
7457 {
7458 echo implode("", $actions);
7459 }
7460 else
7461 {
7462 echo '<div style="text-align:center">' . esc_html(__( 'No actions to display', 'propertyhive' )) . '</div>';
7463 }
7464
7465 echo '</div>
7466
7467 </div>';
7468
7469 die();
7470 }
7471
7472 public function sale_exchanged()
7473 {
7474 check_ajax_referer( 'sale-actions', 'security' );
7475
7476 $post_id = (int)$_POST['sale_id'];
7477
7478 $status = get_post_meta( $post_id, '_status', TRUE );
7479
7480 if ( $status == 'current' )
7481 {
7482 update_post_meta( $post_id, '_status', 'exchanged' );
7483
7484 // Add note/comment to sale
7485 $comment = array(
7486 'note_type' => 'action',
7487 'action' => 'sale_exchanged',
7488 );
7489
7490 PH_Comments::insert_note( $post_id, $comment );
7491
7492 wp_send_json_success();
7493 }
7494
7495 wp_send_json_error();
7496 }
7497
7498 public function sale_completed()
7499 {
7500 check_ajax_referer( 'sale-actions', 'security' );
7501
7502 $post_id = (int)$_POST['sale_id'];
7503
7504 $status = get_post_meta( $post_id, '_status', TRUE );
7505
7506 if ( $status == 'exchanged' )
7507 {
7508 update_post_meta( $post_id, '_status', 'completed' );
7509
7510 // Add note/comment to sale
7511 $comment = array(
7512 'note_type' => 'action',
7513 'action' => 'sale_completed',
7514 );
7515
7516 PH_Comments::insert_note( $post_id, $comment );
7517
7518 wp_send_json_success();
7519 }
7520
7521 wp_send_json_error();
7522 }
7523
7524 public function sale_fallen_through()
7525 {
7526 check_ajax_referer( 'sale-actions', 'security' );
7527
7528 $post_id = (int)$_POST['sale_id'];
7529
7530 $status = get_post_meta( $post_id, '_status', TRUE );
7531
7532 if ( $status == 'current' || $status == 'exchanged' )
7533 {
7534 update_post_meta( $post_id, '_status', 'fallen_through' );
7535
7536 // Add note/comment to sale
7537 $comment = array(
7538 'note_type' => 'action',
7539 'action' => 'sale_fallen_through',
7540 );
7541
7542 PH_Comments::insert_note( $post_id, $comment );
7543
7544 wp_send_json_success();
7545 }
7546
7547 wp_send_json_error();
7548 }
7549
7550 public function get_property_sales_meta_box()
7551 {
7552 $post_id = (int)$_POST['post_id'];
7553
7554 $selected_status = '';
7555 if ( isset($_POST['selected_status']) )
7556 {
7557 $selected_status = ph_clean($_POST['selected_status']);
7558 }
7559
7560 include( PH()->plugin_path() . '/includes/admin/views/html-property-sales-meta-box.php' );
7561
7562 do_action('propertyhive_property_sales_fields');
7563
7564 // Quit out
7565 die();
7566 }
7567
7568 public function get_contact_sales_meta_box()
7569 {
7570 $post_id = (int)$_POST['post_id'];
7571
7572 $selected_status = '';
7573 if ( isset($_POST['selected_status']) )
7574 {
7575 $selected_status = ph_clean($_POST['selected_status']);
7576 }
7577
7578 include( PH()->plugin_path() . '/includes/admin/views/html-contact-sales-meta-box.php' );
7579
7580 do_action('propertyhive_contact_sales_fields');
7581
7582 // Quit out
7583 die();
7584 }
7585
7586 public function get_property_enquiries_meta_box()
7587 {
7588 $post_id = (int)$_POST['post_id'];
7589
7590 $selected_status = '';
7591 if ( isset($_POST['selected_status']) )
7592 {
7593 $selected_status = ph_clean($_POST['selected_status']);
7594 }
7595
7596 include( PH()->plugin_path() . '/includes/admin/views/html-property-enquiries-meta-box.php' );
7597
7598 do_action('propertyhive_property_enquiries_fields');
7599
7600 // Quit out
7601 die();
7602 }
7603
7604 public function get_contact_enquiries_meta_box()
7605 {
7606 $post_id = (int)$_POST['post_id'];
7607
7608 $selected_status = '';
7609 if ( isset($_POST['selected_status']) )
7610 {
7611 $selected_status = ph_clean($_POST['selected_status']);
7612 }
7613
7614 include( PH()->plugin_path() . '/includes/admin/views/html-contact-enquiries-meta-box.php' );
7615
7616 do_action('propertyhive_contact_enquiries_fields');
7617
7618 // Quit out
7619 die();
7620 }
7621
7622 /**
7623 * Add new management key date via ajax
7624 */
7625 public function add_key_date()
7626 {
7627 $parent_post_id = (int)$_POST['post_id'];
7628
7629 if ( $parent_post_id > 0 ) {
7630 $date_description = wp_kses_post( trim( stripslashes( $_POST['key_date_description'] ) ) );
7631 $date_type_id = ph_clean( stripslashes( $_POST['key_date_type'] ) );
7632 $date_due = ph_clean($_POST['key_date_due']) . ' ' . ph_clean($_POST['key_date_hours']) . ':' . ph_clean($_POST['key_date_minutes']);
7633 $date_notes = sanitize_textarea_field($_POST['key_date_notes']);
7634
7635 $parent_post_type = get_post_type( $parent_post_id );
7636
7637 // Insert key date record
7638 $key_date_post = array(
7639 'post_title' => $date_description,
7640 'post_content' => '',
7641 'post_type' => 'key_date',
7642 'post_status' => 'publish',
7643 'comment_status' => 'closed',
7644 'ping_status' => 'closed',
7645 );
7646
7647 // Insert the post into the database
7648 $key_date_post_id = wp_insert_post( $key_date_post );
7649
7650 if ( is_wp_error($key_date_post_id) || $key_date_post_id == 0 )
7651 {
7652 $return = array('error' => 'Failed to create key date post. Please try again');
7653 echo json_encode( $return );
7654 die();
7655 }
7656
7657 add_post_meta( $key_date_post_id, '_date_due', $date_due );
7658 add_post_meta( $key_date_post_id, '_key_date_status', 'pending' );
7659 add_post_meta( $key_date_post_id, '_key_date_type_id', $date_type_id );
7660 add_post_meta( $key_date_post_id, '_key_date_notes', $date_notes );
7661
7662 switch ( $parent_post_type )
7663 {
7664 case 'property' :
7665 {
7666 add_post_meta( $key_date_post_id, '_property_id', $parent_post_id );
7667 break;
7668 }
7669 case 'tenancy' :
7670 {
7671 add_post_meta( $key_date_post_id, '_tenancy_id', $parent_post_id );
7672
7673 $parent_property_id = get_post_meta( $parent_post_id, '_property_id', true );
7674 add_post_meta( $key_date_post_id, '_property_id', $parent_property_id );
7675 break;
7676 }
7677 }
7678 }
7679 die();
7680 }
7681
7682 public function get_management_dates_grid()
7683 {
7684 $post_id = (int)$_POST['post_id'];
7685
7686 if ( isset($_POST['selected_type_id']) )
7687 {
7688 $selected_type_id = (int)$_POST['selected_type_id'];
7689 }
7690
7691 if ( isset($_POST['selected_status']) )
7692 {
7693 $selected_status = ph_clean($_POST['selected_status']);
7694 }
7695
7696 include( PH()->plugin_path() . '/includes/admin/views/html-management-dates-meta-box.php' );
7697
7698 // Quit out
7699 die();
7700 }
7701
7702 public function get_key_dates_quick_edit_row()
7703 {
7704 $post_id = (int)$_POST['post_id'];
7705
7706 include( PH()->plugin_path() . '/includes/admin/views/html-key-dates-quick-edit.php' );
7707
7708 // Quit out
7709 die();
7710 }
7711
7712 public function check_key_date_recurrence()
7713 {
7714 $post_id = (int)$_POST['post_id'];
7715
7716 $next_key_date = '';
7717
7718 $key_date = new PH_Key_Date(get_post($post_id));
7719 $key_date_due = $key_date->date_due();
7720
7721 $key_date_type = $key_date->key_date_type_id();
7722
7723 $recurrence_rules = get_option( 'propertyhive_key_date_type', array() );
7724 $recurrence_rules = is_array( $recurrence_rules ) ? $recurrence_rules : array();
7725
7726 if ( isset($recurrence_rules[$key_date_type]) && isset( $recurrence_rules[$key_date_type]['recurrence_rule'] ) )
7727 {
7728 foreach ( explode(';', $recurrence_rules[$key_date_type]['recurrence_rule']) as $key_value_pair )
7729 {
7730 list($key, $value) = explode('=', $key_value_pair);
7731 $recurrence[strtolower($key)] = $value;
7732 }
7733
7734 if ( isset($recurrence['freq']) && $recurrence['freq'] != 'ONCE' )
7735 {
7736 $interval = isset($recurrence['interval']) ? $recurrence['interval'] : '1';
7737 switch( $recurrence['freq'] )
7738 {
7739 case 'DAILY':
7740 $frequency = 'day';
7741 break;
7742 case 'WEEKLY':
7743 $frequency = 'week';
7744 break;
7745 case 'MONTHLY':
7746 $frequency = 'month';
7747 break;
7748 case 'YEARLY':
7749 $frequency = 'year';
7750 break;
7751 }
7752
7753 if ( isset($frequency) )
7754 {
7755 $next_key_date = date_add($key_date_due, date_interval_create_from_date_string($interval . ' ' . $frequency));
7756 $next_key_date = date_format($next_key_date, 'Y-m-d');
7757 }
7758 }
7759 }
7760
7761 echo esc_html($next_key_date);
7762
7763 // Quit out
7764 die();
7765 }
7766
7767 public function save_key_date()
7768 {
7769 check_ajax_referer( 'save-key-date', 'security' );
7770
7771 $this->json_headers();
7772
7773 if ( ! current_user_can( 'manage_propertyhive' ) )
7774 wp_send_json_error( __( 'You do not have permission to manage key dates', 'propertyhive' ), 403 );
7775
7776 $key_date_post_id = (int)$_POST['post_id'];
7777
7778 $args = array(
7779 'ID' => $key_date_post_id,
7780 'post_title' => ph_clean($_POST['description']),
7781 );
7782 wp_update_post( $args );
7783
7784 update_post_meta( $key_date_post_id, '_date_due', ph_clean($_POST['due_date_time']) );
7785 update_post_meta( $key_date_post_id, '_key_date_status', ph_clean($_POST['status']) );
7786 update_post_meta( $key_date_post_id, '_key_date_type_id', (int)$_POST['type'] );
7787 update_post_meta( $key_date_post_id, '_key_date_notes', sanitize_textarea_field($_POST['notes'] ));
7788
7789 if ( isset($_POST['next_key_date']) )
7790 {
7791 // Insert next key date record
7792 $next_key_date_post = array(
7793 'post_title' => ph_clean($_POST['description']),
7794 'post_content' => '',
7795 'post_type' => 'key_date',
7796 'post_status' => 'publish',
7797 'comment_status' => 'closed',
7798 'ping_status' => 'closed',
7799 );
7800
7801 // Insert the post into the database
7802 $next_key_date_post_id = wp_insert_post( $next_key_date_post );
7803
7804 if ( is_wp_error($next_key_date_post_id) || $next_key_date_post_id == 0 )
7805 {
7806 $return = array('error' => 'Failed to create next key date post. Please try again');
7807 echo json_encode( $return );
7808 die();
7809 }
7810
7811 add_post_meta( $next_key_date_post_id, '_date_due', ph_clean($_POST['next_key_date']) );
7812 add_post_meta( $next_key_date_post_id, '_key_date_status', 'pending' );
7813 add_post_meta( $next_key_date_post_id, '_key_date_type_id', (int)$_POST['type'] );
7814
7815 if ( metadata_exists('post', $key_date_post_id, '_property_id') ) {
7816 add_post_meta( $next_key_date_post_id, '_property_id', get_post_meta($key_date_post_id, '_property_id', true) );
7817 }
7818
7819 if ( metadata_exists('post', $key_date_post_id, '_tenancy_id') ) {
7820 add_post_meta( $next_key_date_post_id, '_tenancy_id', get_post_meta($key_date_post_id, '_tenancy_id', true) );
7821 }
7822 }
7823
7824 die();
7825 }
7826
7827 public function delete_key_date()
7828 {
7829 check_ajax_referer( 'delete-key-date', 'security' );
7830
7831 $this->json_headers();
7832
7833 if ( ! current_user_can( 'manage_propertyhive' ) )
7834 wp_send_json_error( __( 'You do not have permission to manage key dates', 'propertyhive' ), 403 );
7835
7836 $date_post_id = (int)$_POST['date_post_id'];
7837
7838 wp_delete_post($date_post_id, TRUE);
7839
7840 $return = array('success' => true);
7841 echo json_encode( $return );
7842
7843 die();
7844 }
7845
7846 public function get_property_tenancies_grid()
7847 {
7848 $post_id = (int)$_POST['post_id'];
7849
7850 if ( isset($_POST['selected_status']) )
7851 {
7852 $selected_status = ph_clean($_POST['selected_status']);
7853 }
7854
7855 include( PH()->plugin_path() . '/includes/admin/views/html-property-tenancies-meta-box.php' );
7856
7857 // Quit out
7858 die();
7859 }
7860
7861 public function get_contact_tenancies_grid()
7862 {
7863 $post_id = (int)$_POST['post_id'];
7864
7865 if ( isset($_POST['selected_status']) )
7866 {
7867 $selected_status = ph_clean($_POST['selected_status']);
7868 }
7869
7870 include( PH()->plugin_path() . '/includes/admin/views/html-contact-tenancies-meta-box.php' );
7871
7872 // Quit out
7873 die();
7874 }
7875
7876 public function get_contact_solicitor()
7877 {
7878 switch( get_post_type((int)$_POST['post_id']) )
7879 {
7880 case 'contact':
7881 {
7882 $contact_post_ids = array( (int)$_POST['post_id'] );
7883 break;
7884 }
7885 case 'property':
7886 {
7887 $owner_contact_ids = get_post_meta((int)$_POST['post_id'], '_owner_contact_id', TRUE);
7888 if ( !empty( $owner_contact_ids ) )
7889 {
7890 if ( !is_array($owner_contact_ids) )
7891 {
7892 $owner_contact_ids = array($owner_contact_ids);
7893 }
7894
7895 $contact_post_ids = $owner_contact_ids;
7896 }
7897 break;
7898 }
7899 }
7900
7901 if ( isset( $contact_post_ids ) )
7902 {
7903 foreach ( $contact_post_ids as $contact_post_id )
7904 {
7905 $solicitor_contact_id = get_post_meta( $contact_post_id, '_contact_solicitor_contact_id', TRUE );
7906 if ( !empty($solicitor_contact_id) )
7907 {
7908 $solicitor_name = get_the_title($solicitor_contact_id);
7909
7910 $solicitor_company_name = get_post_meta( $solicitor_contact_id, '_company_name', TRUE );
7911 if ( !empty($solicitor_company_name) && $solicitor_company_name != $solicitor_name )
7912 {
7913 $solicitor_name .= ' (' . $solicitor_company_name . ')';
7914 }
7915
7916 echo json_encode( array(
7917 'id' => $solicitor_contact_id,
7918 'name' => $solicitor_name,
7919 ) );
7920 break;
7921 }
7922 }
7923 }
7924
7925 // Quit out
7926 die();
7927 }
7928
7929 public function activate_pro_feature()
7930 {
7931 if ( !wp_verify_nonce( $_POST['_ajax_nonce'], "updates" ) )
7932 {
7933 $return = array(
7934 'errorMessage' => 'Invalid nonce provided'
7935 );
7936 wp_send_json_error($return);
7937 }
7938
7939 if ( ! current_user_can( 'install_plugins' ) )
7940 {
7941 $return = array(
7942 'errorMessage' => __( 'Sorry, you are not allowed to manage plugins on this site.' )
7943 );
7944 wp_send_json_error( $return );
7945 }
7946
7947 // check plugin status
7948 $slug = ph_clean($_POST['slug']);
7949
7950 $feature = get_ph_pro_feature( $slug );
7951
7952 if ( $feature === false )
7953 {
7954 $return = array(
7955 'errorMessage' => 'Feature not found'
7956 );
7957 wp_send_json_error($return);
7958 }
7959
7960 if ( is_plugin_active( $feature['wordpress_plugin_file'] ) )
7961 {
7962 $return = array(
7963 'errorMessage' => 'Plugin already active'
7964 );
7965 wp_send_json_error($return);
7966 }
7967
7968 $pro = false;
7969 $plans = (isset($feature['plans']) & is_array($feature['plans'])) ? $feature['plans'] : array();
7970 if ( !in_array('free', $plans) )
7971 {
7972 $pro = true;
7973 }
7974
7975 // check it's not a pro feature if they don't have pro enabled
7976 if ( $pro )
7977 {
7978 $valid_license_key = false;
7979
7980 // check it's not a plugin that was installed pre version 2
7981 $pre_pro_add_ons = get_option( 'propertyhive_pre_pro_add_ons', array() );
7982 if ( empty($pre_pro_add_ons) ) { $pre_pro_add_ons = array(); }
7983 foreach ($pre_pro_add_ons as $pre_pro_add_on)
7984 {
7985 if ( $pre_pro_add_on['slug'] == $slug )
7986 {
7987 // Yep. It was installed already and should be allowed to be activated
7988 $valid_license_key = true;
7989 }
7990 }
7991
7992 if ( $valid_license_key === false )
7993 {
7994 // check pro license key valid
7995 if ( PH()->license->is_valid_pro_license_key(true) )
7996 {
7997 $product_id_and_package = PH()->license->get_pro_license_product_id_and_package();
7998
7999 if ( isset($product_id_and_package['success']) && $product_id_and_package['success'] === true )
8000 {
8001 if (
8002 isset($feature['plans']) &&
8003 isset($product_id_and_package['package']) &&
8004 in_array($product_id_and_package['package'], $feature['plans'])
8005 )
8006 {
8007 $valid_license_key = true;
8008 }
8009 else
8010 {
8011 $return = array(
8012 'errorMessage' => 'Trying to activate a feature that\'s not on your chosen plan'
8013 );
8014 wp_send_json_error($return);
8015 }
8016 }
8017 else
8018 {
8019 $return = array(
8020 'errorMessage' => 'License key valid but failed to get package'
8021 );
8022 wp_send_json_error($return);
8023 }
8024 }
8025 else
8026 {
8027 $return = array(
8028 'errorMessage' => 'Trying to activate a PRO feature but no valid PRO license key entered'
8029 );
8030 wp_send_json_error($return);
8031 }
8032 }
8033
8034 if ( $valid_license_key === false )
8035 {
8036 $return = array(
8037 'errorMessage' => 'Trying to activate a PRO feature but no valid PRO license key entered'
8038 );
8039 wp_send_json_error($return);
8040 }
8041 }
8042
8043 if ( !is_dir(WP_PLUGIN_DIR . '/' . $slug) && strpos($feature['download_url'], 'wordpress.org') === false )
8044 {
8045 // not a public WP plugin. Must be hosted privately
8046 if ( !$pro )
8047 {
8048 // It's free, just let them have it
8049 $response = wp_remote_get(
8050 $feature['download_url'],
8051 array(
8052 'timeout' => 60,
8053 'sslverify' => true,
8054 )
8055 );
8056 }
8057 else
8058 {
8059 // Run through server check to ensure only the genuinely lovely humans get this Pro feature
8060 $response = wp_remote_post(
8061 'https://wp-property-hive.com/activate-pro-feature.php',
8062 array(
8063 'timeout' => 60,
8064 'sslverify' => true,
8065 'headers' => array(
8066 'Content-Type' => 'application/json',
8067 'X-PH-License-Key' => get_option( 'propertyhive_pro_license_key', '' ),
8068 'X-PH-License-Type' => PH()->license->get_license_type(),
8069 'X-PH-Instance-Id' => get_option( 'propertyhive_pro_instance_id', '' ),
8070 'X-PH-Plugin-Version' => PH_VERSION,
8071 ),
8072 'body' => wp_json_encode(array(
8073 'wordpress_plugin_file' => $feature['wordpress_plugin_file'],
8074 )),
8075 )
8076 );
8077 }
8078
8079 if ( is_wp_error( $response ) )
8080 {
8081 $return = array(
8082 'errorMessage' => $response->get_error_message()
8083 );
8084 wp_send_json_error($return);
8085 }
8086
8087 if ( !isset($response['body']) )
8088 {
8089 $return = array(
8090 'errorMessage' => 'No response body received'
8091 );
8092 wp_send_json_error($return);
8093 }
8094
8095 $zip_contents = $response['body']; // use the content
8096
8097 if ( empty($zip_contents) )
8098 {
8099 $return = array(
8100 'errorMessage' => 'Failed to obtain plugin'
8101 );
8102 wp_send_json_error($return);
8103 }
8104
8105 if ( ! wp_is_writable( WP_PLUGIN_DIR ) )
8106 {
8107 $return = array(
8108 'errorMessage' => 'Destination directory (' . WP_PLUGIN_DIR . ') for writing plugin temporarily does not exist or is not writable.'
8109 );
8110 wp_send_json_error($return);
8111 }
8112
8113 $tmpfname = WP_PLUGIN_DIR . '/' . $slug . '.zip';
8114
8115 $handle = @fopen($tmpfname, "w");
8116 if ( $handle === false )
8117 {
8118 $return = array(
8119 'errorMessage' => 'Failed to write plugin contents to temp file: ' . $tmpfname
8120 );
8121 wp_send_json_error($return);
8122 }
8123 fwrite($handle, $zip_contents);
8124 fclose($handle);
8125
8126 global $wp_filesystem;
8127
8128 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
8129 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
8130
8131 $wp_filesystem = new WP_Filesystem_Direct( false );
8132
8133 if ( !defined( 'FS_CHMOD_FILE' ) ) {
8134 define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
8135 }
8136 if ( !defined( 'FS_CHMOD_DIR' ) ) {
8137 define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
8138 }
8139
8140 // file obtained and stored. need to unzip and put into plugins directory
8141 $unzipped = unzip_file( $tmpfname, WP_PLUGIN_DIR );
8142 if ( is_wp_error( $unzipped ) )
8143 {
8144 @unlink($tmpfname);
8145
8146 $return = array(
8147 'errorMessage' => $unzipped->get_error_message()
8148 );
8149 wp_send_json_error($return);
8150 }
8151
8152 @unlink($tmpfname);
8153
8154 // Need to sort out cache for activate plugin to work
8155 // Taken from WordPress.org docs
8156 $cache_plugins = wp_cache_get( 'plugins', 'plugins' );
8157 if ( !empty( $cache_plugins ) )
8158 {
8159 $new_plugin = array(
8160 'Name' => $slug,
8161 'PluginURI' => '',
8162 'Version' => '',
8163 'Description' => '',
8164 'Author' => '',
8165 'AuthorURI' => '',
8166 'TextDomain' => '',
8167 'DomainPath' => '',
8168 'Network' => '',
8169 'Title' => $slug,
8170 'AuthorName' => '',
8171 );
8172 $cache_plugins[''][$feature['wordpress_plugin_file']] = $new_plugin;
8173 wp_cache_set( 'plugins', $cache_plugins, 'plugins' );
8174 }
8175 }
8176
8177 if ( is_dir(WP_PLUGIN_DIR . '/' . $slug) )
8178 {
8179 // folder already exists. just activate it
8180 $activated = activate_plugin( $feature['wordpress_plugin_file'] );
8181 if ( is_wp_error( $activated ) )
8182 {
8183 $return = array(
8184 'errorMessage' => $activated->get_error_message()
8185 );
8186 wp_send_json_error($return);
8187 }
8188
8189 wp_send_json_success();
8190 }
8191
8192 if ( strpos($feature['download_url'], 'wordpress.org') !== false )
8193 {
8194 // this is a public WP plugin
8195 wp_ajax_install_plugin();
8196 }
8197
8198 wp_send_json_success();
8199 }
8200
8201 public function deactivate_pro_feature()
8202 {
8203 if ( !wp_verify_nonce( $_POST['_ajax_nonce'], "updates" ) )
8204 {
8205 $return = array(
8206 'errorMessage' => 'Invalid nonce provided'
8207 );
8208 wp_send_json_error($return);
8209 }
8210
8211 if ( ! current_user_can( 'install_plugins' ) )
8212 {
8213 $return = array(
8214 'errorMessage' => __( 'Sorry, you are not allowed to manage plugins on this site.' )
8215 );
8216 wp_send_json_error( $return );
8217 }
8218
8219 // check plugin is active
8220 $slug = ph_clean($_POST['slug']);
8221
8222 $feature = get_ph_pro_feature( $slug );
8223
8224 if ( !is_plugin_active( $feature['wordpress_plugin_file'] ) )
8225 {
8226 $return = array(
8227 'errorMessage' => 'Plugin not active'
8228 );
8229 wp_send_json_error($return);
8230 }
8231
8232 deactivate_plugins( array($feature['wordpress_plugin_file']) );
8233
8234 wp_send_json_success();
8235 }
8236 }
8237
8238 new PH_AJAX();
8239