PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.2.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.2.1
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 / addons / agents_offices.php

agents_offices.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.2.1, at includes/addons/agents_offices.php

325 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Agents & Offices addon.
4 *
5 * On the front end (template_redirect), for MLSPIN-family feeds (mls_id 110/200) this
6 * addon lazily fetches the listing agent's and office's details from the Bridge Data
7 * Output API and caches them into the property's post meta (memberfullname / officename),
8 * marking each property as checked so the API is called at most once per property.
9 * It also registers the 'officename' and 'memberfullname' custom fields with the active
10 * theme (WpResidence 991 / Houzez 992) once.
11 *
12 * @package MLSImport
13 */
14
15
16 /**
17 * Fetch agent data from MLS API and save it as post meta if not already saved or attempted.
18 *
19 * @param string $agent_id The MLS ID of the agent.
20 * @param int $post_id The post ID to save the fetched data.
21 * @param string $token The API authorization token.
22 */
23 function mlsimport_fetch_agent_data($mls_id,$agent_id, $post_id, $token) {
24 // Guard: need both an agent ID and an API token.
25 if (!$agent_id || !$token) {
26 //error_log("MLSImport: Missing agent ID or token for post ID {$post_id}");
27 return;
28 }
29
30 // Check if the agent data is already saved or fetch was attempted
31 $existing_member_name = get_post_meta($post_id, 'memberfullname', true);
32 $fetch_attempted = get_post_meta($post_id, 'mlsimport_agent_checked', true);
33
34 // Skip when we already have data or have already tried once.
35 if (!empty($existing_member_name) || $fetch_attempted) {
36 //error_log("MLSImport: Agent data already fetched for post ID {$post_id}. Skipping API request.");
37 return;
38 }
39
40
41 // Map the numeric MLS ID to its Bridge dataset slug.
42 $mls_array_data = [
43 '110' => 'mlspin',
44 '200' => 'shared_mlspin_41854c5'
45 ];
46
47 // Construct API URL
48 // Bridge OData Member endpoint for this agent.
49 $api_url = "https://api.bridgedataoutput.com/api/v2/OData/{$mls_array_data[$mls_id]}/Member('{$agent_id}')";
50 //error_log("MLSImport: Fetching agent data from API for Agent ID: {$agent_id}, Post ID: {$post_id}");
51
52 // Set request headers with authorization token
53 // Bearer-token auth header for the Bridge API.
54 $args = array(
55 'headers' => array(
56 'Authorization' => 'Bearer ' . $token
57 )
58 );
59
60 // Make GET request to the API
61 $response = wp_remote_get($api_url, $args);
62
63 // Handle request errors
64 // On transport error, mark as checked and stop (avoids retry loops).
65 if (is_wp_error($response)) {
66 //error_log("MLSImport: Error fetching agent data for Agent ID: {$agent_id}. Marking as checked.");
67 update_post_meta($post_id, 'mlsimport_agent_checked', 1);
68 return;
69 }
70
71 // Decode the JSON response
72 $data = json_decode(wp_remote_retrieve_body($response), true);
73 //error_log("MLSImport: API response for Agent ID: {$agent_id}: " . json_encode($data));
74
75 // Cache the agent's full name (lowercased) when present.
76 if (!empty($data['MemberFullName'])) {
77 update_post_meta($post_id, 'memberfullname', strtolower($data['MemberFullName']));
78 //error_log("MLSImport: Saved MemberFullName for Agent ID: {$agent_id}, Post ID: {$post_id}");
79 }
80
81 // Mark that we attempted to fetch the agent data
82 update_post_meta($post_id, 'mlsimport_agent_checked', 1);
83 }
84
85
86
87
88
89
90 /**
91 * Fetch office data from MLS API and save it as post meta if not already saved or attempted.
92 *
93 * @param string $office_id The MLS ID of the office.
94 * @param int $post_id The post ID to save the fetched data.
95 * @param string $token The API authorization token.
96 */
97 function mlsimport_fetch_office_data($mls_id,$office_id, $post_id, $token) {
98 // Guard: need both an office ID and an API token.
99 if (!$office_id || !$token) {
100 //error_log("MLSImport: Missing office ID or token for post ID {$post_id}");
101 return;
102 }
103
104 // Check if the office data is already saved or fetch was attempted
105 $existing_office_name = get_post_meta($post_id, 'officename', true);
106 $fetch_attempted = get_post_meta($post_id, 'mlsimport_office_checked', true);
107
108 // Skip when we already have data or have already tried once.
109 if (!empty($existing_office_name) || $fetch_attempted) {
110 //error_log("MLSImport: Office data already fetched for post ID {$post_id}. Skipping API request.");
111 return;
112 }
113
114
115 // Map the numeric MLS ID to its Bridge dataset slug.
116 $mls_array_data = [
117 '110' => 'mlspin',
118 '200' => 'shared_mlspin_41854c5'
119 ];
120
121 // Construct API URL
122 // Bridge OData Office endpoint for this office.
123 $api_url = "https://api.bridgedataoutput.com/api/v2/OData/{$mls_array_data[$mls_id]}/Office('{$office_id}')";
124
125 //error_log("MLSImport: Fetching office data from API for Office ID: {$office_id}, Post ID: {$post_id}");
126
127 // Set request headers with authorization token
128 // Bearer-token auth header for the Bridge API.
129 $args = array(
130 'headers' => array(
131 'Authorization' => 'Bearer ' . $token
132 )
133 );
134
135 // Make GET request to the API
136 $response = wp_remote_get($api_url, $args);
137
138 // Handle request errors
139 // On transport error, mark as checked and stop (avoids retry loops).
140 if (is_wp_error($response)) {
141 //error_log("MLSImport: Error fetching office data for Office ID: {$office_id}. Marking as checked.");
142 update_post_meta($post_id, 'mlsimport_office_checked', 1);
143 return;
144 }
145
146 // Decode the JSON response
147 $data = json_decode(wp_remote_retrieve_body($response), true);
148 //error_log("MLSImport: API response for Office ID: {$office_id}: " . json_encode($data));
149
150 // Cache the office name (lowercased) when present.
151 if (!empty($data['OfficeName'])) {
152 update_post_meta($post_id, 'officename', strtolower($data['OfficeName']));
153 //error_log("MLSImport: Saved OfficeName for Office ID: {$office_id}, Post ID: {$post_id}");
154 }
155
156 // Mark that we attempted to fetch the office data
157 update_post_meta($post_id, 'mlsimport_office_checked', 1);
158 }
159
160
161
162
163
164 /**
165 * Setup function to fetch and save MLS agent and office data only if not already saved or checked.
166 */
167 function mlsimport_fetch_and_save_mls_data() {
168 // Only act on single property pages (WpResidence or Real Homes post types).
169 if (!is_singular(array('estate_property', 'property'))) {
170 return; // Ensure it only runs on the correct post types
171 }
172
173 global $post;
174
175 // Retrieve plugin options
176 $options = get_option('mlsimport_admin_options');
177
178 // Extract MLS ID and token from options
179 $mls_id = isset($options['mlsimport_mls_name']) ? sanitize_text_field(trim($options['mlsimport_mls_name'])) : '';
180 $mls_token = isset($options['mlsimport_mls_token']) ? sanitize_text_field(trim($options['mlsimport_mls_token'])) : '';
181
182 // Ensure MLS ID is 110 or 200 before proceeding
183 // This addon only supports the MLSPIN datasets and requires a token.
184 if (!in_array($mls_id, ['110', '200']) || !$mls_token) {
185 // error_log("MLSImport: Invalid MLS ID ({$mls_id}) or missing token. Skipping.");
186 return;
187 }
188 // Get agent and office MLS IDs from post meta
189 $agent_id = get_post_meta($post->ID, 'listagentmlsid', true);
190 $office_id = get_post_meta($post->ID, 'listofficemlsid', true);
191
192 //error_log("MLSImport: Checking property Post ID: {$post->ID}, Agent ID: {$agent_id}, Office ID: {$office_id}");
193
194 // Fetch and save agent and office data only if they are not already stored or checked
195 // Lazily fetch the agent record when the property has a list-agent ID.
196 if ($agent_id) {
197 mlsimport_fetch_agent_data($mls_id,$agent_id, $post->ID, $mls_token);
198 }
199
200 // Lazily fetch the office record when the property has a list-office ID.
201 if ($office_id) {
202 mlsimport_fetch_office_data($mls_id,$office_id, $post->ID, $mls_token);
203 }
204 }
205
206 // Run the fetch and save function on template_redirect to ensure data is processed before rendering the page
207 add_action('template_redirect', 'mlsimport_fetch_and_save_mls_data');
208
209
210
211
212
213
214
215
216 /**
217 * Register the Agents & Offices fields for the active supported theme.
218 *
219 * Step by step:
220 * 1. Read the active theme, MLS ID, and mirrored current-connection token from
221 * the plugin options.
222 * 2. Stop unless the current connection is one of the supported MLSPIN feeds
223 * and has a token.
224 * 3. For WpResidence, append the two theme custom fields once and stamp the
225 * successful option update.
226 * 4. For Houzez, append the two additional-feature definitions once to the
227 * current property.
228 *
229 * @return void
230 */
231 function mlsimport_update_custom_fields() {
232 // Read the active theme and current connection from mirrored plugin options.
233 $options = get_option('mlsimport_admin_options');
234 $theme_id = isset($options['mlsimport_theme_used']) ? intval($options['mlsimport_theme_used']) : 0;
235 $mls_id = isset($options['mlsimport_mls_name']) ? sanitize_text_field(trim($options['mlsimport_mls_name'])) : '';
236 // Credential values are trim-only so valid token bytes are never altered.
237 $mls_token = isset($options['mlsimport_mls_token']) ? trim((string) $options['mlsimport_mls_token']) : '';
238
239
240
241 // This addon is available only for the two MLSPIN feeds with credentials.
242 if (!in_array($mls_id, ['110', '200']) || !$mls_token) {
243 return;
244 }
245
246 // Check if update has already run to prevent duplicate updates
247 if (get_option('mlsimport_custom_fields_updated')) {
248 return;
249 }
250
251 // WpResidence (991): register the two fields in the theme's custom-fields list.
252 if ($theme_id === 991) {
253 // Retrieve theme options
254 $theme_options = get_option('wpresidence_admin');
255 $custom_fields = isset($theme_options['wpestate_custom_fields_list']) ? $theme_options['wpestate_custom_fields_list'] : array();
256
257 // Ensure we have an array to work with.
258 if (!is_array($custom_fields)) {
259 $custom_fields = array();
260 }
261
262 // Check if the fields already exist before adding
263 // Add the office-name field once.
264 if (!in_array('officename', $custom_fields['add_field_name'] ?? [])) {
265 $custom_fields['add_field_name'][] = 'officename';
266 $custom_fields['add_field_label'][] = 'Office Name';
267 $custom_fields['add_field_type'][] = 'short text';
268 $custom_fields['add_field_order'][] = 998;
269 }
270
271 // Add the member-name field once.
272 if (!in_array('memberfullname', $custom_fields['add_field_name'] ?? [])) {
273 $custom_fields['add_field_name'][] = 'memberfullname';
274 $custom_fields['add_field_label'][] = 'Member Name';
275 $custom_fields['add_field_type'][] = 'short text';
276 $custom_fields['add_field_order'][] = 999;
277 }
278
279 // Save updated custom fields
280 $theme_options['wpestate_custom_fields_list'] = $custom_fields;
281 // Only flag as done if the option actually saved.
282 if (update_option('wpresidence_admin', $theme_options)) {
283 // Mark as updated to prevent re-running
284 update_option('mlsimport_custom_fields_updated', true);
285 }
286 // Houzez (992): add the fields to the current property's additional_features.
287 } else if ($theme_id === 992) {
288 // Update additional features meta for properties
289 global $post;
290 $property_id = $post->ID;
291 $extra_fields = get_post_meta($property_id, 'additional_features', true);
292
293 // Ensure we have an array to work with.
294 if (!is_array($extra_fields)) {
295 $extra_fields = array();
296 }
297
298 // Check if the fields already exist before adding
299 // Collect existing feature titles to avoid duplicates.
300 $existing_titles = array_column($extra_fields, 'fave_additional_feature_title');
301
302 // Add the office-name feature once.
303 if (!in_array('officename', $existing_titles)) {
304 $extra_fields[] = array(
305 'fave_additional_feature_title' => 'officename',
306 'fave_additional_feature_value' => '',
307 );
308 }
309
310 // Add the member-name feature once.
311 if (!in_array('memberfullname', $existing_titles)) {
312 $extra_fields[] = array(
313 'fave_additional_feature_title' => 'memberfullname',
314 'fave_additional_feature_value' => '',
315 );
316 }
317
318 // Save updated additional features
319 update_post_meta($property_id, 'additional_features', $extra_fields);
320 }
321 }
322
323 // Hook into admin init or another relevant action
324 add_action('admin_init', 'mlsimport_update_custom_fields');
325