PluginProbe
Property Hive / 2.3.1
Property Hive v2.3.1
2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 All 261 releases
← All changes | includes/ph-template-functions.php +1378 -111 1.4.52.3.1 View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +// phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 +// ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4 +
2 5 /**
3 6 * PropertyHive Template
4 7 *
5 8 * Functions for the templating system.
@@ -17,8 +20,9 @@
17 20 *
18 21 * @param mixed $post
19 22 * @return PH_Property
20 23 */
24 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_setup_property_data; the established callable name is part of the plugin/extension API and must remain stable.
21 25 function ph_setup_property_data( $post ) {
22 26 unset( $GLOBALS['property'] );
23 27
24 28 if ( is_int( $post ) )
@@ -26,8 +30,9 @@
26 30
27 31 if ( empty( $post->post_type ) || ! in_array( $post->post_type, array( 'property' ) ) )
28 32 return;
29 33
34 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Shared frontend template global; Property Hive intentionally publishes the current property object for templates and builder integrations.
30 35 $GLOBALS['property'] = get_property( $post );
31 36
32 37 return $GLOBALS['property'];
33 38 }
@@ -38,8 +43,9 @@
38 43 *
39 44 * @access public
40 45 * @return void
41 46 */
47 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_properties_rss_feed; the established callable name is part of the plugin/extension API and must remain stable.
42 48 function ph_properties_rss_feed() {
43 49 // Property RSS
44 50 if ( is_post_type_archive( 'property' ) || is_singular( 'property' ) ) {
45 51
@@ -44,9 +50,9 @@
44 50 if ( is_post_type_archive( 'property' ) || is_singular( 'property' ) ) {
45 51
46 52 $feed = get_post_type_archive_feed_link( 'property' );
47 53
48 - echo '<link rel="alternate" type="application/rss+xml" title="' . __( 'Latest Properties', 'propertyhive' ) . '" href="' . esc_attr( $feed ) . '" />';
54 + echo '<link rel="alternate" type="application/rss+xml" title="' . esc_attr(__( 'Latest Properties', 'propertyhive' )) . '" href="' . esc_url( $feed ) . '" />';
49 55
50 56 }
51 57 }
52 58
@@ -55,8 +61,9 @@
55 61 *
56 62 * @access public
57 63 * @return void
58 64 */
65 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_generator_tag; the established callable name is part of the plugin/extension API and must remain stable.
59 66 function ph_generator_tag( $gen, $type ) {
60 67 switch ( $type ) {
61 68 case 'html':
62 69 $gen .= "\n" . '<meta name="generator" content="PropertyHive ' . esc_attr( PH_VERSION ) . '">';
@@ -73,9 +80,12 @@
73 80 *
74 81 * @param array $classes
75 82 * @return array
76 83 */
84 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_body_class; the established callable name is part of the plugin/extension API and must remain stable.
77 85 function ph_body_class( $classes ) {
86 + global $wp_query;
87 +
78 88 $classes = (array) $classes;
79 89
80 90 if ( is_propertyhive() ) {
81 91 $classes[] = 'propertyhive';
@@ -81,8 +91,48 @@
81 91 $classes[] = 'propertyhive';
82 92 $classes[] = 'propertyhive-page';
83 93 }
84 94
95 + // If we're on a single property, add body classes for taxonomy fields and department
96 + if ( is_single() )
97 + {
98 + $post = $wp_query->get_queried_object();
99 + if ( $post->post_type === 'property' )
100 + {
101 + // Duplication of the code in post-template-php that is done for search results posts in get_post_class()
102 + // All public taxonomies.
103 + $taxonomies = get_taxonomies( array( 'public' => true ) );
104 + foreach ( (array) $taxonomies as $taxonomy ) {
105 + if ( is_object_in_taxonomy( $post->post_type, $taxonomy ) ) {
106 + foreach ( (array) get_the_terms( $post->ID, $taxonomy ) as $term ) {
107 + if ( empty( $term->slug ) ) {
108 + continue;
109 + }
110 +
111 + $term_class = sanitize_html_class( $term->slug, $term->term_id );
112 + if ( is_numeric( $term_class ) || ! trim( $term_class, '-' ) ) {
113 + $term_class = $term->term_id;
114 + }
115 +
116 + // 'post_tag' uses the 'tag' prefix for backward compatibility.
117 + if ( 'post_tag' == $taxonomy ) {
118 + $classes[] = 'tag-' . $term_class;
119 + } else {
120 + $classes[] = sanitize_html_class( $taxonomy . '-' . $term_class, $taxonomy . '-' . $term->term_id );
121 + }
122 + }
123 + }
124 + }
125 + //
126 +
127 + $property = get_property( $post->ID );
128 + $classes[] = 'department-' . $property->department;
129 + $classes[] = 'on-market-' . ( $property->on_market == 'yes' ? 'yes' : 'no' );
130 + $classes[] = 'featured-' . ( $property->featured == 'yes' ? 'yes' : 'no' );
131 + $classes[] = 'office-' . $property->office_id;
132 + }
133 + }
134 +
85 135 return array_unique( $classes );
86 136 }
87 137
88 138 /**
@@ -93,8 +143,9 @@
93 143 * @param string|array $class
94 144 * @param int $post_id
95 145 * @return array
96 146 */
147 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_property_post_class; the established callable name is part of the plugin/extension API and must remain stable.
97 148 function ph_property_post_class( $classes, $class = '', $post_id = '' ) {
98 149 if ( ! $post_id || get_post_type( $post_id ) !== 'property' )
99 150 return $classes;
100 151
@@ -115,9 +166,13 @@
115 166 if ( ( $key = array_search( 'property', $classes ) ) !== false ) {
116 167 unset( $classes[ $key ] );
117 168 }
118 169 $classes[] = 'property';
170 + $classes[] = 'department-' . $property->department;
171 + $classes[] = 'office-' . $property->office_id;
119 172
173 + $classes = array_values($classes);
174 +
120 175 return $classes;
121 176 }
122 177
123 178 /** Global ****************************************************************/
@@ -159,28 +214,39 @@
159 214 * @return string
160 215 */
161 216 function propertyhive_page_title( $echo = true ) {
162 217
163 - if ( is_search() ) {
164 - $page_title = sprintf( __( 'Search Results: &ldquo;%s&rdquo;', 'propertyhive' ), get_search_query() );
218 + if ( is_search() )
219 + {
220 + $page_title = sprintf(
221 + /* translators: %s: search query */
222 + __( 'Search Results: &ldquo;%s&rdquo;', 'propertyhive' ),
223 + get_search_query()
224 + );
165 225
166 226 if ( get_query_var( 'paged' ) )
167 - $page_title .= sprintf( __( '&nbsp;&ndash; Page %s', 'propertyhive' ), get_query_var( 'paged' ) );
227 + {
228 + $page_title .= sprintf(
229 + /* translators: %s: page number */
230 + __( '&nbsp;&ndash; Page %s', 'propertyhive' ),
231 + get_query_var( 'paged' )
232 + );
233 + }
168 234
169 - } elseif ( is_tax() ) {
170 -
235 + }elseif ( is_tax() )
236 + {
171 237 $page_title = single_term_title( "", false );
172 -
173 - } else {
174 -
238 + }
239 + else
240 + {
175 241 $search_results_page_id = ph_get_page_id( 'search_results' );
176 242 $page_title = get_the_title( $search_results_page_id );
177 -
178 243 }
179 244
180 - $page_title = apply_filters( 'propertyhive_page_title', $page_title );
245 + $page_title = apply_filters( 'propertyhive_page_title', wp_kses_post( $page_title ) );
181 246
182 247 if ( $echo )
248 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- The core title is KSES-filtered before the trusted PHP propertyhive_page_title filter, which intentionally supports formatted titles.
183 249 echo $page_title;
184 250 else
185 251 return $page_title;
186 252 }
@@ -198,8 +264,9 @@
198 264 function propertyhive_property_loop_start( $echo = true ) {
199 265 ob_start();
200 266 ph_get_template( 'search/loop-start.php' );
201 267 if ( $echo )
268 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Captured trusted PHP loop template; the built-in template emits static list markup and theme overrides own their escaping.
202 269 echo ob_get_clean();
203 270 else
204 271 return ob_get_clean();
205 272 }
@@ -218,8 +285,9 @@
218 285
219 286 ph_get_template( 'search/loop-end.php' );
220 287
221 288 if ( $echo )
289 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Captured trusted PHP loop template; the built-in template emits static list markup and theme overrides own their escaping.
222 290 echo ob_get_clean();
223 291 else
224 292 return ob_get_clean();
225 293 }
@@ -234,9 +302,10 @@
234 302 * @subpackage Loop
235 303 * @return void
236 304 */
237 305 function propertyhive_template_loop_property_thumbnail() {
238 - echo propertyhive_get_property_thumbnail();
306 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound, WordPress.Security.EscapeOutput.OutputNotEscaped -- The thumbnail helper escapes image attributes; preserve its trusted PHP override and placeholder HTML hook, and the existing image-size hook name.
307 + echo propertyhive_get_property_thumbnail( apply_filters( 'property_search_results_thumbnail_size', 'medium' ) );
239 308 }
240 309 }
241 310
242 311 if ( ! function_exists( 'propertyhive_get_property_thumbnail' ) ) {
@@ -244,10 +313,10 @@
244 313 /**
245 314 * Get the property thumbnail, or the placeholder if not set.
246 315 *
247 316 * @access public
248 - * @subpackage Loop
249 - * @param string $size (default: 'shop_catalog')
317 + * @subpackage Loop
318 + * @param string $size (default: 'medium')
250 319 * @param int $placeholder_width (default: 0)
251 320 * @param int $placeholder_height (default: 0)
252 321 * @return string
253 322 */
@@ -253,12 +322,12 @@
253 322 */
254 323 function propertyhive_get_property_thumbnail( $size = 'medium', $class = '', $placeholder_width = 0, $placeholder_height = 0 ) {
255 324 global $post, $property;
256 325
257 - $photo_url = $property->get_main_photo_src( $size);
326 + $photo_url = $property->get_main_photo_src( $size );
258 327
259 328 if ($photo_url !== FALSE)
260 - return '<img src="' . $photo_url . '" alt="' . get_the_title($post->ID) . '" class="' . $class . '">';
329 + return '<img src="' . esc_url( $photo_url ) . '" alt="' . esc_attr( get_the_title($post->ID) ) . '" class="' . esc_attr( $class ) . '">';
261 330
262 331 if ( ph_placeholder_img_src() )
263 332 return ph_placeholder_img( $size );
264 333 }
@@ -273,9 +342,14 @@
273 342 * @subpackage Loop
274 343 * @return void
275 344 */
276 345 function propertyhive_template_loop_floor_area() {
277 - ph_get_template( 'search/floor-area.php' );
346 + global $property;
347 +
348 + if ( $property->department == 'commercial' || ph_get_custom_department_based_on( $property->department ) == 'commercial' )
349 + {
350 + ph_get_template( 'search/floor-area.php' );
351 + }
278 352 }
279 353 }
280 354
281 355 if ( ! function_exists( 'propertyhive_template_loop_price' ) ) {
@@ -287,9 +361,46 @@
287 361 * @subpackage Loop
288 362 * @return void
289 363 */
290 364 function propertyhive_template_loop_price() {
291 - ph_get_template( 'search/price.php' );
365 +
366 + global $property;
367 +
368 + $fees = '';
369 + if ( get_option('propertyhive_lettings_fees_display_search_results', '') == 'yes' )
370 + {
371 + if (
372 + $property->department == 'residential-lettings' &&
373 + get_option('propertyhive_lettings_fees', '') != ''
374 + )
375 + {
376 + $fees = nl2br(get_option('propertyhive_lettings_fees', ''));
377 + }
378 + if (
379 + $property->department == 'commercial' &&
380 + $property->to_rent == 'yes' &&
381 + get_option('propertyhive_lettings_fees_commercial', '') != ''
382 + )
383 + {
384 + $fees = nl2br(get_option('propertyhive_lettings_fees_commercial', ''));
385 + }
386 + }
387 +
388 + $price_qualifier = '';
389 + if (
390 + (
391 + $property->department == 'residential-sales' ||
392 + ph_get_custom_department_based_on($property->department) == 'residential-sales' ||
393 + $property->department == 'commercial' ||
394 + ph_get_custom_department_based_on($property->department) == 'commercial'
395 + ) &&
396 + $property->price_qualifier != ''
397 + )
398 + {
399 + $price_qualifier = $property->price_qualifier;
400 + }
401 +
402 + ph_get_template( 'search/price.php', array( 'price_qualifier' => $price_qualifier, 'fees' => $fees ) );
292 403 }
293 404 }
294 405
295 406 if ( ! function_exists( 'propertyhive_template_loop_summary' ) ) {
@@ -342,10 +453,26 @@
342 453 * @access public
343 454 * @subpackage Loop
344 455 * @return void
345 456 */
346 - function propertyhive_result_count() {
347 - ph_get_template( 'search/result-count.php' );
457 + function propertyhive_result_count( $paged = '', $per_page = null, $total = null, $first = null, $last = null ) {
458 + global $wp_query;
459 +
460 + $paged = $paged !== '' ? (int)$paged : max( 1, (int)$wp_query->get( 'paged' ) );
461 + $per_page = $per_page !== null ? (int)$per_page : max( 1, (int)$wp_query->get( 'posts_per_page' ) );
462 + $total = $total !== null ? (int)$total : (int)$wp_query->found_posts;
463 + $first = $first !== null ? (int)$first : ( $per_page * $paged ) - $per_page + 1;
464 + $last = $last !== null ? (int)$last : min( $total, $per_page * $paged );
465 +
466 + $args = array(
467 + 'paged' => $paged,
468 + 'per_page' => $per_page,
469 + 'total' => $total,
470 + 'first' => $first,
471 + 'last' => $last,
472 + );
473 +
474 + ph_get_template( 'search/result-count.php', $args );
348 475 }
349 476 }
350 477
351 478 if ( ! function_exists( 'propertyhive_catalog_ordering' ) ) {
@@ -356,12 +483,23 @@
356 483 * @access public
357 484 * @subpackage Loop
358 485 * @return void
359 486 */
360 - function propertyhive_catalog_ordering() {
361 - $orderby = isset( $_GET['orderby'] ) ? ph_clean( $_GET['orderby'] ) : apply_filters( 'propertyhive_default_catalog_orderby', get_option( 'propertyhive_default_catalog_orderby' ) );
487 + function propertyhive_catalog_ordering( $department = '', $orderby = '' ) {
362 488
363 - ph_get_template( 'search/orderby.php', array( 'orderby' => $orderby ) );
489 + if ( $orderby === '' )
490 + {
491 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public result ordering; no state change.
492 + $orderby = isset( $_GET['orderby'] ) && is_string( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : apply_filters( 'propertyhive_default_search_results_orderby', get_option( 'propertyhive_default_search_results_orderby' ) );
493 + }
494 +
495 + $args = array(
496 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public ordering control; explicit PHP arguments retain precedence.
497 + 'department' => $department !== '' ? $department : ( isset($_REQUEST['department']) && is_string( $_REQUEST['department'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['department'] ) ) : '' ),
498 + 'orderby' => $orderby,
499 + );
500 +
501 + ph_get_template( 'search/orderby.php', $args );
364 502 }
365 503 }
366 504
367 505 if ( ! function_exists( 'propertyhive_pagination' ) ) {
@@ -372,15 +510,57 @@
372 510 * @access public
373 511 * @subpackage Loop
374 512 * @return void
375 513 */
376 - function propertyhive_pagination() {
377 - ph_get_template( 'search/pagination.php' );
514 + function propertyhive_pagination( $max_num_pages = '' ) {
515 +
516 + global $wp_query;
517 +
518 + $args = array(
519 + 'max_num_pages' => $max_num_pages !== '' ? $max_num_pages : $wp_query->max_num_pages,
520 + );
521 +
522 + ph_get_template( 'search/pagination.php', $args );
378 523 }
379 524 }
380 525
381 -/** Single Product ********************************************************/
526 +/** Single Property ********************************************************/
382 527
528 +if ( ! function_exists( 'propertyhive_template_not_on_market' ) ) {
529 +
530 + /**
531 + * Output a warning/message if property is not on the market
532 + *
533 + * @access public
534 + * @subpackage Property
535 + * @return void
536 + */
537 + function propertyhive_template_not_on_market() {
538 + ph_get_template( 'single-property/not-on-market.php' );
539 + }
540 +}
541 +
542 +if ( get_option('propertyhive_off_market_behaviour', '') == 'redirect' )
543 +{
544 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_redirect_off_market_properties; the established callable name is part of the plugin/extension API and must remain stable.
545 + function ph_redirect_off_market_properties()
546 + {
547 + // If we're viewing an off market property, redirect to the search form
548 + if (is_singular('property'))
549 + {
550 + if ( get_post_meta(get_the_ID(), '_on_market', TRUE) === '' )
551 + {
552 + if ( !is_user_logged_in() || !current_user_can('administrator') && !current_user_can('editor') )
553 + {
554 + wp_safe_redirect(get_permalink(ph_get_page_id('search_results')), 301);
555 + exit;
556 + }
557 + }
558 + }
559 + }
560 + add_action( 'template_redirect', 'ph_redirect_off_market_properties', 5 );
561 +}
562 +
383 563 if ( ! function_exists( 'propertyhive_show_property_images' ) ) {
384 564
385 565 /**
386 566 * Output the property image before the single property summary.
@@ -388,10 +568,56 @@
388 568 * @access public
389 569 * @subpackage Property
390 570 * @return void
391 571 */
392 - function propertyhive_show_property_images() {
393 - ph_get_template( 'single-property/property-images.php' );
572 + function propertyhive_show_property_images( $num_images = '' )
573 + {
574 + global $property;
575 +
576 + $images = array();
577 + if ( get_option('propertyhive_images_stored_as', '') == 'urls' )
578 + {
579 + $photo_urls = $property->_photo_urls;
580 + if ( !is_array($photo_urls) ) { $photo_urls = array(); }
581 +
582 + if ( !empty($num_images) )
583 + {
584 + $photo_urls = array_slice($photo_urls, 0, $num_images);
585 + }
586 +
587 + foreach ( $photo_urls as $photo )
588 + {
589 + $images[] = array(
590 + 'title' => isset($photo['title']) ? $photo['title'] : '',
591 + 'url' => isset($photo['url']) ? $photo['url'] : '',
592 + 'image' => '<img src="' . esc_url( isset($photo['url']) ? $photo['url'] : '' ) . '" alt="' . esc_attr( isset($photo['title']) ? $photo['title'] : '' ) . '">',
593 + );
594 + }
595 + }
596 + else
597 + {
598 + $gallery_attachments = $property->get_gallery_attachment_ids();
599 +
600 + if ( !empty($gallery_attachments) )
601 + {
602 + if ( !empty($num_images) )
603 + {
604 + $gallery_attachments = array_slice($gallery_attachments, 0, $num_images);
605 + }
606 +
607 + foreach ($gallery_attachments as $gallery_attachment)
608 + {
609 + $images[] = array(
610 + 'title' => esc_attr( get_the_title( $gallery_attachment ) ),
611 + 'url' => wp_get_attachment_url( $gallery_attachment ),
612 + 'image' => wp_get_attachment_image( $gallery_attachment, apply_filters( 'propertyhive_single_property_image_size', 'original' ) ),
613 + 'attachment_id' => $gallery_attachment,
614 + );
615 + }
616 + }
617 + }
618 +
619 + ph_get_template( 'single-property/property-images.php', array( 'images' => $images ) );
394 620 }
395 621 }
396 622
397 623 if ( ! function_exists( 'propertyhive_show_property_thumbnails' ) ) {
@@ -403,9 +629,46 @@
403 629 * @subpackage Property
404 630 * @return void
405 631 */
406 632 function propertyhive_show_property_thumbnails() {
407 - ph_get_template( 'single-property/property-thumbnails.php' );
633 +
634 + global $property;
635 +
636 + $images = array();
637 + if ( get_option('propertyhive_images_stored_as', '') == 'urls' )
638 + {
639 + $photo_urls = $property->_photo_urls;
640 + if ( !is_array($photo_urls) ) { $photo_urls = array(); }
641 +
642 + foreach ( $photo_urls as $photo )
643 + {
644 + $images[] = array(
645 + 'title' => isset($photo['title']) ? $photo['title'] : '',
646 + 'url' => isset($photo['url']) ? $photo['url'] : '',
647 + 'image' => '<img src="' . esc_url( isset($photo['url']) ? $photo['url'] : '' ) . '" alt="' . esc_attr( isset($photo['title']) ? $photo['title'] : '' ) . '">',
648 + );
649 + }
650 + }
651 + else
652 + {
653 + $gallery_attachments = $property->get_gallery_attachment_ids();
654 +
655 + if ( !empty($gallery_attachments) )
656 + {
657 + foreach ($gallery_attachments as $gallery_attachment)
658 + {
659 + $images[] = array(
660 + 'title' => esc_attr( get_the_title( $gallery_attachment ) ),
661 + 'url' => wp_get_attachment_url( $gallery_attachment ),
662 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Existing public Property Hive extension hook single_property_small_thumbnail_size; changing the established name would detach installed callbacks.
663 + 'image' => wp_get_attachment_image( $gallery_attachment, apply_filters( 'single_property_small_thumbnail_size', 'thumbnail' ) ),
664 + 'attachment_id' => $gallery_attachment,
665 + );
666 + }
667 + }
668 + }
669 +
670 + ph_get_template( 'single-property/property-thumbnails.php', array( 'images' => $images ) );
408 671 }
409 672 }
410 673
411 674 if ( ! function_exists( 'propertyhive_template_single_title' ) ) {
@@ -431,9 +694,14 @@
431 694 * @subpackage Property
432 695 * @return void
433 696 */
434 697 function propertyhive_template_single_floor_area() {
435 - ph_get_template( 'single-property/floor-area.php' );
698 + global $property;
699 +
700 + if ( $property->department == 'commercial' || ph_get_custom_department_based_on( $property->department ) == 'commercial' )
701 + {
702 + ph_get_template( 'single-property/floor-area.php' );
703 + }
436 704 }
437 705 }
438 706
439 707 if ( ! function_exists( 'propertyhive_template_single_price' ) ) {
@@ -445,9 +713,46 @@
445 713 * @subpackage Property
446 714 * @return void
447 715 */
448 716 function propertyhive_template_single_price() {
449 - ph_get_template( 'single-property/price.php' );
717 +
718 + global $property;
719 +
720 + $fees = '';
721 + if ( get_option('propertyhive_lettings_fees_display_single_property', '') == 'yes' )
722 + {
723 + if (
724 + $property->department == 'residential-lettings' &&
725 + get_option('propertyhive_lettings_fees', '') != ''
726 + )
727 + {
728 + $fees = nl2br(get_option('propertyhive_lettings_fees', ''));
729 + }
730 + if (
731 + $property->department == 'commercial' &&
732 + $property->to_rent == 'yes' &&
733 + get_option('propertyhive_lettings_fees_commercial', '') != ''
734 + )
735 + {
736 + $fees = nl2br(get_option('propertyhive_lettings_fees_commercial', ''));
737 + }
738 + }
739 +
740 + $price_qualifier = '';
741 + if (
742 + (
743 + $property->department == 'residential-sales' ||
744 + ph_get_custom_department_based_on($property->department) == 'residential-sales' ||
745 + $property->department == 'commercial' ||
746 + ph_get_custom_department_based_on($property->department) == 'commercial'
747 + ) &&
748 + $property->price_qualifier != ''
749 + )
750 + {
751 + $price_qualifier = $property->price_qualifier;
752 + }
753 +
754 + ph_get_template( 'single-property/price.php', array( 'price_qualifier' => $price_qualifier, 'fees' => $fees ) );
450 755 }
451 756 }
452 757
453 758 if ( ! function_exists( 'propertyhive_template_single_meta' ) ) {
@@ -459,9 +764,180 @@
459 764 * @subpackage Property
460 765 * @return void
461 766 */
462 767 function propertyhive_template_single_meta() {
463 - ph_get_template( 'single-property/meta.php' );
768 +
769 + global $post, $property;
770 +
771 + $meta = array();
772 +
773 + if ( $property->reference_number != '' )
774 + {
775 + $meta['reference-number'] = array(
776 + 'label' => __('Ref', 'propertyhive'),
777 + 'value' => $property->reference_number
778 + );
779 + }
780 +
781 + if ( $property->property_type != '' )
782 + {
783 + $meta['property-type'] = array(
784 + 'label' => __('Type', 'propertyhive'),
785 + 'value' => $property->property_type
786 + );
787 + }
788 +
789 + if ( $property->availability != '' )
790 + {
791 + $meta['availability'] = array(
792 + 'label' => __('Availability', 'propertyhive'),
793 + 'value' => $property->availability
794 + );
795 + }
796 +
797 + if ( $property->department != 'commercial' && ph_get_custom_department_based_on( $property->department ) != 'commercial' )
798 + {
799 + if ( $property->bedrooms > 0 )
800 + {
801 + $meta['bedrooms'] = array(
802 + 'label' => __('Bedrooms', 'propertyhive'),
803 + 'value' => $property->bedrooms
804 + );
805 + }
806 +
807 + if ( $property->bathrooms > 0 )
808 + {
809 + $meta['bathrooms'] = array(
810 + 'label' => __('Bathrooms', 'propertyhive'),
811 + 'value' => $property->bathrooms
812 + );
813 + }
814 +
815 + if ( $property->reception_rooms > 0 )
816 + {
817 + $meta['reception-rooms'] = array(
818 + 'label' => __('Reception Rooms', 'propertyhive'),
819 + 'value' => $property->reception_rooms
820 + );
821 + }
822 +
823 + if ( $property->parking != '' )
824 + {
825 + $meta['parking'] = array(
826 + 'label' => __('Parking', 'propertyhive'),
827 + 'value' => $property->parking
828 + );
829 + }
830 +
831 + if ( $property->outside_space != '' )
832 + {
833 + $meta['outside-space'] = array(
834 + 'label' => __('Outside Space', 'propertyhive'),
835 + 'value' => $property->outside_space
836 + );
837 + }
838 +
839 + if ( $property->council_tax_band != '' )
840 + {
841 + $meta['council-tax-band'] = array(
842 + 'label' => __('Council Tax Band', 'propertyhive'),
843 + 'value' => $property->council_tax_band
844 + );
845 + }
846 + }
847 +
848 + if ( $property->department == 'residential-sales' || ph_get_custom_department_based_on( $property->department ) == 'residential-sales' )
849 + {
850 + if ( $property->tenure != '' )
851 + {
852 + $meta['tenure'] = array(
853 + 'label' => __('Tenure', 'propertyhive'),
854 + 'value' => $property->tenure
855 + );
856 +
857 + if ( in_array( strtolower($property->tenure), apply_filters('propertyhive_leasehold_tenure_names', array( 'leasehold', 'share of freehold' ) ) ) )
858 + {
859 + if ( $property->leasehold_years_remaining != '' )
860 + {
861 + $meta['leasehold-years-remaining'] = array(
862 + 'label' => __('Leasehold Remaining', 'propertyhive'),
863 + 'value' => $property->leasehold_years_remaining . ' ' . __( 'years', 'propertyhive' )
864 + );
865 + }
866 +
867 + if ( $property->ground_rent != '' )
868 + {
869 + $meta['ground-rent'] = array(
870 + 'label' => __('Ground Rent', 'propertyhive'),
871 + 'value' => '&pound;' . ph_display_price_field($property->ground_rent, true)
872 + );
873 + }
874 +
875 + if ( $property->ground_rent_review_years != '' )
876 + {
877 + $meta['ground-rent-review-years'] = array(
878 + 'label' => __('Ground Rent Review Period', 'propertyhive'),
879 + 'value' => $property->ground_rent_review_years . ' ' . __( 'years', 'propertyhive' )
880 + );
881 + }
882 +
883 + if ( $property->service_charge != '' )
884 + {
885 + $meta['service-charge'] = array(
886 + 'label' => __('Service Charge', 'propertyhive'),
887 + 'value' => '&pound;' . ph_display_price_field($property->service_charge, true)
888 + );
889 + }
890 +
891 + if ( $property->service_charge_review_years != '' )
892 + {
893 + $meta['service-charge-review-years'] = array(
894 + 'label' => __('Service Charge Review Period', 'propertyhive'),
895 + 'value' => $property->service_charge_review_years . ' ' . __( 'years', 'propertyhive' )
896 + );
897 + }
898 +
899 + if ( $property->shared_ownership == 'yes' )
900 + {
901 + $meta['shared-ownership'] = array(
902 + 'label' => __('Shared Ownership', 'propertyhive'),
903 + 'value' => $property->shared_ownership . ( $property->shared_ownership_percentage != '' ? ' (' . $property->shared_ownership_percentage . '%)' : '' )
904 + );
905 + }
906 + }
907 + }
908 + }
909 +
910 + if ( $property->department == 'residential-lettings' || ph_get_custom_department_based_on( $property->department ) == 'residential-lettings' )
911 + {
912 + if ( $property->furnished != '' )
913 + {
914 + $meta['furnished'] = array(
915 + 'label' => __('Furnished', 'propertyhive'),
916 + 'value' => $property->furnished
917 + );
918 + }
919 +
920 + if ( $property->deposit > 0 )
921 + {
922 + $meta['deposit'] = array(
923 + 'label' => __('Deposit', 'propertyhive'),
924 + 'value' => $property->get_formatted_deposit()
925 + );
926 + }
927 +
928 + if ( $property->available_date != '' )
929 + {
930 + $meta['available-date'] = array(
931 + 'label' => __('Available', 'propertyhive'),
932 + 'value' => $property->get_available_date()
933 + );
934 + }
935 + }
936 +
937 + $meta = apply_filters( 'propertyhive_single_property_meta', $meta );
938 +
939 + ph_get_template( 'single-property/meta.php', array( 'meta' => $meta ) );
464 940 }
465 941 }
466 942
467 943 if ( ! function_exists( 'propertyhive_template_single_sharing' ) ) {
@@ -477,102 +953,286 @@
477 953 ph_get_template( 'single-property/share.php' );
478 954 }
479 955 }
480 956
481 -if ( ! function_exists( 'propertyhive_template_single_actions' ) ) {
957 +if ( ! function_exists( 'propertyhive_get_template_single_floorplans_action' ) ) {
482 958
483 - /**
484 - * Output the product actions (make enquiry etc)
485 - *
486 - * @access public
487 - * @subpackage Property
488 - * @return void
489 - */
490 - function propertyhive_template_single_actions() {
959 + function propertyhive_get_template_single_floorplans_action() {
491 960
492 961 global $post, $property;
493 962
494 963 $actions = array();
495 964
496 - $floorplan_ids = $property->get_floorplan_attachment_ids();
497 - if ( !empty( $floorplan_ids ) )
965 + if ( get_option('propertyhive_floorplans_stored_as', '') == 'urls' )
498 966 {
499 - $floorplans_urls = array();
500 - foreach ($floorplan_ids as $floorplan_id)
967 + $floorplan_urls = $property->_floorplan_urls;
968 + if ( is_array($floorplan_urls) && !empty( $floorplan_urls ) )
501 969 {
502 - $floorplans_urls[] = wp_get_attachment_url( $floorplan_id );
970 + foreach ($floorplan_urls as $floorplan)
971 + {
972 + $actions[] = array(
973 + 'href' => ( ( isset($floorplan['url']) ) ? $floorplan['url'] : '' ),
974 + 'label' => __( 'Floorplan', 'propertyhive' ),
975 + 'class' => 'action-floorplans',
976 + 'attributes' => array(
977 + 'data-fancybox' => 'floorplans'
978 + )
979 + );
980 + }
503 981 }
504 - $actions[] = array(
505 - 'href' => '',
506 - 'label' => __( 'Floorplans', 'propertyhive' ),
507 - 'class' => 'action-floorplans',
508 - 'attributes' => array(
509 - 'data-floorplan-urls' => implode("|", $floorplans_urls)
510 - )
511 - );
512 982 }
983 + else
984 + {
985 + $floorplan_ids = $property->get_floorplan_attachment_ids();
986 + if ( !empty( $floorplan_ids ) )
987 + {
988 + foreach ($floorplan_ids as $floorplan_id)
989 + {
990 + $label = __( 'Floorplan', 'propertyhive' );
513 991
514 - $brochure_ids = $property->get_brochure_attachment_ids();
515 - if ( !empty( $brochure_ids ) )
992 + $attachment_data = wp_prepare_attachment_for_js( $floorplan_id );
993 + if ( isset( $attachment_data['caption'] ) && $attachment_data['caption'] != '' )
994 + {
995 + $label = $attachment_data['caption'];
996 + }
997 +
998 +
999 + $actions[] = array(
1000 + 'href' => wp_get_attachment_url( $floorplan_id ),
1001 + 'label' => $label,
1002 + 'class' => 'action-floorplans',
1003 + 'attributes' => array(
1004 + 'data-fancybox' => 'floorplans'
1005 + )
1006 + );
1007 + }
1008 + }
1009 + }
1010 +
1011 + $actions = apply_filters( 'propertyhive_single_property_floorplans_actions', $actions );
1012 +
1013 + return $actions;
1014 + }
1015 +
1016 +}
1017 +
1018 +if ( ! function_exists( 'propertyhive_get_template_single_brochures_action' ) ) {
1019 +
1020 + function propertyhive_get_template_single_brochures_action() {
1021 +
1022 + global $post, $property;
1023 +
1024 + $actions = array();
1025 +
1026 + if ( get_option('propertyhive_brochures_stored_as', '') == 'urls' )
516 1027 {
517 - foreach ($brochure_ids as $brochure_id)
1028 + $brochure_urls = $property->_brochure_urls;
1029 + if ( is_array($brochure_urls) && !empty( $brochure_urls ) )
518 1030 {
519 - $actions[] = array(
520 - 'href' => wp_get_attachment_url( $brochure_id ),
521 - 'label' => __( 'View Brochure', 'propertyhive' ),
522 - 'class' => 'action-brochure',
523 - 'attributes' => array(
524 - 'target' => '_blank'
525 - )
526 - );
1031 + foreach ($brochure_urls as $brochure)
1032 + {
1033 + $actions[] = array(
1034 + 'href' => ( ( isset($brochure['url']) ) ? $brochure['url'] : '' ),
1035 + 'label' => __( 'View Brochure', 'propertyhive' ),
1036 + 'class' => 'action-brochure',
1037 + 'attributes' => array(
1038 + 'target' => '_blank'
1039 + )
1040 + );
1041 + }
527 1042 }
528 1043 }
1044 + else
1045 + {
1046 + $brochure_ids = $property->get_brochure_attachment_ids();
1047 + if ( !empty( $brochure_ids ) )
1048 + {
1049 + foreach ($brochure_ids as $brochure_id)
1050 + {
1051 + $actions[] = array(
1052 + 'href' => wp_get_attachment_url( $brochure_id ),
1053 + 'label' => __( 'View Brochure', 'propertyhive' ),
1054 + 'class' => 'action-brochure',
1055 + 'attributes' => array(
1056 + 'target' => '_blank'
1057 + )
1058 + );
1059 + }
1060 + }
1061 + }
529 1062
530 - $epc_ids = $property->get_epc_attachment_ids();
531 - if ( !empty( $epc_ids ) )
1063 + $actions = apply_filters( 'propertyhive_single_property_brochures_actions', $actions );
1064 +
1065 + return $actions;
1066 + }
1067 +
1068 +}
1069 +
1070 +if ( ! function_exists( 'propertyhive_get_template_single_epcs_action' ) ) {
1071 +
1072 + function propertyhive_get_template_single_epcs_action() {
1073 +
1074 + global $post, $property;
1075 +
1076 + $actions = array();
1077 +
1078 + if ( get_option('propertyhive_epcs_stored_as', '') == 'urls' )
532 1079 {
533 - foreach ($epc_ids as $epc_id)
1080 + $epc_urls = $property->_epc_urls;
1081 + if ( is_array($epc_urls) && !empty( $epc_urls ) )
534 1082 {
535 - $actions[] = array(
536 - 'href' => wp_get_attachment_url( $epc_id ),
537 - 'label' => __( 'View EPC', 'propertyhive' ),
538 - 'class' => 'action-epc',
539 - 'attributes' => array(
540 - 'target' => '_blank'
541 - )
542 - );
1083 + foreach ($epc_urls as $epc)
1084 + {
1085 + $actions[] = array(
1086 + 'href' => ( ( isset($epc['url']) ) ? $epc['url'] : '' ),
1087 + 'label' => __( 'View EPC', 'propertyhive' ),
1088 + 'class' => 'action-epc',
1089 + 'attributes' => array(
1090 + 'target' => '_blank'
1091 + )
1092 + );
1093 + }
543 1094 }
544 1095 }
1096 + else
1097 + {
1098 + $epc_ids = $property->get_epc_attachment_ids();
1099 + if ( !empty( $epc_ids ) )
1100 + {
1101 + foreach ($epc_ids as $epc_id)
1102 + {
1103 + $attributes = array('target' => '_blank');
1104 + if ( wp_attachment_is_image($epc_id) )
1105 + {
1106 + $attributes = array('data-fancybox' => 'epcs');
1107 + }
1108 + $actions[] = array(
1109 + 'href' => wp_get_attachment_url( $epc_id ),
1110 + 'label' => __( 'View EPC', 'propertyhive' ),
1111 + 'class' => 'action-epc',
1112 + 'attributes' => $attributes
1113 + );
1114 + }
1115 + }
1116 + }
545 1117
546 - $virtual_tour_urls = $property->get_virtual_tour_urls();
547 - if ( !empty( $virtual_tour_urls ) )
1118 + $actions = apply_filters( 'propertyhive_single_property_epcs_actions', $actions );
1119 +
1120 + return $actions;
1121 + }
1122 +
1123 +}
1124 +
1125 +if ( ! function_exists( 'propertyhive_get_template_single_virtual_tours_action' ) ) {
1126 +
1127 + function propertyhive_get_template_single_virtual_tours_action() {
1128 +
1129 + global $post, $property;
1130 +
1131 + $actions = array();
1132 +
1133 + $virtual_tours = $property->get_virtual_tours();
1134 + if ( !empty( $virtual_tours ) )
548 1135 {
549 - foreach ($virtual_tour_urls as $virtual_tour_url)
1136 + foreach ($virtual_tours as $virtual_tour)
550 1137 {
1138 + $attributes = array('target' => '_blank');
1139 + if ( strpos($virtual_tour['url'], 'yout') !== FALSE || strpos($virtual_tour['url'], 'vimeo') !== FALSE )
1140 + {
1141 + $attributes['data-fancybox'] = '';
1142 + }
1143 +
551 1144 $actions[] = array(
552 - 'href' => $virtual_tour_url,
553 - 'label' => __( 'Virtual Tour', 'propertyhive' ),
1145 + 'href' => $virtual_tour['url'],
1146 + 'label' => $virtual_tour['label'],
554 1147 'class' => 'action-virtual-tour',
555 - 'attributes' => array(
556 - 'target' => '_blank'
557 - )
1148 + 'attributes' => $attributes,
558 1149 );
559 1150 }
560 1151 }
561 1152
562 - /*$actions[] = array(
563 - 'href' => '',
564 - 'label' => __( 'View on Map', 'propertyhive' ),
565 - 'class' => 'action-map'
1153 + $actions = apply_filters( 'propertyhive_single_property_virtual_tours_actions', $actions );
1154 +
1155 + return $actions;
1156 + }
1157 +
1158 +}
1159 +
1160 +if ( ! function_exists( 'propertyhive_get_template_single_material_information_action' ) ) {
1161 +
1162 + function propertyhive_get_template_single_material_information_action() {
1163 +
1164 + global $post, $property;
1165 +
1166 + $actions = array();
1167 +
1168 + $material_information = $property->get_material_information();
1169 +
1170 + // Only show if at least one bit of material information has been completed
1171 + if ( empty($material_information) )
1172 + {
1173 + return $actions;
1174 + }
1175 +
1176 + $actions[] = array(
1177 + 'href' => 'javascript:;',
1178 + 'label' => __( 'Utilities & More', 'propertyhive' ),
1179 + 'class' => 'action-material-information',
1180 + 'attributes' => array(
1181 + 'data-fancybox' => '',
1182 + 'data-src' => '#ph_material_information'
1183 + ),
566 1184 );
567 1185
568 - $actions[] = array(
569 - 'href' => '',
570 - 'label' => __( 'Street View', 'propertyhive' ),
571 - 'class' => 'action-street-view'
572 - );*/
1186 + echo '<div id="ph_material_information" style="display:none;">';
573 1187
1188 + ph_get_template( 'single-property/material-information.php' );
1189 +
1190 + echo '</div>';
1191 +
1192 + $actions = apply_filters( 'propertyhive_single_property_material_information_actions', $actions );
1193 +
1194 + return $actions;
1195 + }
1196 +
1197 +}
1198 +
1199 +if ( ! function_exists( 'propertyhive_get_template_single_actions' ) ) {
1200 +
1201 + function propertyhive_get_template_single_actions() {
1202 +
1203 + global $post, $property;
1204 +
1205 + $actions = array_merge(
1206 + propertyhive_get_template_single_floorplans_action(),
1207 + propertyhive_get_template_single_brochures_action(),
1208 + propertyhive_get_template_single_epcs_action(),
1209 + propertyhive_get_template_single_virtual_tours_action(),
1210 + propertyhive_get_template_single_material_information_action(),
1211 + );
1212 +
1213 +
574 1214 $actions = apply_filters( 'propertyhive_single_property_actions', $actions );
1215 +
1216 + return $actions;
1217 + }
1218 +
1219 +}
1220 +
1221 +if ( ! function_exists( 'propertyhive_template_single_actions' ) ) {
1222 +
1223 + /**
1224 + * Output the product actions (make enquiry etc)
1225 + *
1226 + * @access public
1227 + * @subpackage Property
1228 + * @return void
1229 + */
1230 + function propertyhive_template_single_actions() {
1231 +
1232 + global $post, $property;
1233 +
1234 + $actions = propertyhive_get_template_single_actions();
575 1235
576 1236 ph_get_template( 'single-property/actions.php', array( 'actions' => $actions ) );
577 1237 }
578 1238 }
@@ -661,8 +1321,10 @@
661 1321 if ( in_array('applicant', $contact_types) )
662 1322 {
663 1323 $applicant_profiles = $contact->applicant_profiles;
664 1324
1325 + $buy_rent = array();
1326 +
665 1327 if ( $applicant_profiles && $applicant_profiles > 0 )
666 1328 {
667 1329 for ( $i = 0; $i < $applicant_profiles; ++$i )
668 1330 {
@@ -669,12 +1331,23 @@
669 1331 $pages['requirements'] = array(
670 1332 'name' => __( 'Requirements', 'propertyhive' ),
671 1333 );
672 1334
673 - break; // At the moment we'll only allow them to manage the first set of requirements
1335 + $applicant_profile = $contact->{'applicant_profile_' . $i};
1336 +
1337 + if ( isset($applicant_profile['department']) && $applicant_profile['department'] == 'residential-sales' )
1338 + {
1339 + $buy_rent[] = 'To Buy';
1340 + }
1341 + if ( isset($applicant_profile['department']) && $applicant_profile['department'] == 'residential-lettings' )
1342 + {
1343 + $buy_rent[] = 'To Rent';
1344 + }
674 1345 }
675 1346 }
676 1347
1348 + $buy_rent = array_unique($buy_rent);
1349 +
677 1350 // Check viewing module is active and that viewings exist, either future or past
678 1351 if ( get_option('propertyhive_module_disabled_viewings', '') != 'yes' )
679 1352 {
680 1353 $args = array(
@@ -681,8 +1354,9 @@
681 1354 'post_type' => 'viewing',
682 1355 'posts_per_page' => 1,
683 1356 'post_status' => 'publish',
684 1357 'fields' => 'ids',
1358 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Account page setup tests whether a contact has viewings; the first query and the owner-viewings query both use posts_per_page=1. The result is presence/count only and fields=ids; the owner query’s IN list is derived from the current owner’s properties.
685 1359 'meta_query' => array(
686 1360 array(
687 1361 'key' => '_applicant_contact_id',
688 1362 'value' => $contact->id
@@ -693,25 +1367,117 @@
693 1367
694 1368 if ( $viewings_query->have_posts() )
695 1369 {
696 1370 $pages['applicant_viewings'] = array(
697 - 'name' => __( 'Viewings', 'propertyhive' ),
1371 + 'name' => __( 'Viewings', 'propertyhive' ) . ( !empty($buy_rent) ? ' ' . implode('/', $buy_rent) : '' ),
698 1372 );
699 1373 }
700 1374 wp_reset_postdata();
701 1375 }
702 1376 }
1377 + if ( in_array('owner', $contact_types) )
1378 + {
1379 + $sell_let = array();
1380 +
1381 + // Get properties belonging to this owner
1382 + $args = array(
1383 + 'post_type' => 'property',
1384 + 'nopaging' => true,
1385 + 'post_status' => 'publish',
1386 + 'fields' => 'ids',
1387 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Account query is scoped to the current contact or their owned property IDs; these legacy relationship/date meta keys are required to return the complete account history using WordPress query APIs.
1388 + 'meta_query' => array(
1389 + 'relation' => 'OR',
1390 + array(
1391 + 'key' => '_owner_contact_id',
1392 + 'value' => ':' . $contact->id . ';',
1393 + 'compare' => 'LIKE'
1394 + ),
1395 + array(
1396 + 'key' => '_owner_contact_id',
1397 + 'value' => ':"' . $contact->id . '";',
1398 + 'compare' => 'LIKE'
1399 + )
1400 + )
1401 + );
1402 +
1403 + $properties_query = new WP_Query( $args );
1404 +
1405 + $property_ids = array();
1406 +
1407 + if ( $properties_query->have_posts() )
1408 + {
1409 + while ( $properties_query->have_posts() )
1410 + {
1411 + $properties_query->the_post();
1412 +
1413 + $property_ids[] = get_the_ID();
1414 +
1415 + if ( get_post_meta( get_the_ID(), '_department', TRUE ) == 'residential-sales' )
1416 + {
1417 + $sell_let[] = 'To Sell';
1418 + }
1419 + if ( get_post_meta( get_the_ID(), '_department', TRUE ) == 'residential-lettings' )
1420 + {
1421 + $sell_let[] = 'To Let';
1422 + }
1423 + }
1424 +
1425 + $pages['owner_properties'] = array(
1426 + 'name' => __( 'Properties', 'propertyhive' ),
1427 + );
1428 + }
1429 + wp_reset_postdata();
1430 +
1431 + $sell_let = array_unique($sell_let);
1432 +
1433 + // Check viewing module is active and that viewings exist, either future or past
1434 + if ( get_option('propertyhive_module_disabled_viewings', '') != 'yes' )
1435 + {
1436 + $past_viewings = array();
1437 + $upcoming_viewings = array();
1438 +
1439 + if ( !empty($property_ids) )
1440 + {
1441 + $args = array(
1442 + 'post_type' => 'viewing',
1443 + 'posts_per_page' => 1,
1444 + 'post_status' => 'publish',
1445 + 'fields' => 'ids',
1446 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Account page setup tests whether a contact has viewings; the first query and the owner-viewings query both use posts_per_page=1. The result is presence/count only and fields=ids; the owner query’s IN list is derived from the current owner’s properties.
1447 + 'meta_query' => array(
1448 + array(
1449 + 'key' => '_property_id',
1450 + 'value' => $property_ids,
1451 + 'compare' => 'IN'
1452 + )
1453 + )
1454 + );
1455 + $viewings_query = new WP_Query( $args );
1456 +
1457 + if ( $viewings_query->have_posts() )
1458 + {
1459 + $pages['owner_viewings'] = array(
1460 + 'name' => __( 'Viewings', 'propertyhive' ) . ( !empty($sell_let) ? ' ' . implode('/', $sell_let) : '' ),
1461 + );
1462 + }
1463 + wp_reset_postdata();
1464 + }
1465 + }
1466 + }
703 1467 }
704 1468
705 - // In future we'll add things like 'My properties', viewings if landlord/vendor, documents etc
1469 + $pages = apply_filters( 'propertyhive_my_account_pages', $pages );
706 1470
1471 + /*$pages['delete'] = array(
1472 + 'name' => __( 'Delete Account', 'propertyhive' )
1473 + );*/
1474 +
707 1475 $pages['logout'] = array(
708 1476 'name' => __( 'Logout', 'propertyhive' ),
709 - 'href' => home_url() . '?logout=1' // Logout URL
1477 + 'href' => wp_nonce_url( add_query_arg( 'logout', '1', home_url( '/' ) ), 'log-out' ) // Logout URL
710 1478 );
711 -
712 - $pages = apply_filters( 'propertyhive_my_account_pages', $pages );
713 -
1479 +
714 1480 return $pages;
715 1481 }
716 1482
717 1483 if ( ! function_exists( 'propertyhive_my_account_navigation' ) ) {
@@ -798,19 +1564,36 @@
798 1564 * @return void
799 1565 */
800 1566 function propertyhive_my_account_requirements() {
801 1567
802 - $form_controls = ph_get_applicant_requirements_form_fields();
803 -
804 - $form_controls = apply_filters( 'propertyhive_applicant_requirements_form_fields', $form_controls );
1568 + $user_id = get_current_user_id();
805 1569
806 - // Remove office as this is only required on initial sign up (at the moment anyway)
807 - if ( isset($form_controls['office_id']) )
1570 + $contact = new PH_Contact( '', $user_id );
1571 +
1572 + if ( is_array($contact->contact_types) && in_array('applicant', $contact->contact_types) )
808 1573 {
809 - unset($form_controls['office_id']);
1574 + $applicant_profiles = $contact->applicant_profiles;
1575 +
1576 + for ( $i = 0; $i < $applicant_profiles; ++$i )
1577 + {
1578 + $form_controls = ph_get_applicant_requirements_form_fields($contact->{'applicant_profile_' . $i});
1579 +
1580 + $form_controls = apply_filters( 'propertyhive_applicant_requirements_form_fields', $form_controls, $contact->{'applicant_profile_' . $i} );
1581 +
1582 + $form_controls['profile_id'] = array(
1583 + 'type' => 'hidden',
1584 + 'value' => $i,
1585 + );
1586 +
1587 + // Remove office as this is only required on initial sign up (at the moment anyway)
1588 + if ( isset($form_controls['office_id']) )
1589 + {
1590 + unset($form_controls['office_id']);
1591 + }
1592 +
1593 + ph_get_template( 'account/requirements.php', array( 'form_controls' => $form_controls ) );
1594 + }
810 1595 }
811 -
812 - ph_get_template( 'account/requirements.php', array( 'form_controls' => $form_controls ) );
813 1596 }
814 1597 }
815 1598
816 1599 if ( ! function_exists( 'propertyhive_my_account_applicant_viewings' ) ) {
@@ -837,9 +1620,11 @@
837 1620 'fields' => 'ids',
838 1621 'orderby' => 'meta_value',
839 1622 'order' => 'DESC',
840 1623 'post_status' => 'publish',
1624 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Account query is scoped to the current contact or their owned property IDs; these legacy relationship/date meta keys are required to return the complete account history using WordPress query APIs.
841 1625 'meta_key' => '_start_date_time',
1626 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Account query is scoped to the current contact or their owned property IDs; these legacy relationship/date meta keys are required to return the complete account history using WordPress query APIs.
842 1627 'meta_query' => array(
843 1628 array(
844 1629 'key' => '_applicant_contact_id',
845 1630 'value' => $contact->id
@@ -850,9 +1635,9 @@
850 1635 // Do past viewings
851 1636 $args2 = $args;
852 1637 $args2['meta_query'][] = array(
853 1638 'key' => '_start_date_time',
854 - 'value' => date("Y-m-d H:i:s"),
1639 + 'value' => gmdate("Y-m-d H:i:s"),
855 1640 'compare' => '<='
856 1641 );
857 1642
858 1643 $viewings_query = new WP_Query( $args2 );
@@ -873,9 +1658,9 @@
873 1658 // Do upcoming viewings
874 1659 $args2 = $args;
875 1660 $args2['meta_query'][] = array(
876 1661 'key' => '_start_date_time',
877 - 'value' => date("Y-m-d H:i:s"),
1662 + 'value' => gmdate("Y-m-d H:i:s"),
878 1663 'compare' => '>='
879 1664 );
880 1665
881 1666 $viewings_query = new WP_Query( $args2 );
@@ -894,5 +1679,487 @@
894 1679 wp_reset_postdata();
895 1680
896 1681 ph_get_template( 'account/applicant-viewings.php', array( 'past_viewings' => $past_viewings, 'upcoming_viewings' => $upcoming_viewings ) );
897 1682 }
898 -}
1683 +}
1684 +
1685 +if ( ! function_exists( 'propertyhive_my_account_owner_properties' ) ) {
1686 +
1687 + /**
1688 + * Output the owner properties section within a users account
1689 + *
1690 + * @access public
1691 + * @return void
1692 + */
1693 + function propertyhive_my_account_owner_properties() {
1694 +
1695 + $user_id = get_current_user_id();
1696 +
1697 + $contact = new PH_Contact( '', $user_id );
1698 +
1699 + // Get properties belonging to this owner
1700 + $args = array(
1701 + 'post_type' => 'property',
1702 + 'nopaging' => true,
1703 + 'post_status' => 'publish',
1704 + 'fields' => 'ids',
1705 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Account query is scoped to the current contact or their owned property IDs; these legacy relationship/date meta keys are required to return the complete account history using WordPress query APIs.
1706 + 'meta_query' => array(
1707 + 'relation' => 'OR',
1708 + array(
1709 + 'key' => '_owner_contact_id',
1710 + 'value' => ':' . $contact->id . ';',
1711 + 'compare' => 'LIKE'
1712 + ),
1713 + array(
1714 + 'key' => '_owner_contact_id',
1715 + 'value' => ':"' . $contact->id . '";',
1716 + 'compare' => 'LIKE'
1717 + )
1718 + )
1719 + );
1720 +
1721 + $properties_query = new WP_Query( $args );
1722 +
1723 + $properties = array();
1724 +
1725 + if ( $properties_query->have_posts() )
1726 + {
1727 + while ( $properties_query->have_posts() )
1728 + {
1729 + $properties_query->the_post();
1730 +
1731 + $properties[] = new PH_Property( get_the_id() );
1732 + }
1733 + }
1734 + wp_reset_postdata();
1735 + ph_get_template( 'account/owner-properties.php', array( 'properties' => $properties ) );
1736 + }
1737 +}
1738 +
1739 +if ( ! function_exists( 'propertyhive_my_account_owner_viewings' ) ) {
1740 +
1741 + /**
1742 + * Output the owner viewings section within a users account
1743 + *
1744 + * @access public
1745 + * @return void
1746 + */
1747 + function propertyhive_my_account_owner_viewings() {
1748 +
1749 + $user_id = get_current_user_id();
1750 +
1751 + $contact = new PH_Contact( '', $user_id );
1752 +
1753 + // Get properties belonging to this owner
1754 + $args = array(
1755 + 'post_type' => 'property',
1756 + 'nopaging' => true,
1757 + 'post_status' => 'publish',
1758 + 'fields' => 'ids',
1759 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Account query is scoped to the current contact or their owned property IDs; these legacy relationship/date meta keys are required to return the complete account history using WordPress query APIs.
1760 + 'meta_query' => array(
1761 + 'relation' => 'OR',
1762 + array(
1763 + 'key' => '_owner_contact_id',
1764 + 'value' => ':' . $contact->id . ';',
1765 + 'compare' => 'LIKE'
1766 + ),
1767 + array(
1768 + 'key' => '_owner_contact_id',
1769 + 'value' => ':"' . $contact->id . '";',
1770 + 'compare' => 'LIKE'
1771 + )
1772 + )
1773 + );
1774 +
1775 + $properties_query = new WP_Query( $args );
1776 +
1777 + $property_ids = array();
1778 +
1779 + if ( $properties_query->have_posts() )
1780 + {
1781 + while ( $properties_query->have_posts() )
1782 + {
1783 + $properties_query->the_post();
1784 +
1785 + $property_ids[] = get_the_id();
1786 + }
1787 + }
1788 + wp_reset_postdata();
1789 +
1790 + $past_viewings = array();
1791 + $upcoming_viewings = array();
1792 +
1793 + if ( !empty($property_ids) )
1794 + {
1795 + $args = array(
1796 + 'post_type' => 'viewing',
1797 + 'nopaging' => true,
1798 + 'post_status' => 'publish',
1799 + 'fields' => 'ids',
1800 + 'orderby' => 'meta_value',
1801 + 'order' => 'DESC',
1802 + 'post_status' => 'publish',
1803 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Account query is scoped to the current contact or their owned property IDs; these legacy relationship/date meta keys are required to return the complete account history using WordPress query APIs.
1804 + 'meta_key' => '_start_date_time',
1805 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Account query is scoped to the current contact or their owned property IDs; these legacy relationship/date meta keys are required to return the complete account history using WordPress query APIs.
1806 + 'meta_query' => array(
1807 + array(
1808 + 'key' => '_property_id',
1809 + 'value' => $property_ids,
1810 + 'compare' => 'IN'
1811 + )
1812 + )
1813 + );
1814 +
1815 + // Do past viewings
1816 + $args2 = $args;
1817 + $args2['meta_query'][] = array(
1818 + 'key' => '_start_date_time',
1819 + 'value' => gmdate("Y-m-d H:i:s"),
1820 + 'compare' => '<='
1821 + );
1822 +
1823 + $viewings_query = new WP_Query( $args2 );
1824 +
1825 + if ( $viewings_query->have_posts() )
1826 + {
1827 + while ( $viewings_query->have_posts() )
1828 + {
1829 + $viewings_query->the_post();
1830 +
1831 + $viewing = new PH_Viewing( get_the_ID() );
1832 +
1833 + $past_viewings[] = $viewing;
1834 + }
1835 + }
1836 + wp_reset_postdata();
1837 +
1838 + // Do upcoming viewings
1839 + $args2 = $args;
1840 + $args2['meta_query'][] = array(
1841 + 'key' => '_start_date_time',
1842 + 'value' => gmdate("Y-m-d H:i:s"),
1843 + 'compare' => '>='
1844 + );
1845 +
1846 + $viewings_query = new WP_Query( $args2 );
1847 +
1848 + if ( $viewings_query->have_posts() )
1849 + {
1850 + while ( $viewings_query->have_posts() )
1851 + {
1852 + $viewings_query->the_post();
1853 +
1854 + $viewing = new PH_Viewing( get_the_ID() );
1855 +
1856 + $upcoming_viewings[] = $viewing;
1857 + }
1858 + }
1859 + wp_reset_postdata();
1860 + }
1861 +
1862 + ph_get_template( 'account/owner-viewings.php', array( 'past_viewings' => $past_viewings, 'upcoming_viewings' => $upcoming_viewings ) );
1863 + }
1864 +}
1865 +
1866 +if ( ! function_exists( 'propertyhive_my_account_delete' ) ) {
1867 +
1868 + /**
1869 + * Output the delete section within a users account
1870 + *
1871 + * @access public
1872 + * @return void
1873 + */
1874 + function propertyhive_my_account_delete() {
1875 +
1876 + ph_get_template( 'account/delete.php' );
1877 + }
1878 +}
1879 +
1880 +add_filter( 'loop_search_results_per_page', 'template_assistant_loop_search_results_per_page', 1 );
1881 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper template_assistant_loop_search_results_per_page; the established callable name is part of the plugin/extension API and must remain stable.
1882 +function template_assistant_loop_search_results_per_page( $cols )
1883 +{
1884 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
1885 +
1886 + if ( isset($current_settings['search_result_columns']) && in_array($current_settings['search_result_columns'], array(3,4)) )
1887 + {
1888 + return 12;
1889 + }
1890 +
1891 + return $cols;
1892 +}
1893 +
1894 +add_filter( 'loop_search_results_columns', 'template_assistant_search_result_columns', 1 );
1895 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper template_assistant_search_result_columns; the established callable name is part of the plugin/extension API and must remain stable.
1896 +function template_assistant_search_result_columns( $cols = 1 )
1897 +{
1898 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
1899 +
1900 + if ( isset($current_settings['search_result_columns']) && in_array($current_settings['search_result_columns'], array(1,2,3,4)) )
1901 + {
1902 + return $current_settings['search_result_columns'];
1903 + }
1904 +
1905 + return 1;
1906 +}
1907 +
1908 +add_filter( 'post_class', 'template_assistant_property_columns_post_class', 20, 3 );
1909 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper template_assistant_property_columns_post_class; the established callable name is part of the plugin/extension API and must remain stable.
1910 +function template_assistant_property_columns_post_class( $classes, $class = '', $post_id = '' )
1911 +{
1912 + if ( ! $post_id || get_post_type( $post_id ) !== 'property' )
1913 + return $classes;
1914 +
1915 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
1916 +
1917 + if ( isset($current_settings['search_result_columns']) && in_array($current_settings['search_result_columns'], array(2,3,4)) )
1918 + {
1919 + $property = get_property( $post_id );
1920 +
1921 + if ( $property )
1922 + {
1923 + $classes[] = 'ph-cols-' . $current_settings['search_result_columns'];
1924 +
1925 + if ( ($key = array_search('clear', $classes)) !== false )
1926 + {
1927 + unset($classes[$key]);
1928 + }
1929 + }
1930 + }
1931 +
1932 + return $classes;
1933 +}
1934 +
1935 +add_action( 'wp_head', 'load_template_assistant_styles' );
1936 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper load_template_assistant_styles; the established callable name is part of the plugin/extension API and must remain stable.
1937 +function load_template_assistant_styles()
1938 +{
1939 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
1940 +
1941 + if (
1942 + is_post_type_archive('property')
1943 + ||
1944 + ( isset($current_settings['search_result_css_all_pages']) && $current_settings['search_result_css_all_pages'] == 'yes' )
1945 + )
1946 + {
1947 + if ( isset( $current_settings['search_result_css'] ) && is_string( $current_settings['search_result_css'] ) )
1948 + {
1949 + // Escape HTML's raw-text terminator without stripping valid stylesheet syntax.
1950 + $css = preg_replace_callback( '~</style~i', static function( $match ) {
1951 + return '<\\/' . substr( $match[0], 2 );
1952 + }, $current_settings['search_result_css'] );
1953 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Privileged custom stylesheet: closing style tags are escaped above; HTML escaping would corrupt valid CSS strings and selectors.
1954 + echo '<style type="text/css">' . $css . '</style>';
1955 + }
1956 + }
1957 +}
1958 +
1959 +add_filter( 'propertyhive_default_search_results_orderby', 'template_assistant_change_default_order' );
1960 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper template_assistant_change_default_order; the established callable name is part of the plugin/extension API and must remain stable.
1961 +function template_assistant_change_default_order( $orderby )
1962 +{
1963 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
1964 +
1965 + if ( isset($current_settings['search_result_default_order']) && $current_settings['search_result_default_order'] != '' )
1966 + {
1967 + return $current_settings['search_result_default_order'];
1968 + }
1969 +
1970 + return $orderby;
1971 +}
1972 +
1973 +add_filter( 'property_search_results_thumbnail_size', 'template_assistant_search_result_image_size_changes' );
1974 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper template_assistant_search_result_image_size_changes; the established callable name is part of the plugin/extension API and must remain stable.
1975 +function template_assistant_search_result_image_size_changes( $image_size )
1976 +{
1977 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
1978 +
1979 + if ( isset($current_settings['search_result_image_size']) && $current_settings['search_result_image_size'] != '' )
1980 + {
1981 + $image_size = $current_settings['search_result_image_size'];
1982 + }
1983 +
1984 + return $image_size;
1985 +}
1986 +
1987 +add_action( 'wp', 'template_assistant_search_result_field_changes' );
1988 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper template_assistant_search_result_field_changes; the established callable name is part of the plugin/extension API and must remain stable.
1989 +function template_assistant_search_result_field_changes()
1990 +{
1991 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
1992 +
1993 + if ( isset($current_settings['search_result_fields']) && is_array($current_settings['search_result_fields']) && !empty($current_settings['search_result_fields']) )
1994 + {
1995 + remove_action( 'propertyhive_after_search_results_loop_item_title', 'propertyhive_template_loop_floor_area', 5 );
1996 + remove_action( 'propertyhive_after_search_results_loop_item_title', 'propertyhive_template_loop_price', 10 );
1997 + remove_action( 'propertyhive_after_search_results_loop_item_title', 'propertyhive_template_loop_summary', 20 );
1998 + remove_action( 'propertyhive_after_search_results_loop_item_title', 'propertyhive_template_loop_actions', 30 );
1999 +
2000 + $priority = 5;
2001 + foreach ( $current_settings['search_result_fields'] as $search_result_field )
2002 + {
2003 + if ( substr($search_result_field, 0, 12) == 'custom_field' )
2004 + {
2005 + // custom field output here
2006 + $custom_field = substr($search_result_field, 12);
2007 +
2008 + add_action( 'propertyhive_after_search_results_loop_item_title', 'propertyhive_template_loop_custom_field', $priority );
2009 +
2010 + $priority += 5;
2011 + continue;
2012 + }
2013 +
2014 + switch ( $search_result_field )
2015 + {
2016 + case "price":
2017 + case "floor_area":
2018 + case "summary":
2019 + case "actions":
2020 + {
2021 + add_action( 'propertyhive_after_search_results_loop_item_title', 'propertyhive_template_loop_' . $search_result_field, $priority );
2022 + break;
2023 + }
2024 + case "availability":
2025 + {
2026 + add_action( 'propertyhive_after_search_results_loop_item_title', function() { global $property; echo '<div class="availability">' . esc_html( $property->availability ) . '</div>'; }, $priority );
2027 + break;
2028 + }
2029 + case "property_type":
2030 + {
2031 + add_action( 'propertyhive_after_search_results_loop_item_title', function() { global $property; echo '<div class="property-type">' . esc_html( $property->property_type ) . '</div>'; }, $priority );
2032 + break;
2033 + }
2034 + case "available_date":
2035 + {
2036 + add_action( 'propertyhive_after_search_results_loop_item_title', function() { global $property; if ( $property->department == 'residential-lettings' && $property->get_available_date() != '' ) { echo '<div class="available-date">' . esc_html( $property->get_available_date() ) . '</div>'; } }, $priority );
2037 + break;
2038 + }
2039 + case "rooms":
2040 + {
2041 + add_action( 'propertyhive_after_search_results_loop_item_title', function() {
2042 + global $property;
2043 +
2044 + if ( ($property->bedrooms != '' && $property->bedrooms != '0') || ($property->bathrooms != '' && $property->bathrooms != '0') || ($property->reception_rooms != '' && $property->reception_rooms != '0') )
2045 + {
2046 + echo '<div class="rooms">';
2047 + if ( $property->bedrooms != '' && $property->bedrooms != '0' ) { echo '<div class="room room-bedrooms"><span class="room-count">' . esc_html( $property->bedrooms ) . '</span> <span class="room-label">Bedroom' . ( $property->bedrooms != 1 ? 's' : '' ) . '</span></div>'; }
2048 + if ( $property->bathrooms != '' && $property->bathrooms != '0' ) { echo '<div class="room room-bathrooms"><span class="room-count">' . esc_html( $property->bathrooms ) . '</span> <span class="room-label">Bathroom' . ( $property->bathrooms != 1 ? 's' : '' ) . '</span></div>'; }
2049 + if ( $property->reception_rooms != '' && $property->reception_rooms != '0' ) { echo '<div class="room room-receptions"><span class="room-count">' . esc_html( $property->reception_rooms ) . '</span> <span class="room-label">Reception' . ( $property->reception_rooms != 1 ? 's' : '' ) . '</span></div>'; }
2050 + echo '</div>';
2051 + }
2052 + }, $priority );
2053 + break;
2054 + }
2055 + default:
2056 + {
2057 + echo 'unknown search result field requested';
2058 + }
2059 + }
2060 +
2061 + $priority += 5;
2062 + }
2063 + }
2064 +}
2065 +
2066 +function propertyhive_template_loop_custom_field()
2067 +{
2068 + global $property;
2069 +
2070 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
2071 +
2072 + foreach ( $current_settings['search_result_fields'] as $search_result_field )
2073 + {
2074 + if ( substr($search_result_field, 0, 12) == 'custom_field' )
2075 + {
2076 + // custom field output here
2077 + $custom_field = substr($search_result_field, 12);
2078 +
2079 + $value = $property->{$custom_field};
2080 + $value = is_array($value) ? implode(", ", $value) : $value;
2081 +
2082 + if ( $value != '' )
2083 + {
2084 + echo '<div class="custom-field custom-field-' . esc_attr( sanitize_title(trim($custom_field, "_")) ) . '">' . wp_kses_post( $value ) . '</div>';
2085 + }
2086 + }
2087 + }
2088 +}
2089 +
2090 +/**
2091 + * Sanitize configurable flag CSS while retaining CSS color functions.
2092 + */
2093 +function propertyhive_get_flag_custom_style( $settings ) {
2094 + $css = ( isset( $settings['flag_position'] ) && is_string( $settings['flag_position'] ) ? $settings['flag_position'] : '' );
2095 + foreach ( array( 'flag_text_color' => 'color', 'flag_bg_color' => 'background' ) as $key => $property_name ) {
2096 + if ( isset( $settings[ $key ] ) && is_string( $settings[ $key ] ) ) {
2097 + $css .= ';' . $property_name . ':' . $settings[ $key ];
2098 + }
2099 + }
2100 + $allow_color = static function( $allowed, $declaration ) {
2101 + // The only parentheses accepted here enclose an RGB/HSL color value.
2102 + return $allowed || 1 === preg_match( '/^(?:color|background):\s*(?:rgba?|hsla?)\([0-9a-z\s.,%+\-\/]*\)(?:\s*!important)?$/i', $declaration );
2103 + };
2104 + add_filter( 'safecss_filter_attr_allow_css', $allow_color, 10, 2 );
2105 + try {
2106 + return safecss_filter_attr( $css );
2107 + } finally {
2108 + remove_filter( 'safecss_filter_attr_allow_css', $allow_color, 10 );
2109 + }
2110 +}
2111 +
2112 +add_action( 'propertyhive_before_search_results_loop_item_title', 'propertyhive_add_flag' );
2113 +function propertyhive_add_flag()
2114 +{
2115 + global $property;
2116 +
2117 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
2118 +
2119 + if ( isset($current_settings['flags_active']) && $current_settings['flags_active'] == '1' )
2120 + {
2121 + $flag = propertyhive_get_flag();
2122 +
2123 + if ( $flag != '' )
2124 + {
2125 + echo '<div class="flag flag-' . esc_attr( sanitize_title($flag) ) . '" style="' . esc_attr( 'position:absolute; text-transform:uppercase; font-size:13px; box-sizing:border-box; padding:7px 20px; ' . propertyhive_get_flag_custom_style( $current_settings ) ) . '">' . esc_html( $flag ) . '</div>';
2126 + }
2127 + }
2128 +}
2129 +
2130 +add_action( 'propertyhive_before_single_property_images', 'propertyhive_add_flag_single', 5 );
2131 +function propertyhive_add_flag_single()
2132 +{
2133 + global $property;
2134 +
2135 + $current_settings = get_option( 'propertyhive_template_assistant', array() );
2136 +
2137 + if ( isset($current_settings['flags_active_single']) && $current_settings['flags_active_single'] == '1' )
2138 + {
2139 + $flag = propertyhive_get_flag();
2140 +
2141 + if ( $flag != '' )
2142 + {
2143 + echo '<div class="flag flag-' . esc_attr( sanitize_title($flag) ) . '" style="' . esc_attr( 'position:absolute; z-index:99; text-transform:uppercase; font-size:13px; box-sizing:border-box; padding:7px 20px; ' . propertyhive_get_flag_custom_style( $current_settings ) ) . '">' . esc_html( $flag ) . '</div>';
2144 + }
2145 + }
2146 +}
2147 +
2148 +function propertyhive_get_flag()
2149 +{
2150 + global $property;
2151 +
2152 + $flag = $property->availability;
2153 +
2154 + if ( $property->marketing_flag != '' )
2155 + {
2156 + $flag = $property->marketing_flag;
2157 + }
2158 +
2159 + $flag = apply_filters( 'propertyhive_template_assistant_flag', $flag );
2160 +
2161 + return $flag;
2162 +}
2163 +
2164 +
2165 +