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 / addons / agents_offices.php

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

317 lines 12.1 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 * Updates custom fields based on the theme ID.
218 *
219 * If the theme ID is 991, it modifies the `wpresidence_admin` options by adding new custom fields.
220 * If the theme ID is 992, it updates the `additional_features` meta field for properties.
221 * The function runs only once by checking `mlsimport_custom_fields_updated`.
222 */
223 function mlsimport_update_custom_fields() {
224 // Read the active theme and MLS from plugin options.
225 $options = get_option('mlsimport_admin_options');
226 $theme_id = isset($options['mlsimport_theme_used']) ? intval($options['mlsimport_theme_used']) : 0;
227 $mls_id = isset($options['mlsimport_mls_name']) ? sanitize_text_field(trim($options['mlsimport_mls_name'])) : '';
228
229
230
231 // Ensure MLS ID is 110 or 200 before proceeding
232 // NOTE: $mls_token is not defined in this function's scope (see SPOTTED notes).
233 if (!in_array($mls_id, ['110', '200']) || !$mls_token) {
234 // error_log("MLSImport: Invalid MLS ID ({$mls_id}) or missing token. Skipping.");
235 return;
236 }
237
238 // Check if update has already run to prevent duplicate updates
239 if (get_option('mlsimport_custom_fields_updated')) {
240 return;
241 }
242
243 // WpResidence (991): register the two fields in the theme's custom-fields list.
244 if ($theme_id === 991) {
245 // Retrieve theme options
246 $theme_options = get_option('wpresidence_admin');
247 $custom_fields = isset($theme_options['wpestate_custom_fields_list']) ? $theme_options['wpestate_custom_fields_list'] : array();
248
249 // Ensure we have an array to work with.
250 if (!is_array($custom_fields)) {
251 $custom_fields = array();
252 }
253
254 // Check if the fields already exist before adding
255 // Add the office-name field once.
256 if (!in_array('officename', $custom_fields['add_field_name'] ?? [])) {
257 $custom_fields['add_field_name'][] = 'officename';
258 $custom_fields['add_field_label'][] = 'Office Name';
259 $custom_fields['add_field_type'][] = 'short text';
260 $custom_fields['add_field_order'][] = 998;
261 }
262
263 // Add the member-name field once.
264 if (!in_array('memberfullname', $custom_fields['add_field_name'] ?? [])) {
265 $custom_fields['add_field_name'][] = 'memberfullname';
266 $custom_fields['add_field_label'][] = 'Member Name';
267 $custom_fields['add_field_type'][] = 'short text';
268 $custom_fields['add_field_order'][] = 999;
269 }
270
271 // Save updated custom fields
272 $theme_options['wpestate_custom_fields_list'] = $custom_fields;
273 // Only flag as done if the option actually saved.
274 if (update_option('wpresidence_admin', $theme_options)) {
275 // Mark as updated to prevent re-running
276 update_option('mlsimport_custom_fields_updated', true);
277 }
278 // Houzez (992): add the fields to the current property's additional_features.
279 } else if ($theme_id === 992) {
280 // Update additional features meta for properties
281 global $post;
282 $property_id = $post->ID;
283 $extra_fields = get_post_meta($property_id, 'additional_features', true);
284
285 // Ensure we have an array to work with.
286 if (!is_array($extra_fields)) {
287 $extra_fields = array();
288 }
289
290 // Check if the fields already exist before adding
291 // Collect existing feature titles to avoid duplicates.
292 $existing_titles = array_column($extra_fields, 'fave_additional_feature_title');
293
294 // Add the office-name feature once.
295 if (!in_array('officename', $existing_titles)) {
296 $extra_fields[] = array(
297 'fave_additional_feature_title' => 'officename',
298 'fave_additional_feature_value' => '',
299 );
300 }
301
302 // Add the member-name feature once.
303 if (!in_array('memberfullname', $existing_titles)) {
304 $extra_fields[] = array(
305 'fave_additional_feature_title' => 'memberfullname',
306 'fave_additional_feature_value' => '',
307 );
308 }
309
310 // Save updated additional features
311 update_post_meta($property_id, 'additional_features', $extra_fields);
312 }
313 }
314
315 // Hook into admin init or another relevant action
316 add_action('admin_init', 'mlsimport_update_custom_fields');
317