b6df4dbb92
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>
136 lines
5.2 KiB
PHP
Executable File
136 lines
5.2 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Property Card for Agent Pages
|
|
*
|
|
* MLS property card using the same markup as the original property-card.php
|
|
* for consistent styling on agent profile pages.
|
|
*
|
|
* Expects MLS property object via set_query_var('agent_listing', $property)
|
|
*
|
|
* @package HomeProz
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
$property = get_query_var('agent_listing');
|
|
|
|
if (!$property) {
|
|
return;
|
|
}
|
|
|
|
// Extract property data from MLS object
|
|
$listing_key = $property->listing_key;
|
|
$listing_id = $property->listing_id;
|
|
$status = $property->standard_status;
|
|
// Use close_price for Closed properties if available
|
|
$price = ($status === 'Closed' && !empty($property->close_price)) ? $property->close_price : $property->list_price;
|
|
$bedrooms = $property->bedrooms_total;
|
|
$bathrooms = $property->bathrooms_total;
|
|
$square_feet = $property->living_area;
|
|
|
|
// Build address
|
|
$street_address = trim(implode(' ', array_filter([
|
|
$property->street_number,
|
|
$property->street_name,
|
|
$property->street_suffix,
|
|
])));
|
|
$city = $property->city;
|
|
$state = $property->state_or_province;
|
|
|
|
$full_address = $street_address;
|
|
if ($city) {
|
|
$full_address .= ', ' . $city;
|
|
}
|
|
if ($state) {
|
|
$full_address .= ', ' . $state;
|
|
}
|
|
|
|
// Status class
|
|
$status_class = 'badge-success';
|
|
if (strtolower($status) === 'pending') {
|
|
$status_class = 'badge-warning';
|
|
} elseif (strtolower($status) === 'sold' || strtolower($status) === 'closed') {
|
|
$status_class = 'badge-muted';
|
|
}
|
|
|
|
// Property URL
|
|
$property_url = home_url('/properties/?listing=' . urlencode($listing_key));
|
|
|
|
// Get primary photo from MLS
|
|
$primary_photo = function_exists('mls_get_property_image') ? mls_get_property_image($listing_key, true) : '';
|
|
?>
|
|
|
|
<article class="property-card card">
|
|
<a href="<?php echo esc_url($property_url); ?>" class="property-card-link-overlay" aria-hidden="true" tabindex="-1"></a>
|
|
<div class="property-card-image">
|
|
<?php if ($primary_photo) : ?>
|
|
<img src="<?php echo esc_url($primary_photo); ?>" alt="<?php echo esc_attr($full_address); ?>" loading="lazy">
|
|
<?php else : ?>
|
|
<div class="property-card-placeholder">
|
|
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
|
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
|
<polyline points="9 22 9 12 15 12 15 22"/>
|
|
</svg>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($status) : ?>
|
|
<span class="property-card-badge badge <?php echo esc_attr($status_class); ?>">
|
|
<?php echo esc_html($status); ?>
|
|
</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="property-card-content">
|
|
<div class="property-card-price">
|
|
<?php echo esc_html(homeproz_format_price($price)); ?>
|
|
</div>
|
|
|
|
<h3 class="property-card-title">
|
|
<?php echo esc_html($full_address); ?>
|
|
</h3>
|
|
|
|
<?php if ($bedrooms || $bathrooms || $square_feet) : ?>
|
|
<ul class="property-card-specs">
|
|
<?php if ($bedrooms) : ?>
|
|
<li class="spec-item">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
<path d="M3 7v11h18V7M3 7V4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v3M3 7h18M7 11h4v4H7zM14 11h3"/>
|
|
</svg>
|
|
<span><?php echo esc_html($bedrooms); ?> <?php echo $bedrooms == 1 ? 'Bed' : 'Beds'; ?></span>
|
|
</li>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($bathrooms) : ?>
|
|
<li class="spec-item">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
<path d="M4 12h16M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7M4 12V6a2 2 0 0 1 2-2h3v2a1 1 0 0 0 1 1h1a1 1 0 0 0 1-1V4"/>
|
|
</svg>
|
|
<span><?php echo esc_html($bathrooms); ?> <?php echo $bathrooms == 1 ? 'Bath' : 'Baths'; ?></span>
|
|
</li>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($square_feet) : ?>
|
|
<li class="spec-item">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
<path d="M3 9h18M9 3v18"/>
|
|
</svg>
|
|
<span><?php echo esc_html(number_format($square_feet)); ?> sqft</span>
|
|
</li>
|
|
<?php endif; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
|
|
<span class="property-card-link">
|
|
View Details
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
<path d="M5 12h14M12 5l7 7-7 7"/>
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
</article>
|