PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 All 261 releases
← All changes | includes/class-ph-post-types.php +1003 -124 1.4.622.3.0 View file →
@@ -13,23 +13,302 @@
13 13 * @package PropertyHive/Classes
14 14 * @category Class
15 15 * @author PropertyHive
16 16 */
17 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public PH_Post_types class; preserve its existing name for plugin and extension compatibility.
17 18 class PH_Post_types {
18 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 +
19 56 /**
20 57 * Constructor
21 58 */
22 59 public function __construct() {
60 + add_filter( 'map_meta_cap', array( __CLASS__, 'map_record_capabilities' ) );
23 61 add_action( 'init', array( __CLASS__, 'register_taxonomies' ), 5 );
24 62 add_action( 'init', array( __CLASS__, 'register_post_types' ), 5 );
63 + add_action( 'init', array( __CLASS__, 'register_post_statuses' ), 5 );
25 64
26 65 add_action( 'wp_trash_post', array( __CLASS__, 'trash_property_children' ), 5 );
66 + add_action( 'wp_trash_post', array( __CLASS__, 'trash_property_enquiries' ), 5 );
27 67
28 68 add_action( 'before_delete_post', array( __CLASS__, 'delete_property_media' ), 5 );
29 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' ) );
30 89 }
31 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 +
32 311 /**
33 312 * Register PropertyHive taxonomies.
34 313 */
35 314 public static function register_taxonomies() {
@@ -41,176 +320,245 @@
41 320
42 321 //$permalinks = get_option( 'propertyhive_permalinks' );
43 322
44 323 register_taxonomy( 'availability',
45 - 'property',
46 - array(
47 - 'label' => __( 'Availabilities', 'propertyhive' ),
48 - 'hierarchical' => true,
49 - 'show_ui' => false,
50 - 'show_in_nav_menus' => false,
51 - 'query_var' => is_admin(),
52 - 'rewrite' => false,
53 - 'public' => true
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 + )
54 337 )
55 338 );
56 339
57 340 register_taxonomy( 'property_type',
58 - array( 'property', 'appraisal' ),
59 - array(
60 - 'label' => __( 'Property Types', 'propertyhive' ),
61 - 'hierarchical' => true,
62 - 'show_ui' => false,
63 - 'show_in_nav_menus' => false,
64 - 'query_var' => is_admin(),
65 - 'rewrite' => false,
66 - 'public' => true
67 - )
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 + )
68 355 );
69 356
70 357 register_taxonomy( 'commercial_property_type',
71 - array( 'property', 'appraisal' ),
72 - array(
73 - 'label' => __( 'Commercial Property Types', 'propertyhive' ),
74 - 'hierarchical' => true,
75 - 'show_ui' => false,
76 - 'show_in_nav_menus' => false,
77 - 'query_var' => is_admin(),
78 - 'rewrite' => false,
79 - 'public' => true
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 + )
80 371 )
81 372 );
82 373
83 374 register_taxonomy( 'location',
84 - 'property',
85 - array(
86 - 'label' => __( 'Locations', 'propertyhive' ),
87 - 'hierarchical' => true,
88 - 'show_ui' => false,
89 - 'show_in_nav_menus' => false,
90 - 'query_var' => is_admin(),
91 - 'rewrite' => false,
92 - 'public' => true
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 + )
93 388 )
94 389 );
95 390
96 391 register_taxonomy( 'parking',
97 - array( 'property', 'appraisal' ),
98 - array(
99 - 'label' => __( 'Parking', 'propertyhive' ),
100 - 'hierarchical' => true,
101 - 'show_ui' => false,
102 - 'show_in_nav_menus' => false,
103 - 'query_var' => is_admin(),
104 - 'rewrite' => false,
105 - 'public' => true
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 + )
106 405 )
107 406 );
108 407
109 408 register_taxonomy( 'outside_space',
110 - array( 'property', 'appraisal' ),
111 - array(
112 - 'label' => __( 'Outside Spaces', 'propertyhive' ),
113 - 'hierarchical' => true,
114 - 'show_ui' => false,
115 - 'show_in_nav_menus' => false,
116 - 'query_var' => is_admin(),
117 - 'rewrite' => false,
118 - 'public' => true
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 + )
119 422 )
120 423 );
121 424
122 425 register_taxonomy( 'price_qualifier',
123 - 'property',
124 - array(
125 - 'label' => __( 'Price Qualifiers', 'propertyhive' ),
126 - 'hierarchical' => true,
127 - 'show_ui' => false,
128 - 'show_in_nav_menus' => false,
129 - 'query_var' => is_admin(),
130 - 'rewrite' => false,
131 - 'public' => true
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 + )
132 439 )
133 440 );
134 441
135 442 register_taxonomy( 'tenure',
136 - 'property',
137 - array(
138 - 'label' => __( 'Tenures', 'propertyhive' ),
139 - 'hierarchical' => true,
140 - 'show_ui' => false,
141 - 'show_in_nav_menus' => false,
142 - 'query_var' => is_admin(),
143 - 'rewrite' => false,
144 - 'public' => true
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 + )
145 456 )
146 457 );
147 458
148 459 register_taxonomy( 'commercial_tenure',
149 - 'property',
150 - array(
151 - 'label' => __( 'Commercial Tenures', 'propertyhive' ),
152 - 'hierarchical' => true,
153 - 'show_ui' => false,
154 - 'show_in_nav_menus' => false,
155 - 'query_var' => is_admin(),
156 - 'rewrite' => false,
157 - 'public' => true
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 + )
158 473 )
159 474 );
160 475
161 476 register_taxonomy( 'sale_by',
162 - 'property',
163 - array(
164 - 'label' => __( 'Sale By', 'propertyhive' ),
165 - 'hierarchical' => true,
166 - 'show_ui' => false,
167 - 'show_in_nav_menus' => false,
168 - 'query_var' => is_admin(),
169 - 'rewrite' => false,
170 - 'public' => true
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 + )
171 490 )
172 491 );
173 492
174 493 register_taxonomy( 'furnished',
175 - array( 'property', 'appraisal' ),
176 - array(
177 - 'label' => __( 'Furnished', 'propertyhive' ),
178 - 'hierarchical' => true,
179 - 'show_ui' => false,
180 - 'show_in_nav_menus' => false,
181 - 'query_var' => is_admin(),
182 - 'rewrite' => false,
183 - 'public' => true
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 + )
184 507 )
185 508 );
186 509
187 510 register_taxonomy( 'marketing_flag',
188 - 'property',
189 - array(
190 - 'label' => __( 'Marketing Flags', 'propertyhive' ),
191 - 'hierarchical' => true,
192 - 'show_ui' => false,
193 - 'show_in_nav_menus' => false,
194 - 'query_var' => is_admin(),
195 - 'rewrite' => false,
196 - 'public' => true
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 + )
197 524 )
198 525 );
199 526
200 527 register_taxonomy( 'property_feature',
201 - 'property',
202 - array(
203 - 'label' => __( 'Property Features', 'propertyhive' ),
204 - 'hierarchical' => true,
205 - 'show_ui' => false,
206 - 'show_in_nav_menus' => false,
207 - 'query_var' => is_admin(),
208 - 'rewrite' => false,
209 - 'public' => true
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 + )
210 541 )
211 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 + );
212 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.
213 561 do_action( 'do_action_after_register_taxonomies' );
214 562 }
215 563
216 564 /**
@@ -248,8 +596,9 @@
248 596 'description' => __( 'This is where you can add new properties to your site.', 'propertyhive' ),
249 597 'public' => true,
250 598 'show_ui' => true,
251 599 'capability_type' => 'post',
600 + 'capabilities' => self::record_capabilities(),
252 601 'map_meta_cap' => true,
253 602 'publicly_queryable' => true,
254 603 'exclude_from_search' => false,
255 604 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -256,9 +605,9 @@
256 605 //'rewrite' => $product_permalink ? array( 'slug' => untrailingslashit( $product_permalink ), 'with_front' => false, 'feeds' => true ) : false,
257 606 'query_var' => true,
258 607 'supports' => array( 'title', 'excerpt' ),
259 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',
260 - 'show_in_nav_menus' => false,
609 + 'show_in_nav_menus' => true,
261 610 'show_in_menu' => false,
262 611 'show_in_admin_bar' => true,
263 612 'show_in_rest' => true,
264 613 )
@@ -287,8 +636,9 @@
287 636 'description' => __( 'This is where you can add new contacts to your site.', 'propertyhive' ),
288 637 'public' => false,
289 638 'show_ui' => true,
290 639 'capability_type' => 'post',
640 + 'capabilities' => self::record_capabilities(),
291 641 'map_meta_cap' => true,
292 642 'publicly_queryable' => false,
293 643 'exclude_from_search' => true,
294 644 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -321,8 +671,9 @@
321 671 ),
322 672 'public' => true,
323 673 'show_ui' => false,
324 674 'capability_type' => 'post',
675 + 'capabilities' => self::record_capabilities(),
325 676 'map_meta_cap' => true,
326 677 'publicly_queryable' => false,
327 678 'exclude_from_search' => true,
328 679 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -328,9 +679,10 @@
328 679 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
329 680 'query_var' => true,
330 681 'supports' => array( 'title' ),
331 682 'show_in_nav_menus' => false,
332 - 'show_in_menu' => false
683 + 'show_in_menu' => false,
684 + 'show_in_rest' => true,
333 685 )
334 686 )
335 687 );
336 688
@@ -355,8 +707,9 @@
355 707 ),
356 708 'public' => false,
357 709 'show_ui' => true,
358 710 'capability_type' => 'post',
711 + 'capabilities' => self::record_capabilities(),
359 712 'map_meta_cap' => true,
360 713 'publicly_queryable' => false,
361 714 'exclude_from_search' => true,
362 715 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -362,9 +715,10 @@
362 715 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
363 716 'query_var' => true,
364 717 'supports' => array( 'title' ),
365 718 'show_in_nav_menus' => false,
366 - 'show_in_menu' => false
719 + 'show_in_menu' => false,
720 + 'show_in_rest' => true,
367 721 )
368 722 )
369 723 );
370 724
@@ -389,8 +743,9 @@
389 743 ),
390 744 'public' => false,
391 745 'show_ui' => true,
392 746 'capability_type' => 'post',
747 + 'capabilities' => self::record_capabilities(),
393 748 'map_meta_cap' => true,
394 749 'publicly_queryable' => false,
395 750 'exclude_from_search' => true,
396 751 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -423,8 +778,9 @@
423 778 ),
424 779 'public' => false,
425 780 'show_ui' => true,
426 781 'capability_type' => 'post',
782 + 'capabilities' => self::record_capabilities(),
427 783 'map_meta_cap' => true,
428 784 'publicly_queryable' => false,
429 785 'exclude_from_search' => true,
430 786 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -457,8 +813,9 @@
457 813 ),
458 814 'public' => false,
459 815 'show_ui' => true,
460 816 'capability_type' => 'post',
817 + 'capabilities' => self::record_capabilities(),
461 818 'map_meta_cap' => true,
462 819 'publicly_queryable' => false,
463 820 'exclude_from_search' => true,
464 821 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -491,11 +848,46 @@
491 848 ),
492 849 'public' => false,
493 850 'show_ui' => true,
494 851 'capability_type' => 'post',
495 - 'capabilities' => array(
852 + 'capabilities' => array_merge( self::record_capabilities(), array(
496 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' )
497 885 ),
886 + 'public' => false,
887 + 'show_ui' => true,
888 + 'capability_type' => 'post',
889 + 'capabilities' => self::record_capabilities(),
498 890 'map_meta_cap' => true,
499 891 'publicly_queryable' => false,
500 892 'exclude_from_search' => true,
501 893 'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
@@ -505,9 +897,45 @@
505 897 'show_in_menu' => false
506 898 )
507 899 )
508 900 );
509 -
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 + );
510 938 do_action( 'propertyhive_after_register_post_types' );
511 939 }
512 940
513 941 public static function trash_property_children( $post_id )
@@ -518,8 +946,9 @@
518 946 $args = array(
519 947 'post_type' => 'property',
520 948 'nopaging' => true,
521 949 'post_parent' => (int)$post_id,
950 + 'suppress_filters' => TRUE,
522 951 'post_status' => array('publish', 'pending', 'private', 'draft', 'auto-draft', 'future'),
523 952 );
524 953
525 954 $properties_query = new WP_Query($args);
@@ -536,11 +965,48 @@
536 965 wp_reset_postdata();
537 966 }
538 967 }
539 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 +
540 1006 public static function delete_property_media( $post_id )
541 1007 {
542 - if ( get_post_type($post_id) == 'property' )
1008 + if ( get_post_type($post_id) == 'property' && apply_filters( 'propertyhive_remove_media_on_property_delete', TRUE ) === TRUE )
543 1009 {
544 1010 $property = new PH_Property( (int)$post_id );
545 1011
546 1012 $media_ids = $property->get_gallery_attachment_ids();
@@ -566,10 +1032,13 @@
566 1032 // Make sure this media isn't used anywhere else
567 1033 $args = array(
568 1034 'post_type' => 'property',
569 1035 'fields' => 'ids',
1036 + 'suppress_filters' => TRUE,
570 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.
571 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.
572 1041 'meta_query' => array(
573 1042 'relation' => 'OR',
574 1043 array(
575 1044 'key' => '_photos',
@@ -632,8 +1101,9 @@
632 1101 // get all children
633 1102 $args = array(
634 1103 'post_type' => 'property',
635 1104 'nopaging' => true,
1105 + 'suppress_filters' => TRUE,
636 1106 'post_parent' => (int)$post_id,
637 1107 'post_status' => array('publish', 'pending', 'private', 'draft', 'auto-draft', 'future', 'trash'),
638 1108 );
639 1109
@@ -648,8 +1118,417 @@
648 1118 wp_delete_post( get_the_ID(), true );
649 1119 }
650 1120 }
651 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 + }
652 1531 }
653 1532 }
654 1533 }
655 1534