| 1 |
<?php |
| 2 |
/** |
| 3 |
* Standalone single listing template, composed from property sections. |
| 4 |
* |
| 5 |
* Theme-overridable: copy to your theme's mlsimport/single-mlsimport-property.php |
| 6 |
* to change the markup. Each mlsimport_render_property_section() call renders one |
| 7 |
* section (the same function that backs its shortcode/block/Elementor widget) and |
| 8 |
* enqueues only that section's assets. |
| 9 |
* |
| 10 |
* Structure is fixed: a full-width hero (breadcrumbs, gallery, title bar, sticky |
| 11 |
* sub-nav) locked at the top, then a content column beside a sticky sidebar (the |
| 12 |
* agent/booking component). Which sections render in the content column, and in |
| 13 |
* what order, comes from the "Arrange Sections" control (the property_sections |
| 14 |
* setting); the locked hero and the sidebar are not affected by that order. |
| 15 |
* |
| 16 |
* @package Mlsimport |
| 17 |
*/ |
| 18 |
|
| 19 |
if ( ! defined( 'ABSPATH' ) ) { |
| 20 |
exit; |
| 21 |
} |
| 22 |
|
| 23 |
// Open the standalone header (theme-agnostic wrapper). |
| 24 |
mlsimport_get_header(); |
| 25 |
|
| 26 |
// Standard single loop: one mlsimport_property post. |
| 27 |
while ( have_posts() ) : |
| 28 |
the_post(); |
| 29 |
$mli_id = get_the_ID(); |
| 30 |
|
| 31 |
/** |
| 32 |
* Filter the colour palette for the standalone property page. |
| 33 |
* |
| 34 |
* @param string $palette '' (warm default) | 'coastal' | 'teal'. |
| 35 |
* @param int $mli_id Property post ID. |
| 36 |
*/ |
| 37 |
$mli_palette = (string) apply_filters( 'mlsimport_property_palette', '', $mli_id ); |
| 38 |
// Base body classes; a recognised palette modifier is appended below. |
| 39 |
$mli_class = 'mlsimport-single mlsimport-property-single'; |
| 40 |
if ( in_array( $mli_palette, array( 'coastal', 'teal' ), true ) ) { |
| 41 |
$mli_class .= ' mlsimport-palette-' . $mli_palette; |
| 42 |
} |
| 43 |
|
| 44 |
// Render order comes from the "Arrange Sections" control (property_sections); |
| 45 |
// the page renders the Enabled sections in the order set there. Falls back to |
| 46 |
// the prototype default when the setting is empty, and picks up sections |
| 47 |
// registered since the layout was last saved. |
| 48 |
// Active content-column sections, in the saved "Arrange Sections" order (with newly registered sections appended). |
| 49 |
$mli_active = mlsimport_standalone_active_sections(); |
| 50 |
|
| 51 |
/** |
| 52 |
* Filter the ordered section slugs rendered on the standalone single page. |
| 53 |
* |
| 54 |
* @param string[] $slugs Active section slugs, in render order. |
| 55 |
* @param int $mli_id Property post ID. |
| 56 |
*/ |
| 57 |
$mli_active = (array) apply_filters( 'mlsimport_single_property_sections', $mli_active, $mli_id ); |
| 58 |
?> |
| 59 |
<?php |
| 60 |
// Full-width hero, locked at the top (matches the prototype) — not reorderable. |
| 61 |
// Breadcrumbs lead, in the conventional spot right under the site header. |
| 62 |
$mli_locked = array( 'breadcrumbs', 'property_gallery', 'title_bar', 'subnav' ); |
| 63 |
?> |
| 64 |
<?php |
| 65 |
// Extension slot: before the single-property article. |
| 66 |
do_action( 'mlsimport_before_single_property', $mli_id ); ?> |
| 67 |
<article class="<?php echo esc_attr( $mli_class ); ?>"> |
| 68 |
<?php |
| 69 |
// Render the locked hero sections first, in fixed order (breadcrumbs, gallery, title bar, sub-nav). |
| 70 |
foreach ( $mli_locked as $mli_slug ) { |
| 71 |
echo mlsimport_render_property_section( $mli_slug, $mli_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 72 |
} |
| 73 |
?> |
| 74 |
<div class="mlsimport-property-layout"> |
| 75 |
<div class="mlsimport-property-layout__main"> |
| 76 |
<?php |
| 77 |
// Content column — rendered in the "Arrange Sections" order, minus the |
| 78 |
// locked hero sections (so they stay full-width and don't duplicate). |
| 79 |
foreach ( $mli_active as $mli_slug ) { |
| 80 |
// Skip the locked hero sections here — already rendered full-width above. |
| 81 |
if ( in_array( $mli_slug, $mli_locked, true ) ) { |
| 82 |
continue; |
| 83 |
} |
| 84 |
echo mlsimport_render_property_section( $mli_slug, $mli_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 85 |
} |
| 86 |
?> |
| 87 |
</div> |
| 88 |
<div class="mlsimport-property-layout__aside"> |
| 89 |
<?php echo /* sticky sidebar: the booking/agent component */ mlsimport_render_property_section( 'booking', $mli_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> |
| 90 |
</div> |
| 91 |
</div> |
| 92 |
</article> |
| 93 |
<?php |
| 94 |
// Extension slot: after the single-property article. |
| 95 |
do_action( 'mlsimport_after_single_property', $mli_id ); |
| 96 |
endwhile; |
| 97 |
|
| 98 |
// Close the standalone footer. |
| 99 |
mlsimport_get_footer(); |
| 100 |
|