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:
@@ -40,6 +40,7 @@ class MLS_CLI {
|
||||
WP_CLI::add_command('mls recovery', array($instance, 'recovery'));
|
||||
WP_CLI::add_command('mls media', array($instance, 'media'));
|
||||
WP_CLI::add_command('mls geo', array($instance, 'geo'));
|
||||
WP_CLI::add_command('mls property', array($instance, 'property'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,7 +257,7 @@ class MLS_CLI {
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <type>
|
||||
* : Sync type: full, incremental, media, or resume
|
||||
* : Sync type: full, incremental, media-refresh, or resume
|
||||
*
|
||||
* [--dry-run]
|
||||
* : Show what would be synced without making changes
|
||||
@@ -267,8 +268,8 @@ class MLS_CLI {
|
||||
* [--id=<sync_id>]
|
||||
* : Sync state ID to resume (for resume command)
|
||||
*
|
||||
* [--force]
|
||||
* : Force re-download of media (for media command)
|
||||
* [--days=<n>]
|
||||
* : Days ahead to check for expiring media (for incremental and media-refresh, default: 3)
|
||||
*
|
||||
* [--quiet]
|
||||
* : Suppress progress output
|
||||
@@ -281,8 +282,10 @@ class MLS_CLI {
|
||||
* wp mls sync full
|
||||
* wp mls sync full --dry-run --limit=10
|
||||
* wp mls sync incremental
|
||||
* wp mls sync incremental --days=7
|
||||
* wp mls sync incremental --verbose
|
||||
* wp mls sync media --limit=100
|
||||
* wp mls sync media-refresh
|
||||
* wp mls sync media-refresh --days=7
|
||||
* wp mls sync resume --id=5
|
||||
*
|
||||
* @subcommand sync
|
||||
@@ -375,6 +378,22 @@ class MLS_CLI {
|
||||
echo "\n";
|
||||
}
|
||||
$this->output_sync_result($result);
|
||||
|
||||
// Run media refresh after successful incremental sync
|
||||
if ($result['success'] && !$dry_run) {
|
||||
$days = isset($assoc_args['days']) ? (int) $assoc_args['days'] : 3;
|
||||
WP_CLI::line('');
|
||||
WP_CLI::line("Running media refresh (properties expiring within {$days} days)...");
|
||||
if (!$quiet) {
|
||||
$this->print_progress_legend($verbose);
|
||||
}
|
||||
|
||||
$media_result = $sync_engine->run_media_refresh_sync($days, false, $progress_callback);
|
||||
if (!$quiet) {
|
||||
echo "\n";
|
||||
}
|
||||
$this->output_sync_result($media_result);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'media':
|
||||
@@ -384,6 +403,24 @@ class MLS_CLI {
|
||||
WP_CLI::line('');
|
||||
WP_CLI::line('Use "wp mls media status" to see cache statistics.');
|
||||
WP_CLI::line('Use "wp mls media fetch --listing=<key>" to pre-cache a specific listing.');
|
||||
WP_CLI::line('Use "wp mls sync media-refresh" to proactively refresh expiring media URLs.');
|
||||
break;
|
||||
|
||||
case 'media-refresh':
|
||||
$days = isset($assoc_args['days']) ? (int) $assoc_args['days'] : 3;
|
||||
WP_CLI::line("Starting media refresh sync (properties expiring within {$days} days)...");
|
||||
if ($dry_run) {
|
||||
WP_CLI::line('DRY RUN - No changes will be made');
|
||||
}
|
||||
if (!$quiet) {
|
||||
$this->print_progress_legend($verbose);
|
||||
}
|
||||
|
||||
$result = $sync_engine->run_media_refresh_sync($days, $dry_run, $progress_callback);
|
||||
if (!$quiet) {
|
||||
echo "\n";
|
||||
}
|
||||
$this->output_sync_result($result);
|
||||
break;
|
||||
|
||||
case 'resume':
|
||||
@@ -405,7 +442,7 @@ class MLS_CLI {
|
||||
break;
|
||||
|
||||
default:
|
||||
WP_CLI::error("Unknown sync type: {$type}. Use 'full', 'incremental', 'media', or 'resume'.");
|
||||
WP_CLI::error("Unknown sync type: {$type}. Use 'full', 'incremental', 'media-refresh', or 'resume'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1306,4 +1343,113 @@ class MLS_CLI {
|
||||
WP_CLI::log("... and " . ($total - 100) . " more.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a property directly from the MLS API and dump the response.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <listing_id>
|
||||
* : The MLS listing ID (e.g., NST6755550 or 6755550)
|
||||
*
|
||||
* [--format=<format>]
|
||||
* : Output format: json, table, or fields (default: fields)
|
||||
*
|
||||
* [--fields=<fields>]
|
||||
* : Comma-separated list of fields to show (for fields format)
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp mls property 6755550
|
||||
* wp mls property NST6755550 --format=json
|
||||
* wp mls property 6755550 --fields=StandardStatus,ListPrice,CloseDate
|
||||
*
|
||||
* @subcommand property
|
||||
*/
|
||||
public function property($args, $assoc_args) {
|
||||
$listing_id = isset($args[0]) ? $args[0] : null;
|
||||
|
||||
if (!$listing_id) {
|
||||
WP_CLI::error('Please provide a listing ID');
|
||||
}
|
||||
|
||||
// Add NST prefix if not present
|
||||
if (!preg_match('/^[A-Z]{3}/', $listing_id)) {
|
||||
$listing_id = 'NST' . $listing_id;
|
||||
}
|
||||
|
||||
WP_CLI::line("Fetching property {$listing_id} from MLS API...");
|
||||
|
||||
$api_client = $this->plugin->get_api_client();
|
||||
$result = $api_client->get_property_media($listing_id);
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
WP_CLI::error('API Error: ' . $result->get_error_message());
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
WP_CLI::error("Property {$listing_id} not found in MLS");
|
||||
}
|
||||
|
||||
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'fields';
|
||||
|
||||
switch ($format) {
|
||||
case 'json':
|
||||
echo json_encode($result, JSON_PRETTY_PRINT) . "\n";
|
||||
break;
|
||||
|
||||
case 'table':
|
||||
// Flatten for table display
|
||||
$flat = array();
|
||||
foreach ($result as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
$flat[$key] = $value;
|
||||
}
|
||||
}
|
||||
WP_CLI\Utils\format_items('table', array($flat), array_keys($flat));
|
||||
break;
|
||||
|
||||
case 'fields':
|
||||
default:
|
||||
// Show key fields
|
||||
$key_fields = array(
|
||||
'ListingId',
|
||||
'ListingKey',
|
||||
'StandardStatus',
|
||||
'MlsStatus',
|
||||
'ListPrice',
|
||||
'ClosePrice',
|
||||
'CloseDate',
|
||||
'ListOfficeName',
|
||||
'ListAgentFullName',
|
||||
'StreetNumber',
|
||||
'StreetName',
|
||||
'City',
|
||||
'StateOrProvince',
|
||||
'ModificationTimestamp',
|
||||
);
|
||||
|
||||
// Allow custom fields
|
||||
if (isset($assoc_args['fields'])) {
|
||||
$key_fields = explode(',', $assoc_args['fields']);
|
||||
}
|
||||
|
||||
WP_CLI::line('');
|
||||
WP_CLI::line('=== Property Details from MLS API ===');
|
||||
WP_CLI::line('');
|
||||
|
||||
foreach ($key_fields as $field) {
|
||||
$field = trim($field);
|
||||
$value = isset($result[$field]) ? $result[$field] : '(not set)';
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value);
|
||||
}
|
||||
WP_CLI::line(sprintf(' %-25s %s', $field . ':', $value));
|
||||
}
|
||||
|
||||
WP_CLI::line('');
|
||||
WP_CLI::line('Use --format=json to see full response');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user