Snapshot: MLS sync fixes, image refresh, plugin/theme updates
MLS plugin fixes from this session: - Fix silent insert failures: location column NOT NULL was rejecting wpdb->insert calls, causing ~18k new properties since Dec 2025 to be lost. Inserts now build raw SQL with ST_PointFromText so the spatial column is populated atomically. - Auto-refresh expired media URLs in MLS_Media_Handler::fetch_and_cache(), guarded by a property-level GET_LOCK so concurrent fetches share one API refresh. - Normalize WP_Error to null in mls_get_property_image() so callers can rely on the documented string|null contract. - Support comma-separated property_type filters in MLS_Query and MLS_Cluster so the homepage "View All Commercial" link (?property_type=Commercial+Sale,Land,Farm) actually filters correctly. - Incremental sync now looks back 10 minutes past the latest modification timestamp as a safety margin against missed records. - Smart sync exits silently (info-level, not warning) when a full sync is in progress. Operational: - New cron: weekly full sync Sundays at 3 AM (/usr/local/bin/mls-full-sync). - New cron: hourly 2GB cap on mls-thumbnails/ and cache/transformed-images/ (/usr/local/bin/mls-image-cache-cap). - Logrotate config for wp-content/debug.log (2-day retention, daily rotation, delaycompress). Repo policy: - CLAUDE.md updated with explicit "commit everything except build artifacts" policy. - .gitignore: untrack runtime image caches and debug.log rotations. Other modifications in this snapshot are pre-existing in-flight theme/plugin/db_content_updates work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -128,11 +128,12 @@ class MLS_API_Client {
|
||||
* @param string $endpoint API endpoint (relative to base URL)
|
||||
* @param array $params Query parameters
|
||||
* @param int $retry Current retry attempt
|
||||
* @param string $channel Rate limit channel ('general' or 'image')
|
||||
* @return array|WP_Error Response data or error
|
||||
*/
|
||||
public function request($endpoint, $params = array(), $retry = 0) {
|
||||
// Check and wait for rate limits
|
||||
$this->rate_limiter->check_and_wait(true);
|
||||
public function request($endpoint, $params = array(), $retry = 0, $channel = 'general') {
|
||||
// Check and wait for rate limits (uses global advisory lock coordination)
|
||||
$this->rate_limiter->check_and_wait(true, $channel);
|
||||
|
||||
$url = $this->build_url($endpoint, $params);
|
||||
|
||||
@@ -172,7 +173,7 @@ class MLS_API_Client {
|
||||
// Retry on transient errors
|
||||
if ($retry < self::MAX_RETRIES) {
|
||||
sleep(pow(2, $retry)); // Exponential backoff
|
||||
return $this->request($endpoint, $params, $retry + 1);
|
||||
return $this->request($endpoint, $params, $retry + 1, $channel);
|
||||
}
|
||||
|
||||
return $response;
|
||||
@@ -195,7 +196,7 @@ class MLS_API_Client {
|
||||
if (($status_code === 429 || $status_code >= 500) && $retry < self::MAX_RETRIES) {
|
||||
$wait = $status_code === 429 ? 60 : pow(2, $retry);
|
||||
sleep($wait);
|
||||
return $this->request($endpoint, $params, $retry + 1);
|
||||
return $this->request($endpoint, $params, $retry + 1, $channel);
|
||||
}
|
||||
|
||||
return new WP_Error('api_error', $error_message, array('status' => $status_code));
|
||||
@@ -382,7 +383,8 @@ class MLS_API_Client {
|
||||
* Get a single property by listing ID with media
|
||||
*
|
||||
* Used to refresh media URLs for a specific listing without
|
||||
* fetching the entire dataset.
|
||||
* fetching the entire dataset. Uses the 'image' rate limit channel
|
||||
* with a 2-second interval for on-demand image requests.
|
||||
*
|
||||
* Note: MLS Grid only allows filtering by ListingId (not ListingKey)
|
||||
* for the Property resource. The caller must provide the listing_id.
|
||||
@@ -400,7 +402,8 @@ class MLS_API_Client {
|
||||
$params['$expand'] = 'Media';
|
||||
$params['$top'] = 1;
|
||||
|
||||
$response = $this->request('Property', $params);
|
||||
// Use 'image' channel with 2-second rate limiting for on-demand media fetches
|
||||
$response = $this->request('Property', $params, 0, 'image');
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return $response;
|
||||
@@ -414,6 +417,41 @@ class MLS_API_Client {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple properties by listing IDs with media (batched)
|
||||
*
|
||||
* Fetches up to 25 properties in a single API request using OData 'in' filter.
|
||||
* Used for efficient media URL refresh without making individual API calls.
|
||||
* Uses the 'image' rate limit channel with 2-second interval.
|
||||
*
|
||||
* @param array $listing_ids Array of MLS listing IDs (max 25)
|
||||
* @return array|WP_Error Array of property data with Media, or error
|
||||
*/
|
||||
public function get_properties_by_ids($listing_ids) {
|
||||
if (empty($listing_ids)) {
|
||||
return array('value' => array());
|
||||
}
|
||||
|
||||
// Limit to 25 (MLS Grid's max with $expand)
|
||||
$listing_ids = array_slice($listing_ids, 0, 25);
|
||||
|
||||
$params = array();
|
||||
$system = $this->options->get_originating_system();
|
||||
|
||||
// Build 'in' filter: ListingId in ('ID1', 'ID2', 'ID3')
|
||||
$escaped_ids = array_map(function($id) {
|
||||
return "'" . addslashes($id) . "'";
|
||||
}, $listing_ids);
|
||||
$in_list = implode(',', $escaped_ids);
|
||||
|
||||
$params['$filter'] = "OriginatingSystemName eq '{$system}' and ListingId in ({$in_list})";
|
||||
$params['$expand'] = 'Media';
|
||||
$params['$top'] = 25;
|
||||
|
||||
// Use 'image' channel with 2-second rate limiting for media fetches
|
||||
return $this->request('Property', $params, 0, 'image');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next page of results
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user