PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.1.2
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.1.2
7.2.1 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 All 36 releases
mlsimport / includes / standalone / page-blocks-agents.php

page-blocks-agents.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.1.2, at includes/standalone/page-blocks-agents.php

223 lines 8.3 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) Agents Directory page block ("Team Directory").
4 *
5 * A grid/directory of ALL agents (the mlsimport_agent CPT), each tile linking to
6 * that agent's page — the "meet the team" surface, distinct from the single Agent
7 * Card and the per-agent listings grid. Offices are not a separate entity in this
8 * plugin (they are the ListOfficeName text on each agent), so an office shows as a
9 * tile subtitle, not as its own tile.
10 *
11 * Like the Category widgets, ONE resolver (mlsimport_agents_directory_items) and
12 * ONE tile renderer (mlsimport_agent_directory_tile) back the block — and, through
13 * the page-block dispatcher, the Shortcode, Gutenberg and Elementor surfaces all
14 * emit the same markup. The featured-first sort carries no WordPress and is
15 * unit-tested in isolation. See page-block-registry.php and docs/adr/0007.
16 *
17 * @package Mlsimport
18 */
19
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit;
22 }
23
24 /**
25 * Order agent view-models featured-first, preserving the incoming order within the
26 * featured and non-featured groups (a stable partition). The resolver feeds this a
27 * name-ordered list, so the result is "featured agents (by name), then the rest (by
28 * name)". Pure: no WordPress.
29 *
30 * @param array<int,array<string,mixed>> $items View-models carrying a 'featured' flag.
31 * @return array<int,array<string,mixed>>
32 */
33 function mlsimport_agents_sort_featured_first( array $items ): array {
34 $featured = array();
35 $rest = array();
36 foreach ( $items as $vm ) {
37 if ( ! empty( $vm['featured'] ) ) {
38 $featured[] = $vm;
39 } else {
40 $rest[] = $vm;
41 }
42 }
43 return array_merge( $featured, $rest );
44 }
45
46 /**
47 * Parse a comma list of agent post ids into positive ints (order preserved,
48 * duplicates and non-positive values dropped). Pure: no WordPress.
49 *
50 * @param mixed $raw Comma string (or already an array).
51 * @return int[]
52 */
53 function mlsimport_agents_parse_ids( $raw ): array {
54 if ( is_array( $raw ) ) {
55 $parts = $raw;
56 } elseif ( is_string( $raw ) ) {
57 $parts = explode( ',', $raw );
58 } else {
59 $parts = array();
60 }
61
62 $ids = array();
63 foreach ( $parts as $part ) {
64 $id = (int) trim( (string) $part );
65 if ( $id > 0 && ! in_array( $id, $ids, true ) ) {
66 $ids[] = $id;
67 }
68 }
69 return $ids;
70 }
71
72 /**
73 * Resolve the block's selection args into an ordered list of agent view-models:
74 * id, name, url (the agent's own page), photo_url, office, title and a featured
75 * flag. An explicit id list keeps that exact given order; otherwise every published
76 * agent is returned featured-first (then by name), optionally capped and/or limited
77 * to featured agents.
78 *
79 * @param array $args { agents, count, featured_only, sort }.
80 * @return array<int,array<string,mixed>>
81 */
82 function mlsimport_agents_directory_items( array $args ): array {
83 if ( ! post_type_exists( 'mlsimport_agent' ) ) {
84 return array();
85 }
86
87 $ids = mlsimport_agents_parse_ids( $args['agents'] ?? '' );
88 $count = isset( $args['count'] ) ? max( 0, (int) $args['count'] ) : 0;
89 $featured_only = ! in_array( (string) ( $args['featured_only'] ?? '' ), array( '', '0', 'no', 'false' ), true );
90 // Order: 'featured' (verified-first, then A–Z — the default), or a plain
91 // alphabetical A–Z / Z–A by name. An explicit id set always keeps its own order.
92 $sort = isset( $args['sort'] ) && '' !== (string) $args['sort'] ? (string) $args['sort'] : 'featured';
93
94 $query = array(
95 'post_type' => 'mlsimport_agent',
96 'post_status' => 'publish',
97 'posts_per_page' => -1,
98 'orderby' => 'title',
99 'order' => 'name_desc' === $sort ? 'DESC' : 'ASC',
100 'fields' => 'ids',
101 'no_found_rows' => true,
102 );
103 if ( ! empty( $ids ) ) {
104 // Explicit set: that exact ordered list.
105 $query['post__in'] = $ids;
106 $query['orderby'] = 'post__in';
107 }
108 if ( $featured_only ) {
109 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- operator-set flag on a small CPT.
110 $query['meta_key'] = 'mlsimport_featured';
111 $query['meta_value'] = '1';
112 }
113
114 $agent_ids = get_posts( $query );
115 if ( empty( $agent_ids ) ) {
116 return array();
117 }
118
119 $out = array();
120 foreach ( $agent_ids as $aid ) {
121 $aid = (int) $aid;
122 $photo = (int) get_post_thumbnail_id( $aid );
123 $out[] = array(
124 'id' => $aid,
125 'name' => (string) get_the_title( $aid ),
126 'url' => (string) get_permalink( $aid ),
127 'photo_url' => $photo > 0 ? (string) wp_get_attachment_image_url( $photo, 'medium' ) : '',
128 'office' => (string) get_post_meta( $aid, 'mlsimport_ListOfficeName', true ),
129 'title' => (string) get_post_meta( $aid, 'mlsimport_JobTitle', true ),
130 'featured' => '1' === (string) get_post_meta( $aid, 'mlsimport_featured', true ),
131 );
132 }
133
134 // Verified-first only for the default order on a non-explicit set; the plain
135 // alphabetical orders keep the query's title order, and an id set keeps its own.
136 if ( empty( $ids ) && 'featured' === $sort ) {
137 $out = mlsimport_agents_sort_featured_first( $out );
138 }
139 if ( $count > 0 ) {
140 $out = array_slice( $out, 0, $count );
141 }
142
143 return $out;
144 }
145
146 /**
147 * Render one agent tile — the single tile markup for the directory. Photo (or a
148 * neutral placeholder), name, optional job title and optional office subtitle; the
149 * whole tile links to the agent's own page. Reuses the .mlsimport-agent-card
150 * vocabulary the agents archive already ships.
151 *
152 * @param array $vm Agent view-model from mlsimport_agents_directory_items().
153 * @param array $flags { show_title, show_office } booleans.
154 * @return string
155 */
156 function mlsimport_agent_directory_tile( array $vm, array $flags ): string {
157 $url = '' !== (string) $vm['url'] ? (string) $vm['url'] : '#';
158 $name = (string) $vm['name'];
159
160 $photo = '' !== (string) $vm['photo_url']
161 ? '<img class="mlsimport-agent-card__img" src="' . esc_url( $vm['photo_url'] ) . '" alt="" loading="lazy" />'
162 : '<span class="mlsimport-agent-card__img mlsimport-agent-card__img--empty" aria-hidden="true"></span>';
163
164 $title = '';
165 if ( ! empty( $flags['show_title'] ) && '' !== (string) $vm['title'] ) {
166 $title = '<span class="mlsimport-agent-card__title">' . esc_html( $vm['title'] ) . '</span>';
167 }
168
169 $office = '';
170 if ( ! empty( $flags['show_office'] ) && '' !== (string) $vm['office'] ) {
171 $office = '<span class="mlsimport-agent-card__office">' . esc_html( $vm['office'] ) . '</span>';
172 }
173
174 return '<a class="mlsimport-agent-card mlsimport-agent-card--dir" href="' . esc_url( $url ) . '">'
175 . '<span class="mlsimport-agent-card__photo">' . $photo . '</span>'
176 . '<span class="mlsimport-agent-card__body">'
177 . '<span class="mlsimport-agent-card__name">' . esc_html( $name ) . '</span>'
178 . $title
179 . $office
180 . '</span>'
181 . '</a>';
182 }
183
184 /**
185 * Agents Directory — every agent (or a hand-picked/featured subset) as a grid of
186 * tiles, each linking to that agent's page. Backs the [mlsimport_agents_directory]
187 * shortcode, the mlsimport/agents-directory block and the Elementor "Team Directory"
188 * widget.
189 *
190 * @param array $args Block args.
191 * @return string
192 */
193 function mlsimport_page_block_agents_directory( array $args ): string {
194 $items = mlsimport_agents_directory_items( $args );
195 if ( empty( $items ) ) {
196 return '';
197 }
198
199 $show_title = ! in_array( (string) ( $args['show_title'] ?? '1' ), array( '', '0', 'no', 'false' ), true );
200 $show_office = ! in_array( (string) ( $args['show_office'] ?? '1' ), array( '', '0', 'no', 'false' ), true );
201 $flags = array( 'show_title' => $show_title, 'show_office' => $show_office );
202
203 $tiles = '';
204 foreach ( $items as $vm ) {
205 $tiles .= mlsimport_agent_directory_tile( $vm, $flags );
206 }
207
208 // Look controls as CSS custom properties the grid CSS reads; columns always set.
209 $per_row = in_array( (string) ( $args['per_row'] ?? '4' ), array( '2', '3', '4', '5', '6' ), true ) ? (string) $args['per_row'] : '4';
210 $style_parts = array( '--mlsimport-agents-cols:' . $per_row );
211 if ( isset( $args['gap'] ) && '' !== (string) $args['gap'] ) {
212 $style_parts[] = '--mlsimport-agents-gap:' . (int) $args['gap'] . 'px';
213 }
214 if ( isset( $args['border_radius'] ) && '' !== (string) $args['border_radius'] ) {
215 $style_parts[] = '--mlsimport-agents-radius:' . (int) $args['border_radius'] . 'px';
216 }
217 $style = ' style="' . esc_attr( implode( ';', $style_parts ) ) . '"';
218
219 return '<div class="mlsimport-page-block mlsimport-page-block--agents-directory mlsimport-agents-directory"' . $style . '>'
220 . $tiles
221 . '</div>';
222 }
223