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-comments.php +200 -68 1.4.592.3.0 View file →
@@ -36,10 +36,86 @@
36 36 add_action( 'add_post_meta', array( __CLASS__, 'check_on_market_add' ), 10, 3 );
37 37 add_action( 'update_post_meta', array( __CLASS__, 'check_on_market_update' ), 10, 4 );
38 38
39 39 add_action( 'update_post_meta', array( __CLASS__, 'check_price_change' ), 10, 4 );
40 +
41 + add_action( 'set_object_terms', array( __CLASS__, 'check_property_status_update' ), 10, 6 );
40 42 }
41 43
44 + public static function check_property_status_update( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids )
45 + {
46 + if ( $taxonomy == 'availability' )
47 + {
48 + if (
49 + get_post_type($object_id) == 'property' &&
50 + get_post_status($object_id) == 'publish' &&
51 + $tt_ids != $old_tt_ids
52 + )
53 + {
54 + if ( apply_filters( 'propertyhive_add_property_availability_change_note', true ) === true )
55 + {
56 + $all_availability_terms = get_terms( array_merge( wp_parse_args( array( 'hide_empty' => 0 ) ), array( 'taxonomy' => 'availability' ) ) );
57 +
58 + $old_availability_id = '';
59 + $old_availability_name = '';
60 + if ( is_array($old_tt_ids) && !empty($old_tt_ids) )
61 + {
62 + $old_availability_id = (int)$old_tt_ids[0];
63 +
64 + foreach( $all_availability_terms as $term )
65 + {
66 + $tt_id = (int)$term->term_taxonomy_id;
67 + if( $tt_id == $old_availability_id )
68 + {
69 + $old_availability_name = $term->name;
70 + }
71 + }
72 + }
73 +
74 + $new_availability_id = '';
75 + $new_availability_name = '';
76 + if ( is_array($tt_ids) && !empty($tt_ids) )
77 + {
78 + $new_availability_id = (int)$tt_ids[0];
79 +
80 + foreach( $all_availability_terms as $term )
81 + {
82 + $tt_id = (int)$term->term_taxonomy_id;
83 + if( $tt_id == $new_availability_id )
84 + {
85 + $new_availability_name = $term->name;
86 + }
87 + }
88 + }
89 +
90 + $current_user = wp_get_current_user();
91 +
92 + // Add note/comment to property
93 + $comment = array(
94 + 'note_type' => 'action',
95 + 'action' => 'property_availability_change',
96 + 'original_value' => $old_availability_name,
97 + 'new_value' => $new_availability_name
98 + );
99 +
100 + $data = array(
101 + 'comment_post_ID' => (int)$object_id,
102 + 'comment_author' => $current_user->display_name,
103 + 'comment_author_email' => 'propertyhive@noreply.com',
104 + 'comment_author_url' => '',
105 + 'comment_date' => gmdate("Y-m-d H:i:s"),
106 + 'comment_content' => serialize($comment),
107 + 'comment_approved' => 1,
108 + 'comment_type' => 'propertyhive_note',
109 + );
110 + $comment_id = wp_insert_comment( $data );
111 + }
112 +
113 + update_post_meta( $object_id, '_availability_change_date', gmdate("Y-m-d H:i:s") );
114 + }
115 + }
116 + }
117 +
42 118 public static function related_to_or_post_id( $clauses )
43 119 {
44 120 global $wpdb, $post;
45 121
@@ -46,14 +122,28 @@
46 122 if ( strpos($clauses['where'], 'related_to') !== FALSE )
47 123 {
48 124 $clauses['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $clauses['join'] );
49 125
126 + // Remove main post ID constraint as it's handled by OR below
127 + $strpos_post_id = strpos($clauses['where'], 'comment_post_ID');
128 + if ( $strpos_post_id !== FALSE )
129 + {
130 + $strpos_next_and = strpos($clauses['where'], 'AND', $strpos_post_id);
131 + if ( $strpos_next_and !== FALSE )
132 + {
133 + $clauses['where'] = substr_replace($clauses['where'], ' 1=1 ', $strpos_post_id, $strpos_next_and - $strpos_post_id );
134 + }
135 + }
136 +
50 137 // we're searching for related_to so should check where main post is comment_post_ID
51 - $clauses['where'] = str_replace( $wpdb->prefix . 'commentmeta.meta_key', '( ' . $wpdb->prefix . 'commentmeta.meta_key', $clauses['where'] );
138 + $clauses['where'] = str_replace(
139 + $wpdb->prefix . 'commentmeta.meta_key',
140 + '( ' . $wpdb->prefix . 'commentmeta.meta_key',
141 + $clauses['where']
142 + );
52 143 $clauses['where'] .= ' OR comment_post_ID = "' . $post->ID . '" ) ';
53 144 }
54 145
55 -
56 146 return $clauses;
57 147 }
58 148
59 149 public static function insert_note( $post_id, $comment )
@@ -84,8 +174,9 @@
84 174 }
85 175 case "contact": {
86 176 // check contact type, then add to property if owner
87 177 $contact_types = get_post_meta( $post_id, '_contact_types', TRUE );
178 + $contact_types = is_array( $contact_types ) ? $contact_types : ( is_string( $contact_types ) && '' !== $contact_types ? array( $contact_types ) : array() );
88 179 if ( in_array('owner', $contact_types) )
89 180 {
90 181 // this contact is an owner
91 182 // get properties
@@ -92,8 +183,9 @@
92 183 $args = array(
93 184 'post_type' => 'property',
94 185 'nopaging' => true,
95 186 'fields' => 'ids',
187 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Notes must link every owned property; supports both scalar and serialized legacy owner IDs and fetches IDs only.
96 188 'meta_query' => array(
97 189 'relation' => 'OR',
98 190 array(
99 191 'key' => '_owner_contact_id',
@@ -156,9 +248,9 @@
156 248 }
157 249 }
158 250
159 251 // get applicant
160 - $applicant_ids = get_post_meta( $post_id, '_applicant_contact_id', TRUE );
252 + $applicant_ids = get_post_meta( $post_id, '_applicant_contact_id' );
161 253 if ( !empty($applicant_ids) )
162 254 {
163 255 if ( !is_array($applicant_ids) )
164 256 {
@@ -172,8 +264,24 @@
172 264 break;
173 265 }
174 266 }
175 267
268 + if ( isset($comment['note_type']) && $comment['note_type'] == 'note' && isset($comment['note']) && !empty($comment['note']) )
269 + {
270 + // Regular expression pattern to match {{mention-ID|NAME}} or {{mention-ID}}
271 + $pattern = '/\{\{mention-(\d+)(?:\|([^}]*))?\}\}/';
272 +
273 + // Use preg_match_all to find all matches
274 + if ( preg_match_all($pattern, $comment['note'], $matches, PREG_SET_ORDER) )
275 + {
276 + foreach ($matches as $match)
277 + {
278 + $related_to[] = (int)$match[1];
279 + }
280 + }
281 + }
282 +
283 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Existing public Property Hive extension hook property_insert_note_related_to; changing the established name would detach installed callbacks.
176 284 $related_to = apply_filters( 'property_insert_note_related_to', $related_to, $post_id );
177 285
178 286 $related_to = array_filter( $related_to );
179 287
@@ -188,9 +296,9 @@
188 296 'comment_post_ID' => $post_id,
189 297 'comment_author' => $current_user->display_name,
190 298 'comment_author_email' => 'propertyhive@noreply.com',
191 299 'comment_author_url' => '',
192 - 'comment_date' => date("Y-m-d H:i:s"),
300 + 'comment_date' => gmdate("Y-m-d H:i:s"),
193 301 'comment_content' => serialize($comment),
194 302 'comment_approved' => 1,
195 303 'comment_type' => 'propertyhive_note',
196 304 'comment_meta' => array(
@@ -196,9 +304,9 @@
196 304 'comment_meta' => array(
197 305 'related_to' => $new_related_to,
198 306 ),
199 307 );
200 - $comment_id = wp_insert_comment( $data );
308 + $comment_id = wp_insert_comment( wp_slash( $data ) );
201 309
202 310 return $comment_id;
203 311 }
204 312
@@ -209,31 +317,34 @@
209 317 $original_value = get_post_meta( $object_id, $meta_key, TRUE );
210 318
211 319 if ( $original_value != $meta_value )
212 320 {
213 - $current_user = wp_get_current_user();
321 + if ( apply_filters( 'propertyhive_add_property_price_change_note', true ) === true )
322 + {
323 + $current_user = wp_get_current_user();
214 324
215 - // Add note/comment to property
216 - $comment = array(
217 - 'note_type' => 'action',
218 - 'action' => 'property_price_change',
219 - 'original_value' => $original_value,
220 - 'new_value' => $meta_value
221 - );
325 + // Add note/comment to property
326 + $comment = array(
327 + 'note_type' => 'action',
328 + 'action' => 'property_price_change',
329 + 'original_value' => $original_value,
330 + 'new_value' => $meta_value
331 + );
222 332
223 - $data = array(
224 - 'comment_post_ID' => (int)$object_id,
225 - 'comment_author' => $current_user->display_name,
226 - 'comment_author_email' => 'propertyhive@noreply.com',
227 - 'comment_author_url' => '',
228 - 'comment_date' => date("Y-m-d H:i:s"),
229 - 'comment_content' => serialize($comment),
230 - 'comment_approved' => 1,
231 - 'comment_type' => 'propertyhive_note',
232 - );
233 - $comment_id = wp_insert_comment( $data );
333 + $data = array(
334 + 'comment_post_ID' => (int)$object_id,
335 + 'comment_author' => $current_user->display_name,
336 + 'comment_author_email' => 'propertyhive@noreply.com',
337 + 'comment_author_url' => '',
338 + 'comment_date' => gmdate("Y-m-d H:i:s"),
339 + 'comment_content' => serialize($comment),
340 + 'comment_approved' => 1,
341 + 'comment_type' => 'propertyhive_note',
342 + );
343 + $comment_id = wp_insert_comment( $data );
344 + }
234 345
235 - update_post_meta( $object_id, '_price_change_date', date("Y-m-d H:i:s") );
346 + update_post_meta( $object_id, '_price_change_date', gmdate("Y-m-d H:i:s") );
236 347 }
237 348 }
238 349 }
239 350
@@ -242,31 +353,34 @@
242 353 if ( get_post_type($object_id) == 'property' && $meta_key == '_on_market' )
243 354 {
244 355 if ( $meta_value == 'yes' )
245 356 {
246 - $note_action = 'property_on_market';
357 + if ( apply_filters( 'propertyhive_add_property_on_market_change_note', true ) === true )
358 + {
359 + $note_action = 'property_on_market';
247 360
248 - $current_user = wp_get_current_user();
361 + $current_user = wp_get_current_user();
249 362
250 - // Add note/comment to property
251 - $comment = array(
252 - 'note_type' => 'action',
253 - 'action' => $note_action,
254 - );
363 + // Add note/comment to property
364 + $comment = array(
365 + 'note_type' => 'action',
366 + 'action' => $note_action,
367 + );
255 368
256 - $data = array(
257 - 'comment_post_ID' => (int)$object_id,
258 - 'comment_author' => $current_user->display_name,
259 - 'comment_author_email' => 'propertyhive@noreply.com',
260 - 'comment_author_url' => '',
261 - 'comment_date' => date("Y-m-d H:i:s"),
262 - 'comment_content' => serialize($comment),
263 - 'comment_approved' => 1,
264 - 'comment_type' => 'propertyhive_note',
265 - );
266 - $comment_id = wp_insert_comment( $data );
369 + $data = array(
370 + 'comment_post_ID' => (int)$object_id,
371 + 'comment_author' => $current_user->display_name,
372 + 'comment_author_email' => 'propertyhive@noreply.com',
373 + 'comment_author_url' => '',
374 + 'comment_date' => gmdate("Y-m-d H:i:s"),
375 + 'comment_content' => serialize($comment),
376 + 'comment_approved' => 1,
377 + 'comment_type' => 'propertyhive_note',
378 + );
379 + $comment_id = wp_insert_comment( $data );
380 + }
267 381
268 - update_post_meta( $object_id, '_on_market_change_date', date("Y-m-d H:i:s") );
382 + update_post_meta( $object_id, '_on_market_change_date', gmdate("Y-m-d H:i:s") );
269 383 }
270 384 }
271 385 }
272 386
@@ -277,35 +391,38 @@
277 391 $original_value = get_post_meta( $object_id, $meta_key, TRUE );
278 392
279 393 if ( $original_value != $meta_value )
280 394 {
281 - $note_action = 'property_off_market';
282 - if ($meta_value == 'yes')
395 + if ( apply_filters( 'propertyhive_add_property_on_market_change_note', true ) === true )
283 396 {
284 - $note_action = 'property_on_market';
285 - }
397 + $note_action = 'property_off_market';
398 + if ($meta_value == 'yes')
399 + {
400 + $note_action = 'property_on_market';
401 + }
286 402
287 - $current_user = wp_get_current_user();
403 + $current_user = wp_get_current_user();
288 404
289 - // Add note/comment to property
290 - $comment = array(
291 - 'note_type' => 'action',
292 - 'action' => $note_action,
293 - );
405 + // Add note/comment to property
406 + $comment = array(
407 + 'note_type' => 'action',
408 + 'action' => $note_action,
409 + );
294 410
295 - $data = array(
296 - 'comment_post_ID' => (int)$object_id,
297 - 'comment_author' => $current_user->display_name,
298 - 'comment_author_email' => 'propertyhive@noreply.com',
299 - 'comment_author_url' => '',
300 - 'comment_date' => date("Y-m-d H:i:s"),
301 - 'comment_content' => serialize($comment),
302 - 'comment_approved' => 1,
303 - 'comment_type' => 'propertyhive_note',
304 - );
305 - $comment_id = wp_insert_comment( $data );
411 + $data = array(
412 + 'comment_post_ID' => (int)$object_id,
413 + 'comment_author' => $current_user->display_name,
414 + 'comment_author_email' => 'propertyhive@noreply.com',
415 + 'comment_author_url' => '',
416 + 'comment_date' => gmdate("Y-m-d H:i:s"),
417 + 'comment_content' => serialize($comment),
418 + 'comment_approved' => 1,
419 + 'comment_type' => 'propertyhive_note',
420 + );
421 + $comment_id = wp_insert_comment( $data );
422 + }
306 423
307 - update_post_meta( $object_id, '_on_market_change_date', date("Y-m-d H:i:s") );
424 + update_post_meta( $object_id, '_on_market_change_date', gmdate("Y-m-d H:i:s") );
308 425 }
309 426 }
310 427 }
311 428
@@ -318,13 +435,27 @@
318 435 */
319 436 public static function exclude_note_comments( $clauses ) {
320 437 //global $wpdb, $typenow;
321 438
322 - if ( is_admin() && function_exists( 'get_current_screen' ) )
439 + if ( is_admin() && current_user_can( 'manage_propertyhive' ) && function_exists( 'get_current_screen' ) )
323 440 {
324 441 $screen = get_current_screen();
442 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read-only query visibility filter; the authorized CRM action verifies its own nonce before requesting notes.
443 + $post_action = isset( $_POST['action'] ) && is_string( $_POST['action'] ) ? sanitize_text_field( wp_unslash( $_POST['action'] ) ) : '';
444 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only CRM screen context; manage_propertyhive is checked above.
445 + $get_action = isset( $_GET['action'] ) && is_string( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : '';
446 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only CRM screen context; manage_propertyhive is checked above.
447 + $page = isset( $_GET['page'] ) && is_string( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
325 448
326 - if ( isset($screen->id) && in_array( $screen->id, apply_filters( 'propertyhive_post_types_with_notes', array( 'property', 'contact', 'enquiry', 'appraisal', 'viewing', 'offer', 'sale' ) ) ) )
449 + if (
450 + ( isset($screen->id) && in_array( $screen->id, apply_filters( 'propertyhive_post_types_with_notes', array( 'property', 'contact', 'enquiry', 'appraisal', 'viewing', 'offer', 'sale', 'tenancy' ) ) ) )
451 + ||
452 + ( wp_doing_ajax() && in_array( $post_action, array( 'propertyhive_get_notes_grid', 'propertyhive_get_pinned_notes_grid', 'propertyhive_merge_contact_records' ), true ) )
453 + ||
454 + ( wp_doing_ajax() && strpos( $get_action, 'propertyhive_' ) !== FALSE && strpos( $get_action, '_lightbox' ) !== FALSE )
455 + ||
456 + ( substr( $page, 0, 3 ) == 'ph-' )
457 + )
327 458 {
328 459 return $clauses; // Don't hide when viewing Property Hive record
329 460 }
330 461 }
@@ -357,8 +488,9 @@
357 488 if ( 0 === $post_id ) {
358 489 $stats = get_transient( 'ph_count_comments' );
359 490 if ( ! $stats ) {
360 491 $stats = array();
492 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Aggregate excludes private CRM notes; ph_count_comments transient above caches this result and comment mutations invalidate it.
361 493 $count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} WHERE comment_type != 'propertyhive_note' GROUP BY comment_approved", ARRAY_A );
362 494 $total = 0;
363 495 $approved = array( '0' => 'moderated', '1' => 'approved', 'spam' => 'spam', 'trash' => 'trash', 'post-trashed' => 'post-trashed' );
364 496 foreach ( (array) $count as $row ) {