PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.2
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.2
7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 6.0.5 6.0.7 All 35 releases
mlsimport / includes / standalone / featured-card.php

featured-card.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.2, at includes/standalone/featured-card.php

300 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Standalone (theme_id 990) Featured Property card — six WPResidence-style designs.
4 *
5 * The Featured Property page block renders ONE hand-picked listing in one of six
6 * layouts that mirror WpResidence's featured_property_1..6 templates (image+price
7 * badge, overlay, horizontal split, full-bleed gradient hero, floating content
8 * box, status hero). Unlike the grid card these are single-listing showcases, so
9 * they live here rather than in the shared card templates.
10 *
11 * Data comes from mlsimport_card_view() (address, price, status, beds/baths/size,
12 * office). WpResidence's designs also show an agent face/name; the standalone
13 * importer stores no agent record, so that element is intentionally omitted.
14 *
15 * @package Mlsimport
16 */
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 require_once __DIR__ . '/listing-card.php';
23 require_once __DIR__ . '/property-sections.php';
24
25 /**
26 * Render a single listing as a featured card in one of six designs.
27 *
28 * @param int $id Listing post ID.
29 * @param int $design Design number 1-6 (out-of-range falls back to 1).
30 * @return string Card HTML, or '' when the post is missing/not a listing.
31 */
32 function mlsimport_featured_card( int $id, int $design ): string {
33 // Guard: bail unless the ID resolves to a real standalone listing post.
34 $post = get_post( $id );
35 if ( ! $post || 'mlsimport_property' !== $post->post_type ) {
36 return '';
37 }
38
39 // Fetch the listing's fast-table row (searchable scalars) for this post.
40 global $wpdb;
41 $table = $wpdb->prefix . 'mlsimport_listings';
42 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
43 $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE post_id = %d", $id ) );
44
45 // Build the shared card view model; clamp the design to the 1-6 range.
46 $v = mlsimport_card_view( $post, $row );
47 $design = ( $design >= 1 && $design <= 6 ) ? $design : 1;
48
49 // The raw content/excerpt as a short blurb for the designs that show one.
50 $source = '' !== $post->post_excerpt ? $post->post_excerpt : $post->post_content;
51 $excerpt = wp_trim_words( wp_strip_all_tags( (string) $source ), 26, '' );
52
53 // Linked agent (assigned at import via list_agent_id): the agent post's
54 // featured image is the agent face the image-based designs show.
55 $agent = function_exists( 'mlsimport_property_data' ) ? mlsimport_property_data( $id )['agent'] ?? null : null;
56 $agent_face = ( $agent && ! empty( $agent['photo_id'] ) ) ? array(
57 'name' => (string) $agent['name'],
58 'photo' => (string) wp_get_attachment_image_url( (int) $agent['photo_id'], 'thumbnail' ),
59 'url' => ! empty( $agent['id'] ) ? (string) get_permalink( (int) $agent['id'] ) : '',
60 ) : null;
61
62 // Assemble the card context the design renderers consume (address, price,
63 // specs, blurb, image markup + URL, and the agent face).
64 $c = array(
65 'id' => $v['id'],
66 'permalink' => $v['permalink'],
67 'address' => $v['address'],
68 'price' => $v['price_fmt'],
69 'status' => $v['status'],
70 'specs' => implode( ' · ', $v['specs'] ),
71 'excerpt' => $excerpt,
72 'img' => $v['has_thumb'] ? get_the_post_thumbnail( $id, 'large', array( 'class' => 'mlsimport-featured__img', 'alt' => $v['address'] ) ) : '',
73 'img_url' => $v['has_thumb'] ? (string) get_the_post_thumbnail_url( $id, 'large' ) : '',
74 'agent' => $agent_face,
75 );
76
77 // Hand off to the shared layout tail.
78 return mlsimport_featured_card_render( $c, $design );
79 }
80
81 /**
82 * Lay a built card context out in one of the six designs — the shared tail of
83 * the stored (post) and live (RESO record) featured cards.
84 *
85 * @param array $c Card context (id, permalink, address, price, status,
86 * specs, excerpt, img, img_url, agent).
87 * @param int $design Design number 1-6 (out-of-range falls back to 1).
88 * @return string
89 */
90 function mlsimport_featured_card_render( array $c, int $design ): string {
91 // Clamp the design number into the supported 1-6 range.
92 $design = ( $design >= 1 && $design <= 6 ) ? $design : 1;
93
94 // Dispatch to the matching design renderer; design 1 is the safety fallback.
95 $fn = 'mlsimport_featured_design_' . $design;
96 $html = function_exists( $fn ) ? $fn( $c ) : mlsimport_featured_design_1( $c );
97
98 // Wrap the design's markup in the design-tagged, id-carrying outer element.
99 $class = 'mlsimport-featured mlsimport-featured--design-' . $design;
100 return '<div class="' . esc_attr( $class ) . '" data-id="' . esc_attr( (string) $c['id'] ) . '">' . $html . '</div>';
101 }
102
103 /**
104 * Status + "Featured" tag cluster (shared by the image-based designs).
105 *
106 * @param array $c Card context.
107 * @return string
108 */
109 function mlsimport_featured_tags( array $c ): string {
110 $out = '<div class="mlsimport-featured__tags">';
111 $out .= '<span class="mlsimport-featured__flag">' . esc_html__( 'Featured', 'mlsimport' ) . '</span>';
112 if ( '' !== $c['status'] ) {
113 $out .= '<span class="mlsimport-featured__badge">' . esc_html( $c['status'] ) . '</span>';
114 }
115 $out .= '</div>';
116 return $out;
117 }
118
119 /**
120 * Round agent-face avatar (the linked agent's featured image), linked to the
121 * agent page. Empty when the listing has no agent photo. Used by the image-based
122 * designs that float the face over the photo.
123 *
124 * @param array $c Card context.
125 * @return string
126 */
127 function mlsimport_featured_agent_avatar( array $c ): string {
128 // No agent, or agent without a photo → render nothing.
129 $a = $c['agent'];
130 if ( empty( $a ) || '' === $a['photo'] ) {
131 return '';
132 }
133 // The round face image; wrapped in a link when the agent has a page.
134 $face = '<span class="mlsimport-featured__agent-photo" style="background-image:url(' . esc_url( $a['photo'] ) . ')" role="img" aria-label="' . esc_attr( $a['name'] ) . '"></span>';
135 if ( '' !== $a['url'] ) {
136 return '<a class="mlsimport-featured__agent" href="' . esc_url( $a['url'] ) . '">' . $face . '</a>';
137 }
138 return '<span class="mlsimport-featured__agent">' . $face . '</span>';
139 }
140
141 /**
142 * Agent avatar + name chip (the linked agent's featured image and display name).
143 * Empty when the listing has no agent photo. Used by the designs that show the
144 * agent in the text area rather than over the photo.
145 *
146 * @param array $c Card context.
147 * @return string
148 */
149 function mlsimport_featured_agent_chip( array $c ): string {
150 // No agent, or agent without a photo → render nothing.
151 $a = $c['agent'];
152 if ( empty( $a ) || '' === $a['photo'] ) {
153 return '';
154 }
155 // Face image plus (optionally) the agent name, optionally wrapped in a link.
156 $inner = '<span class="mlsimport-featured__agent-photo" style="background-image:url(' . esc_url( $a['photo'] ) . ')" role="img" aria-label="' . esc_attr( $a['name'] ) . '"></span>';
157 if ( '' !== $a['name'] ) {
158 $inner .= '<span class="mlsimport-featured__agent-name">' . esc_html( $a['name'] ) . '</span>';
159 }
160 if ( '' !== $a['url'] ) {
161 return '<a class="mlsimport-featured__agent mlsimport-featured__agent--chip" href="' . esc_url( $a['url'] ) . '">' . $inner . '</a>';
162 }
163 return '<span class="mlsimport-featured__agent mlsimport-featured__agent--chip">' . $inner . '</span>';
164 }
165
166 /**
167 * Design 1 — image with price badge + tags; title/specs bar overlapping the
168 * image's bottom edge. Mirrors WpResidence featured_property_type1.
169 *
170 * @param array $c Card context.
171 * @return string
172 */
173 function mlsimport_featured_design_1( array $c ): string {
174 $price = '' !== $c['price'] ? '<div class="mlsimport-featured__price">' . esc_html( $c['price'] ) . '</div>' : '';
175 $specs = '' !== $c['specs'] ? '<div class="mlsimport-featured__specs">' . esc_html( $c['specs'] ) . '</div>' : '';
176 return '<div class="mlsimport-featured__media">'
177 . mlsimport_featured_tags( $c )
178 . '<a class="mlsimport-featured__media-link" href="' . esc_url( $c['permalink'] ) . '">' . $c['img'] . '</a>'
179 . $price
180 . '</div>'
181 . '<div class="mlsimport-featured__bar">'
182 . mlsimport_featured_agent_chip( $c )
183 . '<h2 class="mlsimport-featured__title"><a href="' . esc_url( $c['permalink'] ) . '">' . esc_html( $c['address'] ) . '</a></h2>'
184 . $specs
185 . '</div>';
186 }
187
188 /**
189 * Design 2 — image with title + price overlaid at the bottom. Mirrors
190 * featured_property_type2.
191 *
192 * @param array $c Card context.
193 * @return string
194 */
195 function mlsimport_featured_design_2( array $c ): string {
196 $price = '' !== $c['price'] ? '<span class="mlsimport-featured__price">' . esc_html( $c['price'] ) . '</span>' : '';
197 return '<div class="mlsimport-featured__media">'
198 . mlsimport_featured_tags( $c )
199 . '<a class="mlsimport-featured__media-link" href="' . esc_url( $c['permalink'] ) . '">' . $c['img'] . '</a>'
200 . mlsimport_featured_agent_avatar( $c )
201 . '<div class="mlsimport-featured__overlay">'
202 . '<h2 class="mlsimport-featured__title"><a href="' . esc_url( $c['permalink'] ) . '">' . esc_html( $c['address'] ) . '</a></h2>'
203 . $price
204 . '</div>'
205 . '</div>';
206 }
207
208 /**
209 * Design 3 — horizontal split: photo left, title/price/excerpt/specs right.
210 * Mirrors featured_property_type3.
211 *
212 * @param array $c Card context.
213 * @return string
214 */
215 function mlsimport_featured_design_3( array $c ): string {
216 $price = '' !== $c['price'] ? '<div class="mlsimport-featured__price">' . esc_html( $c['price'] ) . '</div>' : '';
217 $excerpt = '' !== $c['excerpt'] ? '<p class="mlsimport-featured__excerpt">' . esc_html( $c['excerpt'] ) . '</p>' : '';
218 $specs = '' !== $c['specs'] ? '<div class="mlsimport-featured__specrow">' . esc_html( $c['specs'] ) . '</div>' : '';
219 return '<div class="mlsimport-featured__media">'
220 . mlsimport_featured_tags( $c )
221 . '<a class="mlsimport-featured__media-link" href="' . esc_url( $c['permalink'] ) . '">' . $c['img'] . '</a>'
222 . mlsimport_featured_agent_avatar( $c )
223 . '</div>'
224 . '<div class="mlsimport-featured__panel">'
225 . '<h2 class="mlsimport-featured__title"><a href="' . esc_url( $c['permalink'] ) . '">' . esc_html( $c['address'] ) . '</a></h2>'
226 . $price
227 . $excerpt
228 . $specs
229 . '</div>';
230 }
231
232 /**
233 * Design 4 — full-bleed image, dark gradient, "Featured Property" label + big
234 * title + "discover more". Mirrors featured_property_type4.
235 *
236 * @param array $c Card context.
237 * @return string
238 */
239 function mlsimport_featured_design_4( array $c ): string {
240 $style = '' !== $c['img_url'] ? ' style="background-image:url(' . esc_url( $c['img_url'] ) . ')"' : '';
241 // A div (not an anchor) hero so the inner title/more/agent links stay valid.
242 // Agent chip first (its own line, left), then the "Discover more" link below it —
243 // otherwise both are inline and the chip floats to the right of the link.
244 return '<div class="mlsimport-featured__hero"' . $style . '>'
245 . '<div class="mlsimport-featured__gradient"></div>'
246 . '<div class="mlsimport-featured__hero-body">'
247 . '<div class="mlsimport-featured__label">' . esc_html__( 'Featured Property', 'mlsimport' ) . '</div>'
248 . '<h2 class="mlsimport-featured__title"><a href="' . esc_url( $c['permalink'] ) . '">' . esc_html( $c['address'] ) . '</a></h2>'
249 . mlsimport_featured_agent_chip( $c )
250 . '<a class="mlsimport-featured__more" href="' . esc_url( $c['permalink'] ) . '">' . esc_html__( 'Discover more', 'mlsimport' ) . ' &rsaquo;</a>'
251 . '</div>'
252 . '</div>';
253 }
254
255 /**
256 * Design 5 — full-bleed image with a floating white content box (price label,
257 * title, specs, excerpt, discover more). Mirrors featured_property_type5.
258 *
259 * @param array $c Card context.
260 * @return string
261 */
262 function mlsimport_featured_design_5( array $c ): string {
263 $style = '' !== $c['img_url'] ? ' style="background-image:url(' . esc_url( $c['img_url'] ) . ')"' : '';
264 $price = '' !== $c['price'] ? '<div class="mlsimport-featured__label">' . esc_html( $c['price'] ) . '</div>' : '';
265 $specs = '' !== $c['specs'] ? '<div class="mlsimport-featured__specrow">' . esc_html( $c['specs'] ) . '</div>' : '';
266 $excerpt = '' !== $c['excerpt'] ? '<p class="mlsimport-featured__excerpt">' . esc_html( $c['excerpt'] ) . '</p>' : '';
267 return '<div class="mlsimport-featured__hero"' . $style . '>'
268 . '<div class="mlsimport-featured__box">'
269 . $price
270 . '<h2 class="mlsimport-featured__title"><a href="' . esc_url( $c['permalink'] ) . '">' . esc_html( $c['address'] ) . '</a></h2>'
271 . $specs
272 . $excerpt
273 . '<a class="mlsimport-featured__more" href="' . esc_url( $c['permalink'] ) . '">' . esc_html__( 'discover more', 'mlsimport' ) . ' &rsaquo;</a>'
274 . '</div>'
275 . '</div>';
276 }
277
278 /**
279 * Design 6 — full-bleed image, gradient, status badge, title + specs + price
280 * over the image. Mirrors featured_property_type6.
281 *
282 * @param array $c Card context.
283 * @return string
284 */
285 function mlsimport_featured_design_6( array $c ): string {
286 $style = '' !== $c['img_url'] ? ' style="background-image:url(' . esc_url( $c['img_url'] ) . ')"' : '';
287 $badge = '' !== $c['status'] ? '<span class="mlsimport-featured__badge">' . esc_html( $c['status'] ) . '</span>' : '';
288 $specs = '' !== $c['specs'] ? '<div class="mlsimport-featured__features">' . esc_html( $c['specs'] ) . '</div>' : '';
289 $price = '' !== $c['price'] ? '<div class="mlsimport-featured__price">' . esc_html( $c['price'] ) . '</div>' : '';
290 return '<a class="mlsimport-featured__hero" href="' . esc_url( $c['permalink'] ) . '"' . $style . '>'
291 . '<div class="mlsimport-featured__gradient"></div>'
292 . $badge
293 . '<div class="mlsimport-featured__hero-body">'
294 . '<h2 class="mlsimport-featured__title">' . esc_html( $c['address'] ) . '</h2>'
295 . $specs
296 . $price
297 . '</div>'
298 . '</a>';
299 }
300