PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
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 1.4.62 All 260 releases
propertyhive / includes / class-ph-post-types.php

class-ph-post-types.php in Property Hive 2.3.0, at includes/class-ph-post-types.php

1,536 lines 67.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly
4 }
5
6 /**
7 * Post types
8 *
9 * Registers post types
10 *
11 * @class PH_Post_types
12 * @version 1.0.0
13 * @package PropertyHive/Classes
14 * @category Class
15 * @author PropertyHive
16 */
17 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public PH_Post_types class; preserve its existing name for plugin and extension compatibility.
18 class PH_Post_types {
19
20
21 /**
22 * Keep native CRM screens separate from ordinary WordPress post access.
23 * Existing post permissions remain required, alongside Property Hive access.
24 *
25 * @return array Dedicated primitive capabilities.
26 */
27 public static function record_capabilities() {
28 $capabilities = array();
29 foreach ( array( 'edit_posts', 'create_posts', 'edit_others_posts', 'edit_private_posts', 'edit_published_posts', 'publish_posts', 'delete_posts', 'delete_others_posts', 'delete_private_posts', 'delete_published_posts', 'read_private_posts' ) as $capability ) {
30 $capabilities[$capability] = 'propertyhive_' . $capability;
31 }
32 return $capabilities;
33 }
34
35 /**
36 * Resolve both direct checks and capabilities produced by core's post mapper.
37 * This preserves custom roles' existing edit/publish/delete restrictions.
38 *
39 * @param array $caps Required primitive capabilities.
40 * @return array Required capabilities.
41 */
42 public static function map_record_capabilities( $caps ) {
43 $mapped = array();
44 $record_caps = array_flip( self::record_capabilities() );
45 foreach ( $caps as $capability ) {
46 if ( isset( $record_caps[$capability] ) ) {
47 $mapped[] = 'manage_propertyhive';
48 $mapped[] = 'create_posts' === $record_caps[$capability] ? 'edit_posts' : $record_caps[$capability];
49 } else {
50 $mapped[] = $capability;
51 }
52 }
53 return array_values( array_unique( $mapped ) );
54 }
55
56 /**
57 * Constructor
58 */
59 public function __construct() {
60 add_filter( 'map_meta_cap', array( __CLASS__, 'map_record_capabilities' ) );
61 add_action( 'init', array( __CLASS__, 'register_taxonomies' ), 5 );
62 add_action( 'init', array( __CLASS__, 'register_post_types' ), 5 );
63 add_action( 'init', array( __CLASS__, 'register_post_statuses' ), 5 );
64
65 add_action( 'wp_trash_post', array( __CLASS__, 'trash_property_children' ), 5 );
66 add_action( 'wp_trash_post', array( __CLASS__, 'trash_property_enquiries' ), 5 );
67
68 add_action( 'before_delete_post', array( __CLASS__, 'delete_property_media' ), 5 );
69 add_action( 'before_delete_post', array( __CLASS__, 'delete_property_children' ), 5 );
70 add_action( 'before_delete_post', array( __CLASS__, 'delete_property_enquiries' ), 5 );
71 add_action( 'before_delete_post', array( __CLASS__, 'delete_contact_user' ), 5 );
72
73 add_action( 'delete_user', array( $this, 'delete_contact_user_link' ) );
74
75 add_action( 'save_post', array( __CLASS__, 'create_concatenated_indexable_meta' ), 99, 3 );
76
77 add_action( 'save_post', array( __CLASS__, 'ensure_property_floor_area_to_set' ), 99, 3 );
78
79 add_action( 'save_post', array( __CLASS__, 'update_property_indexed_owner_names' ), 99, 3 );
80
81 add_action( 'save_post', array( __CLASS__, 'store_related_viewings' ), 99, 3 );
82 add_action( 'updated_post_meta', array( __CLASS__, 'store_related_viewings_meta_change' ), 10, 4 );
83
84 add_action( 'propertyhive_update_address_concatenated', array( __CLASS__, 'update_address_concatenated' ) );
85
86 add_filter( 'get_terms', array( $this, 'put_terms_in_order' ), 10, 4 );
87
88 add_action( 'propertyhive_after_register_post_types', array( __CLASS__, 'maybe_flush_rewrite_rules' ) );
89 }
90
91 public static function maybe_flush_rewrite_rules()
92 {
93 if ( 'yes' === get_option( 'propertyhive_queue_flush_rewrite_rules' ) ) {
94 update_option( 'propertyhive_queue_flush_rewrite_rules', 'no' );
95 flush_rewrite_rules();
96 }
97 }
98
99 public static function register_post_statuses()
100 {
101 register_post_status(
102 'archive',
103 array(
104 'label' => _x('Archived', 'post status label', 'propertyhive'),
105 'public' => false,
106 'exclude_from_search' => true,
107 'show_in_admin_all_list' => false,
108 'show_in_admin_status_list' => true,
109 /* translators: %s: Number of archived posts. */
110 'label_count' => _n_noop(
111 'Archived <span class="count">(%s)</span>',
112 'Archived <span class="count">(%s)</span>',
113 'propertyhive'
114 ),
115 )
116 );
117 }
118
119 public static function update_property_indexed_owner_names( $post_id, $post, $update )
120 {
121 // $post_id and $post are required
122 if ( empty( $post_id ) || empty( $post ) ) {
123 return;
124 }
125
126 // Dont' save meta boxes for revisions or autosaves
127 if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
128 return;
129 }
130
131 if ( $post->post_type !== 'property' && $post->post_type !== 'contact' ) {
132 return;
133 }
134
135 if ( $post->post_type == 'property' )
136 {
137 // Get the owner contact IDs
138 $contact_ids = get_post_meta($post_id, '_owner_contact_id', true);
139 if ( !is_array($contact_ids) )
140 {
141 $contact_ids = array($contact_ids);
142 }
143
144 // Get the names of the contacts
145 $owner_details = array();
146 if ( !empty($contact_ids) )
147 {
148 foreach ( $contact_ids as $contact_id )
149 {
150 $contact_post = get_post($contact_id);
151 if ( $contact_post && $contact_post->post_type == 'contact' )
152 {
153 $owner_details[] = $contact_post->post_title;
154 if ( get_post_meta( $contact_id, '_telephone_number', TRUE ) != '' )
155 {
156 $owner_details[] = get_post_meta( $contact_id, '_telephone_number', TRUE );
157 }
158 if ( get_post_meta( $contact_id, '_email_address', TRUE ) != '' )
159 {
160 $owner_details[] = get_post_meta( $contact_id, '_email_address', TRUE );
161 }
162 }
163 }
164
165 $owner_details = array_filter($owner_details);
166 $owner_details = array_unique($owner_details);
167 }
168
169 // Update the owner names meta field
170 update_post_meta($post_id, '_owner_details', implode(', ', $owner_details));
171 }
172
173 if ( $post->post_type == 'contact' )
174 {
175 // Find all properties linked to this contact
176 $args = array(
177 'post_type' => 'property',
178 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Owner search metadata must be refreshed on properties linked through the existing serialized owner IDs; retain the relationship match.
179 'meta_query' => array(
180 array(
181 'key' => '_owner_contact_id',
182 'value' => '"' . $post_id . '"',
183 'compare' => 'LIKE'
184 )
185 ),
186 'posts_per_page' => -1
187 );
188
189 $properties = get_posts($args);
190 foreach ( $properties as $property )
191 {
192 // Get the owner contact IDs
193 $contact_ids = get_post_meta($property->ID, '_owner_contact_id', true);
194 if ( !is_array($contact_ids) )
195 {
196 $contact_ids = array($contact_ids);
197 }
198
199 // Get the names of the contacts
200 $owner_details = array();
201 if ( !empty($contact_ids) )
202 {
203 foreach ( $contact_ids as $contact_id )
204 {
205 $contact_post = get_post($contact_id);
206 if ( $contact_post && $contact_post->post_type == 'contact' )
207 {
208 $owner_details[] = $contact_post->post_title;
209 if ( get_post_meta( $contact_id, '_telephone_number', TRUE ) != '' )
210 {
211 $owner_details[] = get_post_meta( $contact_id, '_telephone_number', TRUE );
212 }
213 if ( get_post_meta( $contact_id, '_email_address', TRUE ) != '' )
214 {
215 $owner_details[] = get_post_meta( $contact_id, '_email_address', TRUE );
216 }
217 }
218 }
219
220 $owner_details = array_filter($owner_details);
221 $owner_details = array_unique($owner_details);
222 }
223
224 // Update the owner names meta field
225 update_post_meta($property->ID, '_owner_details', implode(', ', $owner_details));
226 }
227 }
228 }
229
230 public static function put_terms_in_order( $terms, $taxonomies, $query_vars, $term_query ) {
231
232 if ( empty($taxonomies) )
233 {
234 return $terms;
235 }
236
237 if ( !isset($query_vars['fields']) || ( isset($query_vars['fields']) && $query_vars['fields'] != 'all' ) )
238 {
239 return $terms;
240 }
241
242 if ( !is_array($taxonomies) ) { $taxonomies = array($taxonomies); }
243
244 foreach ( $taxonomies as $taxonomy_name )
245 {
246 $taxonomy = get_taxonomy( $taxonomy_name );
247
248 if ( is_array($taxonomy->object_type) && in_array('property', $taxonomy->object_type) )
249 {
250
251 }
252 else
253 {
254 return $terms;
255 }
256
257 $order = get_option( 'propertyhive_taxonomy_terms_order_' . $taxonomy_name, '' );
258
259 if ( empty($order) )
260 {
261 return $terms;
262 }
263
264 // Convert $order string to an array of IDs
265 $order_array = explode('|', $order);
266
267 // Initialize a new array to store the sorted objects
268 $sorted_array = array();
269
270 // Loop through each order ID and find matching WP_Term objects
271 foreach ( $order_array as $order_id )
272 {
273 foreach ( $terms as $item )
274 {
275 if ( $item->term_id == $order_id )
276 {
277 $sorted_array[] = $item;
278 break; // Stop the inner loop once a match is found
279 }
280 }
281 }
282
283 // Append WP_Term objects not listed in $order at the end of $sorted_array
284 foreach ( $terms as $item )
285 {
286 // Check if the item is not in $sorted_array
287 $found = false;
288 foreach ( $sorted_array as $sorted_item )
289 {
290 if ( $sorted_item->term_id == $item->term_id )
291 {
292 $found = true;
293 break;
294 }
295 }
296 // If not found, append to $sorted_array
297 if ( !$found )
298 {
299 $sorted_array[] = $item;
300 }
301 }
302
303 // Replace the original array with the sorted array
304 $terms = $sorted_array;
305 }
306
307 return $terms;
308
309 }
310
311 /**
312 * Register PropertyHive taxonomies.
313 */
314 public static function register_taxonomies() {
315
316 if ( taxonomy_exists( 'property_type' ) )
317 return;
318
319 do_action( 'propertyhive_register_taxonomy' );
320
321 //$permalinks = get_option( 'propertyhive_permalinks' );
322
323 register_taxonomy( 'availability',
324 apply_filters( 'propertyhive_taxonomy_objects_availability', array( 'property' ) ),
325 apply_filters(
326 'propertyhive_taxonomy_args_availability',
327 array(
328 'label' => __( 'Availabilities', 'propertyhive' ),
329 'hierarchical' => true,
330 'show_ui' => false,
331 'show_in_nav_menus' => false,
332 'query_var' => is_admin(),
333 'rewrite' => false,
334 'public' => true,
335 'show_in_rest' => true,
336 )
337 )
338 );
339
340 register_taxonomy( 'property_type',
341 apply_filters( 'propertyhive_taxonomy_objects_property_type', array( 'property', 'appraisal' ) ),
342 apply_filters(
343 'propertyhive_taxonomy_args_property_type',
344 array(
345 'label' => __( 'Property Types', 'propertyhive' ),
346 'hierarchical' => true,
347 'show_ui' => false,
348 'show_in_nav_menus' => false,
349 'query_var' => is_admin(),
350 'rewrite' => false,
351 'public' => true,
352 'show_in_rest' => true,
353 )
354 )
355 );
356
357 register_taxonomy( 'commercial_property_type',
358 apply_filters( 'propertyhive_taxonomy_objects_commercial_property_type', array( 'property', 'appraisal' ) ),
359 apply_filters(
360 'propertyhive_taxonomy_args_commercial_property_type',
361 array(
362 'label' => __( 'Commercial Property Types', 'propertyhive' ),
363 'hierarchical' => true,
364 'show_ui' => false,
365 'show_in_nav_menus' => false,
366 'query_var' => is_admin(),
367 'rewrite' => false,
368 'public' => true,
369 'show_in_rest' => true,
370 )
371 )
372 );
373
374 register_taxonomy( 'location',
375 apply_filters( 'propertyhive_taxonomy_objects_location', array( 'property' ) ),
376 apply_filters(
377 'propertyhive_taxonomy_args_location',
378 array(
379 'label' => __( 'Locations', 'propertyhive' ),
380 'hierarchical' => true,
381 'show_ui' => false,
382 'show_in_nav_menus' => false,
383 'query_var' => is_admin(),
384 'rewrite' => false,
385 'public' => true,
386 'show_in_rest' => true,
387 )
388 )
389 );
390
391 register_taxonomy( 'parking',
392 apply_filters( 'propertyhive_taxonomy_objects_parking', array( 'property', 'appraisal' ) ),
393 apply_filters(
394 'propertyhive_taxonomy_args_parking',
395 array(
396 'label' => __( 'Parking', 'propertyhive' ),
397 'hierarchical' => true,
398 'show_ui' => false,
399 'show_in_nav_menus' => false,
400 'query_var' => is_admin(),
401 'rewrite' => false,
402 'public' => true,
403 'show_in_rest' => true,
404 )
405 )
406 );
407
408 register_taxonomy( 'outside_space',
409 apply_filters( 'propertyhive_taxonomy_objects_outside_space', array( 'property', 'appraisal' ) ),
410 apply_filters(
411 'propertyhive_taxonomy_args_outside_space',
412 array(
413 'label' => __( 'Outside Spaces', 'propertyhive' ),
414 'hierarchical' => true,
415 'show_ui' => false,
416 'show_in_nav_menus' => false,
417 'query_var' => is_admin(),
418 'rewrite' => false,
419 'public' => true,
420 'show_in_rest' => true,
421 )
422 )
423 );
424
425 register_taxonomy( 'price_qualifier',
426 apply_filters( 'propertyhive_taxonomy_objects_price_qualifier', array( 'property' ) ),
427 apply_filters(
428 'propertyhive_taxonomy_args_price_qualifier',
429 array(
430 'label' => __( 'Price Qualifiers', 'propertyhive' ),
431 'hierarchical' => true,
432 'show_ui' => false,
433 'show_in_nav_menus' => false,
434 'query_var' => is_admin(),
435 'rewrite' => false,
436 'public' => true,
437 'show_in_rest' => true,
438 )
439 )
440 );
441
442 register_taxonomy( 'tenure',
443 apply_filters( 'propertyhive_taxonomy_objects_tenure', array( 'property' ) ),
444 apply_filters(
445 'propertyhive_taxonomy_args_tenure',
446 array(
447 'label' => __( 'Tenures', 'propertyhive' ),
448 'hierarchical' => true,
449 'show_ui' => false,
450 'show_in_nav_menus' => false,
451 'query_var' => is_admin(),
452 'rewrite' => false,
453 'public' => true,
454 'show_in_rest' => true,
455 )
456 )
457 );
458
459 register_taxonomy( 'commercial_tenure',
460 apply_filters( 'propertyhive_taxonomy_objects_commercial_tenure', array( 'property' ) ),
461 apply_filters(
462 'propertyhive_taxonomy_args_commercial_tenure',
463 array(
464 'label' => __( 'Commercial Tenures', 'propertyhive' ),
465 'hierarchical' => true,
466 'show_ui' => false,
467 'show_in_nav_menus' => false,
468 'query_var' => is_admin(),
469 'rewrite' => false,
470 'public' => true,
471 'show_in_rest' => true,
472 )
473 )
474 );
475
476 register_taxonomy( 'sale_by',
477 apply_filters( 'propertyhive_taxonomy_objects_sale_by', array( 'property' ) ),
478 apply_filters(
479 'propertyhive_taxonomy_args_sale_by',
480 array(
481 'label' => __( 'Sale By', 'propertyhive' ),
482 'hierarchical' => true,
483 'show_ui' => false,
484 'show_in_nav_menus' => false,
485 'query_var' => is_admin(),
486 'rewrite' => false,
487 'public' => true,
488 'show_in_rest' => true,
489 )
490 )
491 );
492
493 register_taxonomy( 'furnished',
494 apply_filters( 'propertyhive_taxonomy_objects_furnished', array( 'property', 'appraisal' ) ),
495 apply_filters(
496 'propertyhive_taxonomy_args_furnished',
497 array(
498 'label' => __( 'Furnished', 'propertyhive' ),
499 'hierarchical' => true,
500 'show_ui' => false,
501 'show_in_nav_menus' => false,
502 'query_var' => is_admin(),
503 'rewrite' => false,
504 'public' => true,
505 'show_in_rest' => true,
506 )
507 )
508 );
509
510 register_taxonomy( 'marketing_flag',
511 apply_filters( 'propertyhive_taxonomy_objects_marketing_flag', array( 'property' ) ),
512 apply_filters(
513 'propertyhive_taxonomy_args_marketing_flag',
514 array(
515 'label' => __( 'Marketing Flags', 'propertyhive' ),
516 'hierarchical' => true,
517 'show_ui' => false,
518 'show_in_nav_menus' => false,
519 'query_var' => is_admin(),
520 'rewrite' => false,
521 'public' => true,
522 'show_in_rest' => true,
523 )
524 )
525 );
526
527 register_taxonomy( 'property_feature',
528 apply_filters( 'propertyhive_taxonomy_objects_property_feature', array( 'property' ) ),
529 apply_filters(
530 'propertyhive_taxonomy_args_property_feature',
531 array(
532 'label' => __( 'Property Features', 'propertyhive' ),
533 'hierarchical' => true,
534 'show_ui' => false,
535 'show_in_nav_menus' => false,
536 'query_var' => is_admin(),
537 'rewrite' => false,
538 'public' => true,
539 'show_in_rest' => true,
540 )
541 )
542 );
543
544 register_taxonomy( 'management_key_date_type',
545 apply_filters( 'propertyhive_taxonomy_objects_management_key_date_type', array( 'key_date' ) ),
546 apply_filters(
547 'propertyhive_taxonomy_args_management_key_date_type',
548 array(
549 'label' => __( 'Management Dates', 'propertyhive' ),
550 'hierarchical' => true,
551 'show_ui' => false,
552 'show_in_nav_menus' => false,
553 'query_var' => is_admin(),
554 'rewrite' => false,
555 'public' => true
556 )
557 )
558 );
559
560 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Existing public Property Hive extension hook do_action_after_register_taxonomies; changing the established name would detach installed callbacks.
561 do_action( 'do_action_after_register_taxonomies' );
562 }
563
564 /**
565 * Register core post types
566 */
567 public static function register_post_types() {
568
569 if ( post_type_exists('property') )
570 return;
571
572 do_action( 'propertyhive_register_post_type' );
573
574 //$permalinks = get_option( 'property_permalinks' );
575 //$product_permalink = empty( $permalinks['property_base'] ) ? _x( 'property', 'slug', 'propertyhive' ) : $permalinks['property_base'];
576
577 register_post_type( "property",
578 apply_filters( 'propertyhive_register_post_type_property',
579 array(
580 'labels' => array(
581 'name' => __( 'Properties', 'propertyhive' ),
582 'singular_name' => __( 'Property', 'propertyhive' ),
583 'menu_name' => _x( 'Properties', 'Admin menu name', 'propertyhive' ),
584 'add_new' => __( 'Add Property', 'propertyhive' ),
585 'add_new_item' => __( 'Add New Property', 'propertyhive' ),
586 'edit' => __( 'Edit', 'propertyhive' ),
587 'edit_item' => __( 'Edit Property', 'propertyhive' ),
588 'new_item' => __( 'New Property', 'propertyhive' ),
589 'view' => __( 'View Property', 'propertyhive' ),
590 'view_item' => __( 'View Property', 'propertyhive' ),
591 'search_items' => __( 'Search Properties', 'propertyhive' ),
592 'not_found' => __( 'No properties found', 'propertyhive' ),
593 'not_found_in_trash' => __( 'No properties found in trash', 'propertyhive' ),
594 'parent' => __( 'Parent Property', 'propertyhive' )
595 ),
596 'description' => __( 'This is where you can add new properties to your site.', 'propertyhive' ),
597 'public' => true,
598 'show_ui' => true,
599 'capability_type' => 'post',
600 'capabilities' => self::record_capabilities(),
601 'map_meta_cap' => true,
602 'publicly_queryable' => true,
603 'exclude_from_search' => false,
604 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
605 //'rewrite' => $product_permalink ? array( 'slug' => untrailingslashit( $product_permalink ), 'with_front' => false, 'feeds' => true ) : false,
606 'query_var' => true,
607 'supports' => array( 'title', 'excerpt' ),
608 'has_archive' => ( $search_results_page_id = ph_get_page_id( 'search_results' ) ) && get_page( $search_results_page_id ) ? get_page_uri( $search_results_page_id ) : 'search_results',
609 'show_in_nav_menus' => true,
610 'show_in_menu' => false,
611 'show_in_admin_bar' => true,
612 'show_in_rest' => true,
613 )
614 )
615 );
616
617 register_post_type( "contact",
618 apply_filters( 'propertyhive_register_post_type_contact',
619 array(
620 'labels' => array(
621 'name' => __( 'Contacts', 'propertyhive' ),
622 'singular_name' => __( 'Contact', 'propertyhive' ),
623 'menu_name' => _x( 'Contacts', 'Admin menu name', 'propertyhive' ),
624 'add_new' => __( 'Add Contact', 'propertyhive' ),
625 'add_new_item' => __( 'Add New Contact', 'propertyhive' ),
626 'edit' => __( 'Edit', 'propertyhive' ),
627 'edit_item' => __( 'Edit Contact', 'propertyhive' ),
628 'new_item' => __( 'New Contact', 'propertyhive' ),
629 'view' => __( 'View Contact', 'propertyhive' ),
630 'view_item' => __( 'View Contact', 'propertyhive' ),
631 'search_items' => __( 'Search Contacts', 'propertyhive' ),
632 'not_found' => __( 'No contacts found', 'propertyhive' ),
633 'not_found_in_trash' => __( 'No contacts found in trash', 'propertyhive' ),
634 'parent' => __( 'Parent Contact', 'propertyhive' )
635 ),
636 'description' => __( 'This is where you can add new contacts to your site.', 'propertyhive' ),
637 'public' => false,
638 'show_ui' => true,
639 'capability_type' => 'post',
640 'capabilities' => self::record_capabilities(),
641 'map_meta_cap' => true,
642 'publicly_queryable' => false,
643 'exclude_from_search' => true,
644 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
645 'query_var' => true,
646 'supports' => array( 'title' ),
647 'show_in_nav_menus' => false,
648 'show_in_menu' => false
649 )
650 )
651 );
652
653 register_post_type( "office",
654 apply_filters( 'propertyhive_register_post_type_office',
655 array(
656 'labels' => array(
657 'name' => __( 'Offices', 'propertyhive' ),
658 'singular_name' => __( 'Office', 'propertyhive' ),
659 'menu_name' => _x( 'Offices', 'Admin menu name', 'propertyhive' ),
660 'add_new' => __( 'Add Office', 'propertyhive' ),
661 'add_new_item' => __( 'Add New Office', 'propertyhive' ),
662 'edit' => __( 'Edit', 'propertyhive' ),
663 'edit_item' => __( 'Edit Office', 'propertyhive' ),
664 'new_item' => __( 'New Office', 'propertyhive' ),
665 'view' => __( 'View Office', 'propertyhive' ),
666 'view_item' => __( 'View Office', 'propertyhive' ),
667 'search_items' => __( 'Search Offices', 'propertyhive' ),
668 'not_found' => __( 'No offices found', 'propertyhive' ),
669 'not_found_in_trash' => __( 'No offices found in trash', 'propertyhive' ),
670 'parent' => __( 'Parent Office', 'propertyhive' )
671 ),
672 'public' => true,
673 'show_ui' => false,
674 'capability_type' => 'post',
675 'capabilities' => self::record_capabilities(),
676 'map_meta_cap' => true,
677 'publicly_queryable' => false,
678 'exclude_from_search' => true,
679 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
680 'query_var' => true,
681 'supports' => array( 'title' ),
682 'show_in_nav_menus' => false,
683 'show_in_menu' => false,
684 'show_in_rest' => true,
685 )
686 )
687 );
688
689 register_post_type( "enquiry",
690 apply_filters( 'propertyhive_register_post_type_enquiry',
691 array(
692 'labels' => array(
693 'name' => __( 'Enquiries', 'propertyhive' ),
694 'singular_name' => __( 'Enquiry', 'propertyhive' ),
695 'menu_name' => _x( 'Enquiries', 'Admin menu name', 'propertyhive' ),
696 'add_new' => __( 'Add Enquiry', 'propertyhive' ),
697 'add_new_item' => __( 'Add New Enquiry', 'propertyhive' ),
698 'edit' => __( 'Edit', 'propertyhive' ),
699 'edit_item' => __( 'Edit Enquiry', 'propertyhive' ),
700 'new_item' => __( 'New Enquiry', 'propertyhive' ),
701 'view' => __( 'View Enquiry', 'propertyhive' ),
702 'view_item' => __( 'View Enquiry', 'propertyhive' ),
703 'search_items' => __( 'Search Enquiries', 'propertyhive' ),
704 'not_found' => __( 'No enquiries found', 'propertyhive' ),
705 'not_found_in_trash' => __( 'No enquiries found in trash', 'propertyhive' ),
706 'parent' => __( 'Parent Enquiry', 'propertyhive' )
707 ),
708 'public' => false,
709 'show_ui' => true,
710 'capability_type' => 'post',
711 'capabilities' => self::record_capabilities(),
712 'map_meta_cap' => true,
713 'publicly_queryable' => false,
714 'exclude_from_search' => true,
715 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
716 'query_var' => true,
717 'supports' => array( 'title' ),
718 'show_in_nav_menus' => false,
719 'show_in_menu' => false,
720 'show_in_rest' => true,
721 )
722 )
723 );
724
725 register_post_type( "appraisal",
726 apply_filters( 'propertyhive_register_post_type_appraisal',
727 array(
728 'labels' => array(
729 'name' => __( 'Appraisals', 'propertyhive' ),
730 'singular_name' => __( 'Appraisal', 'propertyhive' ),
731 'menu_name' => _x( 'Appraisals', 'Admin menu name', 'propertyhive' ),
732 'add_new' => __( 'Add Appraisal', 'propertyhive' ),
733 'add_new_item' => __( 'Add New Appraisal', 'propertyhive' ),
734 'edit' => __( 'Edit', 'propertyhive' ),
735 'edit_item' => __( 'Edit Appraisal', 'propertyhive' ),
736 'new_item' => __( 'New Appraisal', 'propertyhive' ),
737 'view' => __( 'View Appraisal', 'propertyhive' ),
738 'view_item' => __( 'View Appraisal', 'propertyhive' ),
739 'search_items' => __( 'Search Appraisals', 'propertyhive' ),
740 'not_found' => __( 'No appraisals found', 'propertyhive' ),
741 'not_found_in_trash' => __( 'No appraisals found in trash', 'propertyhive' ),
742 'parent' => __( 'Parent Appraisal', 'propertyhive' )
743 ),
744 'public' => false,
745 'show_ui' => true,
746 'capability_type' => 'post',
747 'capabilities' => self::record_capabilities(),
748 'map_meta_cap' => true,
749 'publicly_queryable' => false,
750 'exclude_from_search' => true,
751 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
752 'query_var' => true,
753 'supports' => false,
754 'show_in_nav_menus' => false,
755 'show_in_menu' => false
756 )
757 )
758 );
759
760 register_post_type( "viewing",
761 apply_filters( 'propertyhive_register_post_type_viewing',
762 array(
763 'labels' => array(
764 'name' => __( 'Viewings', 'propertyhive' ),
765 'singular_name' => __( 'Viewing', 'propertyhive' ),
766 'menu_name' => _x( 'Viewings', 'Admin menu name', 'propertyhive' ),
767 'add_new' => __( 'Add Viewing', 'propertyhive' ),
768 'add_new_item' => __( 'Add New Viewing', 'propertyhive' ),
769 'edit' => __( 'Edit', 'propertyhive' ),
770 'edit_item' => __( 'Edit Viewing', 'propertyhive' ),
771 'new_item' => __( 'New Viewing', 'propertyhive' ),
772 'view' => __( 'View Viewing', 'propertyhive' ),
773 'view_item' => __( 'View Viewing', 'propertyhive' ),
774 'search_items' => __( 'Search Viewings', 'propertyhive' ),
775 'not_found' => __( 'No viewings found', 'propertyhive' ),
776 'not_found_in_trash' => __( 'No viewings found in trash', 'propertyhive' ),
777 'parent' => __( 'Parent Viewing', 'propertyhive' )
778 ),
779 'public' => false,
780 'show_ui' => true,
781 'capability_type' => 'post',
782 'capabilities' => self::record_capabilities(),
783 'map_meta_cap' => true,
784 'publicly_queryable' => false,
785 'exclude_from_search' => true,
786 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
787 'query_var' => true,
788 'supports' => false,
789 'show_in_nav_menus' => false,
790 'show_in_menu' => false
791 )
792 )
793 );
794
795 register_post_type( "offer",
796 apply_filters( 'propertyhive_register_post_type_offer',
797 array(
798 'labels' => array(
799 'name' => __( 'Offers', 'propertyhive' ),
800 'singular_name' => __( 'Offer', 'propertyhive' ),
801 'menu_name' => _x( 'Offers', 'Admin menu name', 'propertyhive' ),
802 'add_new' => __( 'Add Offer', 'propertyhive' ),
803 'add_new_item' => __( 'Add New Offer', 'propertyhive' ),
804 'edit' => __( 'Edit', 'propertyhive' ),
805 'edit_item' => __( 'Edit Offer', 'propertyhive' ),
806 'new_item' => __( 'New Offer', 'propertyhive' ),
807 'view' => __( 'View Offer', 'propertyhive' ),
808 'view_item' => __( 'View Offer', 'propertyhive' ),
809 'search_items' => __( 'Search Offers', 'propertyhive' ),
810 'not_found' => __( 'No offers found', 'propertyhive' ),
811 'not_found_in_trash' => __( 'No offers found in trash', 'propertyhive' ),
812 'parent' => __( 'Parent Offer', 'propertyhive' )
813 ),
814 'public' => false,
815 'show_ui' => true,
816 'capability_type' => 'post',
817 'capabilities' => self::record_capabilities(),
818 'map_meta_cap' => true,
819 'publicly_queryable' => false,
820 'exclude_from_search' => true,
821 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
822 'query_var' => true,
823 'supports' => false,
824 'show_in_nav_menus' => false,
825 'show_in_menu' => false
826 )
827 )
828 );
829
830 register_post_type( "sale",
831 apply_filters( 'propertyhive_register_post_type_sale',
832 array(
833 'labels' => array(
834 'name' => __( 'Sales', 'propertyhive' ),
835 'singular_name' => __( 'Sale', 'propertyhive' ),
836 'menu_name' => _x( 'Sales', 'Admin menu name', 'propertyhive' ),
837 'add_new' => __( 'Add Sale', 'propertyhive' ),
838 'add_new_item' => __( 'Add New Sale', 'propertyhive' ),
839 'edit' => __( 'Edit', 'propertyhive' ),
840 'edit_item' => __( 'Edit Sale', 'propertyhive' ),
841 'new_item' => __( 'New Sale', 'propertyhive' ),
842 'view' => __( 'View Sale', 'propertyhive' ),
843 'view_item' => __( 'View Sale', 'propertyhive' ),
844 'search_items' => __( 'Search Sales', 'propertyhive' ),
845 'not_found' => __( 'No sales found', 'propertyhive' ),
846 'not_found_in_trash' => __( 'No sales found in trash', 'propertyhive' ),
847 'parent' => __( 'Parent Sale', 'propertyhive' )
848 ),
849 'public' => false,
850 'show_ui' => true,
851 'capability_type' => 'post',
852 'capabilities' => array_merge( self::record_capabilities(), array(
853 'create_posts' => false
854 ) ),
855 'map_meta_cap' => true,
856 'publicly_queryable' => false,
857 'exclude_from_search' => true,
858 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
859 'query_var' => true,
860 'supports' => false,
861 'show_in_nav_menus' => false,
862 'show_in_menu' => false
863 )
864 )
865 );
866
867 register_post_type( "tenancy",
868 apply_filters( 'propertyhive_register_post_type_tenancy',
869 array(
870 'labels' => array(
871 'name' => __( 'Tenancies', 'propertyhive' ),
872 'singular_name' => __( 'Tenancy', 'propertyhive' ),
873 'menu_name' => _x( 'Tenancies', 'Admin menu name', 'propertyhive' ),
874 'add_new' => __( 'Add Tenancy', 'propertyhive' ),
875 'add_new_item' => __( 'Add New Tenancy', 'propertyhive' ),
876 'edit' => __( 'Edit', 'propertyhive' ),
877 'edit_item' => __( 'Edit Tenancy', 'propertyhive' ),
878 'new_item' => __( 'New Tenancy', 'propertyhive' ),
879 'view' => __( 'View Tenancy', 'propertyhive' ),
880 'view_item' => __( 'View Tenancy', 'propertyhive' ),
881 'search_items' => __( 'Search Tenancies', 'propertyhive' ),
882 'not_found' => __( 'No tenancies found', 'propertyhive' ),
883 'not_found_in_trash' => __( 'No tenancies found in trash', 'propertyhive' ),
884 'parent' => __( 'Parent Tenancy', 'propertyhive' )
885 ),
886 'public' => false,
887 'show_ui' => true,
888 'capability_type' => 'post',
889 'capabilities' => self::record_capabilities(),
890 'map_meta_cap' => true,
891 'publicly_queryable' => false,
892 'exclude_from_search' => true,
893 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
894 'query_var' => true,
895 'supports' => false,
896 'show_in_nav_menus' => false,
897 'show_in_menu' => false
898 )
899 )
900 );
901
902 register_post_type( "key_date",
903 apply_filters( 'propertyhive_register_post_type_key_date',
904 array(
905 'labels' => array(
906 'name' => __( 'Key Dates', 'propertyhive' ),
907 'singular_name' => __( 'Key Date', 'propertyhive' ),
908 'menu_name' => _x( 'Management', 'Admin menu name', 'propertyhive' ),
909 'add_new' => __( 'Add Key Date', 'propertyhive' ),
910 'add_new_item' => __( 'Add New Key Date', 'propertyhive' ),
911 'edit' => __( 'Edit', 'propertyhive' ),
912 'edit_item' => __( 'Edit Key Date', 'propertyhive' ),
913 'new_item' => __( 'New Key Date', 'propertyhive' ),
914 'view' => __( 'View Key Date', 'propertyhive' ),
915 'view_item' => __( 'View Key Date', 'propertyhive' ),
916 'search_items' => __( 'Search Key Dates', 'propertyhive' ),
917 'not_found' => __( 'No key dates found', 'propertyhive' ),
918 'not_found_in_trash' => __( 'No key dates found in trash', 'propertyhive' ),
919 'parent' => __( 'Parent Key Date', 'propertyhive' )
920 ),
921 'public' => false,
922 'show_ui' => true,
923 'capability_type' => 'post',
924 'map_meta_cap' => true,
925 'publicly_queryable' => false,
926 'exclude_from_search' => true,
927 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
928 'query_var' => true,
929 'supports' => false,
930 'show_in_nav_menus' => false,
931 'show_in_menu' => false,
932 'capabilities' => array_merge( self::record_capabilities(), array(
933 'create_posts' => 'do_not_allow'
934 ) ),
935 )
936 )
937 );
938 do_action( 'propertyhive_after_register_post_types' );
939 }
940
941 public static function trash_property_children( $post_id )
942 {
943 if ( get_post_type($post_id) == 'property' )
944 {
945 // get all children
946 $args = array(
947 'post_type' => 'property',
948 'nopaging' => true,
949 'post_parent' => (int)$post_id,
950 'suppress_filters' => TRUE,
951 'post_status' => array('publish', 'pending', 'private', 'draft', 'auto-draft', 'future'),
952 );
953
954 $properties_query = new WP_Query($args);
955
956 if ( $properties_query->have_posts() )
957 {
958 while( $properties_query->have_posts() )
959 {
960 $properties_query->the_post();
961
962 wp_trash_post( get_the_ID() );
963 }
964 }
965 wp_reset_postdata();
966 }
967 }
968
969 public static function trash_property_enquiries( $post_id )
970 {
971 if ( apply_filters( 'propertyhive_delete_enquiries_on_property_delete', false ) === true && get_post_type($post_id) == 'property' )
972 {
973 $args = array(
974 'post_type' => 'enquiry',
975 'nopaging' => true,
976 'post_status' => array('publish', 'pending', 'private', 'draft', 'auto-draft', 'future', 'trash'),
977 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Both legacy and current property relationship metadata must match when the explicit enquiry-trash option is enabled.
978 'meta_query' => array(
979 'relation' => 'OR',
980 array(
981 'key' => '_property_id',
982 'value' => $post_id
983 ),
984 array(
985 'key' => 'property_id',
986 'value' => $post_id
987 )
988 )
989 );
990
991 $enquiries_query = new WP_Query($args);
992
993 if ( $enquiries_query->have_posts() )
994 {
995 while( $enquiries_query->have_posts() )
996 {
997 $enquiries_query->the_post();
998
999 wp_trash_post( get_the_ID() );
1000 }
1001 }
1002 wp_reset_postdata();
1003 }
1004 }
1005
1006 public static function delete_property_media( $post_id )
1007 {
1008 if ( get_post_type($post_id) == 'property' && apply_filters( 'propertyhive_remove_media_on_property_delete', TRUE ) === TRUE )
1009 {
1010 $property = new PH_Property( (int)$post_id );
1011
1012 $media_ids = $property->get_gallery_attachment_ids();
1013 self::do_delete_media_attachments( $post_id, $media_ids );
1014
1015 $media_ids = $property->get_floorplan_attachment_ids();
1016 self::do_delete_media_attachments( $post_id, $media_ids );
1017
1018 $media_ids = $property->get_brochure_attachment_ids();
1019 self::do_delete_media_attachments( $post_id, $media_ids );
1020
1021 $media_ids = $property->get_epc_attachment_ids();
1022 self::do_delete_media_attachments( $post_id, $media_ids );
1023 }
1024 }
1025
1026 private static function do_delete_media_attachments( $post_id, $media_ids = array() )
1027 {
1028 if ( is_array($media_ids) && !empty($media_ids) )
1029 {
1030 foreach ( $media_ids as $media_id )
1031 {
1032 // Make sure this media isn't used anywhere else
1033 $args = array(
1034 'post_type' => 'property',
1035 'fields' => 'ids',
1036 'suppress_filters' => TRUE,
1037 'posts_per_page' => 1,
1038 // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Excludes only the property being deleted so its own media reference cannot prevent attachment cleanup.
1039 'post__not_in' => array( $post_id ),
1040 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Before deleting an attachment, check all four serialized property media relationships; the query stops at one matching property to preserve shared media.
1041 'meta_query' => array(
1042 'relation' => 'OR',
1043 array(
1044 'key' => '_photos',
1045 'value' => ':' . $media_id . ';',
1046 'compare' => 'LIKE'
1047 ),
1048 array(
1049 'key' => '_photos',
1050 'value' => ':"' . $media_id . '";',
1051 'compare' => 'LIKE'
1052 ),
1053 array(
1054 'key' => '_floorplans',
1055 'value' => ':' . $media_id . ';',
1056 'compare' => 'LIKE'
1057 ),
1058 array(
1059 'key' => '_floorplans',
1060 'value' => ':"' . $media_id . '";',
1061 'compare' => 'LIKE'
1062 ),
1063 array(
1064 'key' => '_epcs',
1065 'value' => ':' . $media_id . ';',
1066 'compare' => 'LIKE'
1067 ),
1068 array(
1069 'key' => '_epcs',
1070 'value' => ':"' . $media_id . '";',
1071 'compare' => 'LIKE'
1072 ),
1073 array(
1074 'key' => '_brochures',
1075 'value' => ':' . $media_id . ';',
1076 'compare' => 'LIKE'
1077 ),
1078 array(
1079 'key' => '_brochures',
1080 'value' => ':"' . $media_id . '";',
1081 'compare' => 'LIKE'
1082 ),
1083 )
1084 );
1085
1086 $property_query = new WP_Query( $args );
1087
1088 if ( !$property_query->have_posts() )
1089 {
1090 // We're ok to delete
1091 wp_delete_attachment( $media_id, true );
1092 }
1093 }
1094 }
1095 }
1096
1097 public static function delete_property_children( $post_id )
1098 {
1099 if ( get_post_type($post_id) == 'property' )
1100 {
1101 // get all children
1102 $args = array(
1103 'post_type' => 'property',
1104 'nopaging' => true,
1105 'suppress_filters' => TRUE,
1106 'post_parent' => (int)$post_id,
1107 'post_status' => array('publish', 'pending', 'private', 'draft', 'auto-draft', 'future', 'trash'),
1108 );
1109
1110 $properties_query = new WP_Query($args);
1111
1112 if ( $properties_query->have_posts() )
1113 {
1114 while( $properties_query->have_posts() )
1115 {
1116 $properties_query->the_post();
1117
1118 wp_delete_post( get_the_ID(), true );
1119 }
1120 }
1121 wp_reset_postdata();
1122 }
1123 }
1124
1125 public static function delete_property_enquiries( $post_id )
1126 {
1127 if ( apply_filters( 'propertyhive_delete_enquiries_on_property_delete', false ) === true && get_post_type($post_id) == 'property' )
1128 {
1129 $args = array(
1130 'post_type' => 'enquiry',
1131 'nopaging' => true,
1132 'post_status' => array('publish', 'pending', 'private', 'draft', 'auto-draft', 'future', 'trash'),
1133 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Both legacy and current property relationship metadata must match when the explicit enquiry-delete option is enabled.
1134 'meta_query' => array(
1135 'relation' => 'OR',
1136 array(
1137 'key' => '_property_id',
1138 'value' => $post_id
1139 ),
1140 array(
1141 'key' => 'property_id',
1142 'value' => $post_id
1143 )
1144 )
1145 );
1146
1147 $enquiries_query = new WP_Query($args);
1148
1149 if ( $enquiries_query->have_posts() )
1150 {
1151 while( $enquiries_query->have_posts() )
1152 {
1153 $enquiries_query->the_post();
1154
1155 wp_delete_post( get_the_ID(), true );
1156 }
1157 }
1158 wp_reset_postdata();
1159 }
1160 }
1161
1162 public static function delete_contact_user( $post_id )
1163 {
1164 if ( get_post_type($post_id) == 'contact' )
1165 {
1166 // If the contact being deleted has a user_id meta_key, delete the user with that ID if they're a contact or agent
1167 $contact_user_id = get_post_meta ($post_id, '_user_id', TRUE );
1168 if ( !empty($contact_user_id) )
1169 {
1170 $user_meta = get_userdata($contact_user_id);
1171 $user_roles = $user_meta->roles;
1172 $user_role = array_shift($user_roles);
1173
1174 if ( in_array($user_role, array( 'property_hive_contact' )) )
1175 {
1176 // Include user admin functions to get access to wp_delete_user().
1177 require_once ABSPATH . 'wp-admin/includes/user.php';
1178 wp_delete_user( $contact_user_id );
1179 }
1180 }
1181 }
1182 }
1183
1184 public static function delete_contact_user_link( $user_id )
1185 {
1186 global $post;
1187
1188 $args = array(
1189 'post_type' => 'contact',
1190 'nopaging' => true,
1191 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Remove only contacts linked to the deleted user through their stored user ID.
1192 'meta_query' => array(
1193 array(
1194 'key' => '_user_id',
1195 'value' => (int)$user_id,
1196 )
1197 )
1198 );
1199 $agent_query = new WP_Query( $args );
1200 if ( $agent_query->have_posts() )
1201 {
1202 while ( $agent_query->have_posts() )
1203 {
1204 $agent_query->the_post();
1205
1206 delete_post_meta( $post->ID, '_user_id' );
1207
1208 wp_reset_postdata();
1209 }
1210 }
1211 }
1212
1213 public static function ensure_property_floor_area_to_set( $post_id, $post, $update )
1214 {
1215 // $post_id and $post are required
1216 if ( empty( $post_id ) || empty( $post ) ) {
1217 return;
1218 }
1219
1220 // Dont' save meta boxes for revisions or autosaves
1221 if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
1222 return;
1223 }
1224
1225 if ( $post->post_type !== 'property' ) {
1226 return;
1227 }
1228
1229 $property = new PH_Property( $post_id );
1230
1231
1232 if ( $property->department == 'commercial' || ph_get_custom_department_based_on( $property->department ) == 'commercial' )
1233 {
1234 if ( ($property->floor_area_to_sqft == '' || $property->floor_area_to_sqft == '0') && ( $property->floor_area_from_sqft != '' && $property->floor_area_from_sqft != '0' ) )
1235 {
1236 update_post_meta( $post_id, '_floor_area_to', $property->floor_area_from );
1237 update_post_meta( $post_id, '_floor_area_to_sqft', $property->floor_area_from_sqft);
1238 }
1239 }
1240 }
1241
1242 /**
1243 * Check if we're saving, then trigger an action based on the post type
1244 *
1245 * @param int $post_id
1246 * @param object $post
1247 */
1248 public static function create_concatenated_indexable_meta( $post_id, $post, $update )
1249 {
1250 // $post_id and $post are required
1251 if ( empty( $post_id ) || empty( $post ) ) {
1252 return;
1253 }
1254
1255 // Dont' save meta boxes for revisions or autosaves
1256 if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
1257 return;
1258 }
1259
1260 if ( $post->post_type !== 'property' && $post->post_type !== 'contact' ) {
1261 return;
1262 }
1263
1264 if ( $post->post_type == 'property' )
1265 {
1266 $property = new PH_Property( $post_id );
1267
1268 // Set field of concatenated address
1269 self::remove_all_duplicated_post_meta( $post_id, '_address_concatenated' );
1270
1271 update_post_meta( $post_id, '_address_concatenated', $property->get_formatted_full_address() );
1272
1273 // Set field of concatenated features
1274 self::remove_all_duplicated_post_meta( $post_id, '_features_concatenated' );
1275
1276 $features_concat_array = $property->get_features();
1277
1278 $features_concat = implode('|', array_filter($features_concat_array));
1279 update_post_meta($post_id, '_features_concatenated', $features_concat);
1280
1281 // Set field of concatenated descriptions information
1282 self::remove_all_duplicated_post_meta( $post_id, '_descriptions_concatenated' );
1283
1284 $descs_concat = $property->get_formatted_description();
1285 update_post_meta($post_id, '_descriptions_concatenated', $descs_concat);
1286 }
1287
1288 if ( $post->post_type == 'contact' )
1289 {
1290 $contact = new PH_Contact( $post_id );
1291
1292 // Set field of concatenated address
1293 self::remove_all_duplicated_post_meta( $post_id, '_address_concatenated' );
1294
1295 update_post_meta( $post_id, '_address_concatenated', $contact->get_formatted_full_address() );
1296 }
1297 }
1298
1299 private static function remove_all_duplicated_post_meta( $post_id, $meta_key )
1300 {
1301 $current_meta_array = get_post_meta( $post_id, $meta_key );
1302 if ( is_array($current_meta_array) && count($current_meta_array) > 1 )
1303 {
1304 delete_post_meta( $post_id, $meta_key );
1305 }
1306 }
1307
1308 public static function update_address_concatenated()
1309 {
1310 $args = array(
1311 'post_type' => 'property',
1312 'fields' => 'ids',
1313 'post_status' => array( 'publish', 'draft'),
1314 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- This maintenance backfill must select records missing the destination metadata; removing the predicate would overwrite existing values.
1315 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Owner search metadata must be refreshed on properties linked through the existing serialized owner IDs; retain the relationship match.
1316 'meta_query' => array(
1317 array(
1318 'key' => '_address_concatenated',
1319 'compare' => 'NOT EXISTS'
1320 )
1321 ),
1322 'nopaging' => true,
1323 'orderby' => 'rand',
1324 // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- Maintenance must cover every language; front-end language filters would omit records from this backfill.
1325 'suppress_filters' => true,
1326 );
1327 $property_query = new WP_Query($args);
1328
1329 if ( $property_query->have_posts() )
1330 {
1331 while ( $property_query->have_posts() )
1332 {
1333 $property_query->the_post();
1334
1335 $property = new PH_Property( get_the_ID() );
1336
1337 // Set field of concatenated address
1338 update_post_meta( get_the_ID(), '_address_concatenated', $property->get_formatted_full_address() );
1339 }
1340 }
1341
1342 wp_reset_postdata();
1343
1344 $args = array(
1345 'post_type' => 'contact',
1346 'fields' => 'ids',
1347 'post_status' => 'publish',
1348 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- This maintenance backfill must select records missing the destination metadata; removing the predicate would overwrite existing values.
1349 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Owner search metadata must be refreshed on properties linked through the existing serialized owner IDs; retain the relationship match.
1350 'meta_query' => array(
1351 array(
1352 'key' => '_address_concatenated',
1353 'compare' => 'NOT EXISTS'
1354 )
1355 ),
1356 'nopaging' => true,
1357 'orderby' => 'rand',
1358 // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- Maintenance must cover every language; front-end language filters would omit records from this backfill.
1359 'suppress_filters' => true,
1360 );
1361 $contact_query = new WP_Query($args);
1362
1363 if ( $contact_query->have_posts() )
1364 {
1365 while ( $contact_query->have_posts() )
1366 {
1367 $contact_query->the_post();
1368
1369 $contact = new PH_Contact( get_the_ID() );
1370
1371 // Set field of concatenated address
1372 update_post_meta( get_the_ID(), '_address_concatenated', $contact->get_formatted_full_address() );
1373 }
1374 }
1375
1376 wp_reset_postdata();
1377 }
1378
1379 /**
1380 * @param int $post_id
1381 * @param object $post
1382 */
1383 public static function store_related_viewings( $post_id, $post, $update )
1384 {
1385 // $post_id and $post are required
1386 if ( empty( $post_id ) || empty( $post ) ) {
1387 return;
1388 }
1389
1390 // Dont' save meta boxes for revisions or autosaves
1391 if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
1392 return;
1393 }
1394
1395 if ( $post->post_type !== 'property' && $post->post_type !== 'contact' && $post->post_type !== 'viewing' ) {
1396 return;
1397 }
1398
1399 if ( get_option('propertyhive_module_disabled_viewings', '') == 'yes' ) {
1400 return;
1401 }
1402
1403 $viewing_ids = array();
1404
1405 switch ( $post->post_type )
1406 {
1407 case "property":
1408 {
1409 // get all viewings for this property
1410 $meta_query = array(
1411 array(
1412 'key' => '_property_id',
1413 'value' => $post_id,
1414 )
1415 );
1416
1417 $args = array(
1418 'fields' => 'ids',
1419 'post_type' => 'viewing',
1420 'nopaging' => true,
1421 'post_status' => 'publish',
1422 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Recompute related-viewing metadata only for viewings linked to the changed property or applicant; retain existing relationship and status semantics.
1423 'meta_query' => $meta_query,
1424 'orderby' => 'none'
1425 );
1426
1427 $viewings_query = new WP_Query( $args );
1428
1429 if ( $viewings_query->have_posts() )
1430 {
1431 while ( $viewings_query->have_posts() )
1432 {
1433 $viewings_query->the_post();
1434
1435 $viewing_ids[] = get_the_ID();
1436 }
1437 }
1438 wp_reset_postdata();
1439
1440 break;
1441 }
1442 case "contact":
1443 {
1444 // get all viewings for this contact
1445 $meta_query = array(
1446 array(
1447 'key' => '_applicant_contact_id',
1448 'value' => $post_id,
1449 )
1450 );
1451
1452 $args = array(
1453 'fields' => 'ids',
1454 'post_type' => 'viewing',
1455 'nopaging' => true,
1456 'post_status' => 'publish',
1457 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Recompute related-viewing metadata only for viewings linked to the changed property or applicant; retain existing relationship and status semantics.
1458 'meta_query' => $meta_query,
1459 'orderby' => 'none'
1460 );
1461
1462 $viewings_query = new WP_Query( $args );
1463
1464 if ( $viewings_query->have_posts() )
1465 {
1466 while ( $viewings_query->have_posts() )
1467 {
1468 $viewings_query->the_post();
1469
1470 $viewing_ids[] = get_the_ID();
1471 }
1472 }
1473 wp_reset_postdata();
1474
1475 break;
1476 }
1477 case "viewing":
1478 {
1479 $viewing_ids[] = $post_id;
1480
1481 break;
1482 }
1483 }
1484
1485 if ( !empty($viewing_ids) )
1486 {
1487 foreach ( $viewing_ids as $viewing_id )
1488 {
1489 $viewing = new PH_Viewing( $viewing_id );
1490
1491 $related_viewings = $viewing->get_related_viewings();
1492
1493 update_post_meta( $post_id, '_related_viewings', $related_viewings );
1494
1495 if ( !empty($related_viewings['all']) )
1496 {
1497 foreach ( $related_viewings['all'] as $related_viewing_id )
1498 {
1499 $other_viewing = new PH_Viewing( $related_viewing_id );
1500
1501 $other_related_viewings = $other_viewing->get_related_viewings();
1502
1503 update_post_meta( $related_viewing_id, '_related_viewings', $other_related_viewings );
1504 }
1505 }
1506 }
1507 }
1508 }
1509
1510 public static function store_related_viewings_meta_change( $meta_id, $object_id, $meta_key, $meta_value )
1511 {
1512 if ( get_post_type($object_id) == 'viewing' && ( $meta_key == '_status' || $meta_key == '_start_date_time' ) )
1513 {
1514 $viewing = new PH_Viewing( $object_id );
1515
1516 $related_viewings = $viewing->get_related_viewings();
1517
1518 update_post_meta( $object_id, '_related_viewings', $related_viewings );
1519
1520 if ( !empty($related_viewings['all']) )
1521 {
1522 foreach ( $related_viewings['all'] as $related_viewing_id )
1523 {
1524 $other_viewing = new PH_Viewing( $related_viewing_id );
1525
1526 $other_related_viewings = $other_viewing->get_related_viewings();
1527
1528 update_post_meta( $related_viewing_id, '_related_viewings', $other_related_viewings );
1529 }
1530 }
1531 }
1532 }
1533 }
1534
1535 new PH_Post_types();
1536