/** * Firma Real Estate - Theme Functions * Updated: 2026-03 - Added Supabase integration to hero form, * admin dashboard AJAX endpoints, improved emails. */ require_once get_template_directory() . '/gallery_functions.php'; if (!function_exists('firma_re_setup')): function firma_re_setup() { add_theme_support('title-tag'); add_theme_support('post-thumbnails'); add_theme_support('html5', array( 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption', )); register_nav_menus(array( 'menu-1' => esc_html__('Primary', 'firma-re'), )); } endif; add_action('after_setup_theme', 'firma_re_setup'); /** * Enqueue scripts and styles. */ function firma_re_scripts() { // Theme stylesheet wp_enqueue_style('firma-re-style', get_stylesheet_uri(), array(), wp_get_theme()->get('Version')); // Google Fonts wp_enqueue_style('firma-re-fonts', 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@700;800;900&display=swap', array(), null); // JavaScript wp_enqueue_script('firma-re-main', get_template_directory_uri() . '/js/main.js', array(), wp_get_theme()->get('Version'), true); // Localize for AJAX wp_localize_script('firma-re-main', 'firmaAjax', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('firma_form_nonce') )); } add_action('wp_enqueue_scripts', 'firma_re_scripts'); /** * Register Leads Custom Post Type */ function firma_re_register_leads_cpt() { register_post_type('lead', array( 'labels' => array( 'name' => 'Leads', 'singular_name' => 'Lead' ), 'public' => false, 'show_ui' => true, 'menu_icon'=> 'dashicons-email-alt', 'supports' => array('title', 'editor', 'custom-fields') )); } add_action('init', 'firma_re_register_leads_cpt'); /** * Add custom columns to Leads list in Admin */ function firma_re_set_lead_columns($columns) { return array( 'cb' => $columns['cb'], 'title' => 'Nombre / Lead', 'phone' => 'Teléfono / WhatsApp', 'location' => 'Ubicación', 'date' => $columns['date'], ); } add_filter('manage_lead_posts_columns', 'firma_re_set_lead_columns'); function firma_re_fill_lead_columns($column, $post_id) { switch ($column) { case 'phone': echo esc_html(get_post_meta($post_id, '_lead_phone', true)); break; case 'location': echo esc_html(get_post_meta($post_id, '_lead_location', true)); break; } } add_action('manage_lead_posts_custom_column', 'firma_re_fill_lead_columns', 10, 2); /** * MLS Import theme compatibility */ add_filter('mlsi_theme_name', function () { return 'WpResidence'; }); add_filter('pre_option_template', function ($value) { if (isset($_REQUEST['page']) && strpos($_REQUEST['page'], 'mlsimport') !== false) { return 'wpresidence'; } return $value; }); add_filter('single_template', function ($template) { global $post; $mls_post_types = array('listing', 'property', 'mlslisting', 'estate_property'); if (in_array($post->post_type, $mls_post_types)) { $custom = get_template_directory() . '/single-listing.php'; if (file_exists($custom)) { return $custom; } } return $template; }); add_filter('http_request_timeout', function () { return 120; }); /* ========================================================= * SHARED HELPER — SUPABASE PROXY * Sends lead data to Supabase REST API (server-side only). * The service key never touches the browser. * ========================================================= */ function firma_re_send_to_supabase($payload) { if (!defined('SUPABASE_URL') || !defined('SUPABASE_SERVICE_KEY') || empty(SUPABASE_URL)) { error_log('[Firma] Supabase: credenciales no configuradas en wp-config.php'); return false; } $url = rtrim(SUPABASE_URL, '/') . '/rest/v1/leads'; $args = array( 'headers' => array( 'apikey' => SUPABASE_SERVICE_KEY, 'Authorization' => 'Bearer ' . SUPABASE_SERVICE_KEY, 'Content-Type' => 'application/json', 'Prefer' => 'return=minimal', ), 'body' => wp_json_encode($payload), 'method' => 'POST', 'timeout' => 15, 'data_format' => 'body', ); $response = wp_remote_post($url, $args); if (is_wp_error($response)) { error_log('[Firma] Supabase WP_Error: ' . $response->get_error_message()); return false; } $code = wp_remote_retrieve_response_code($response); if ($code >= 200 && $code < 300) { return true; } error_log('[Firma] Supabase API error (' . $code . '): ' . wp_remote_retrieve_body($response)); return false; } /* ========================================================= * SHARED HELPER — EMAIL NOTIFICATION * Sends a formatted email to the admin for every new lead. * ========================================================= */ function firma_re_send_lead_email($data) { $to = defined('FIRMA_ADMIN_EMAIL') ? FIRMA_ADMIN_EMAIL : 'jlayala@firmarealestate.com'; $source = $data['source'] ?? 'formulario'; $name = $data['name'] ?? ''; $phone = $data['phone'] ?? ''; $email = $data['email'] ?? ''; $location= $data['location'] ?? ($data['address'] ?? ''); $type = $data['property_type'] ?? ''; $urgency = $data['urgency'] ?? ''; $message = $data['message'] ?? ''; $now = current_time('d/m/Y H:i'); $label_map = array( 'hero_form' => 'Inicio — Análisis de Mercado', 'seller_form' => 'Vende tu Propiedad', ); $source_label = $label_map[$source] ?? ucfirst($source); $subject = "Nuevo Lead [{$source_label}] - {$name}"; $body = "Has recibido un nuevo lead desde el sitio web.\n\n"; $body .= "═══════════════════════════\n"; $body .= "DATOS DEL CLIENTE\n"; $body .= "═══════════════════════════\n"; $body .= "Fecha y hora: {$now}\n"; $body .= "Nombre: {$name}\n"; $body .= "Teléfono/WA: {$phone}\n"; $body .= "Correo: {$email}\n\n"; $body .= "PROPIEDAD\n"; $body .= "───────────────────────────\n"; $body .= "Ubicación: {$location}\n"; $body .= "Tipo: {$type}\n"; $body .= "¿Cuándo?: {$urgency}\n\n"; if ($message) { $body .= "Mensaje:\n{$message}\n\n"; } $body .= "───────────────────────────\n"; $body .= "Fuente: {$source_label}\n"; $body .= "Sitio: https://test.firmarealestate.com\n"; $headers = array('Content-Type: text/plain; charset=UTF-8'); try { wp_mail($to, $subject, $body, $headers); } catch (Throwable $e) { error_log('[Firma] wp_mail error: ' . $e->getMessage()); } } /* ========================================================= * HERO FORM — Handle AJAX Submission * (Home page "Solicitar Análisis de Mercado" form) * ========================================================= */ function firma_re_handle_hero_form() { check_ajax_referer('firma_form_nonce', 'nonce'); $location = sanitize_text_field($_POST['location'] ?? ''); $type = sanitize_text_field($_POST['property_type'] ?? ''); $name = sanitize_text_field($_POST['name'] ?? ''); $phone = sanitize_text_field($_POST['phone'] ?? ''); $email = sanitize_email($_POST['email'] ?? ''); $urgency = sanitize_text_field($_POST['timeline'] ?? $_POST['urgency'] ?? ''); $message = sanitize_textarea_field($_POST['message'] ?? ''); if (empty($name) || empty($phone)) { wp_send_json_error('Campos obligatorios incompletos.'); return; } // 1. Save in WordPress dashboard $post_id = wp_insert_post(array( 'post_title' => "Lead: {$name} - {$location}", 'post_type' => 'lead', 'post_status' => 'publish', )); if ($post_id) { update_post_meta($post_id, '_lead_type_source', 'hero_form'); update_post_meta($post_id, '_lead_name', $name); update_post_meta($post_id, '_lead_phone', $phone); update_post_meta($post_id, '_lead_email', $email); update_post_meta($post_id, '_lead_location', $location); update_post_meta($post_id, '_lead_type', $type); update_post_meta($post_id, '_lead_urgency', $urgency); update_post_meta($post_id, '_lead_message', $message); } // 2. Send to Supabase $payload = array( 'source' => 'hero_form', 'name' => $name, 'phone' => $phone, 'email' => $email, 'location' => $location, 'property_type' => $type, 'urgency' => $urgency, 'message' => $message, ); try { firma_re_send_to_supabase($payload); } catch (Throwable $e) { error_log('[Firma] Supabase hero form error: ' . $e->getMessage()); } // 3. Send email notification firma_re_send_lead_email($payload); wp_send_json_success('Lead guardado correctamente.'); } add_action('wp_ajax_submit_hero_valoracion', 'firma_re_handle_hero_form'); add_action('wp_ajax_nopriv_submit_hero_valoracion', 'firma_re_handle_hero_form'); /* ========================================================= * SELLER FORM — Handle AJAX Submission * (Vende tu Propiedad page) * ========================================================= */ function firma_re_handle_seller_lead() { // Security: verify nonce if (!isset($_POST['seller_nonce']) || !wp_verify_nonce($_POST['seller_nonce'], 'firma_seller_nonce')) { wp_send_json_error('Solicitud no válida.'); return; } $name = sanitize_text_field($_POST['name'] ?? ''); $phone = sanitize_text_field($_POST['phone'] ?? ''); $email = sanitize_email($_POST['email'] ?? ''); $address = sanitize_text_field($_POST['address'] ?? $_POST['location'] ?? ''); $prop_type= sanitize_text_field($_POST['property_type'] ?? ''); $urgency = sanitize_text_field($_POST['timeline'] ?? $_POST['urgency'] ?? ''); $message = sanitize_textarea_field($_POST['message'] ?? ''); if (empty($name) || empty($phone) || empty($address)) { wp_send_json_error('Campos obligatorios incompletos.'); return; } // 1. Save in WordPress dashboard $post_id = wp_insert_post(array( 'post_title' => "Vendedor: {$name} - {$address}", 'post_type' => 'lead', 'post_status' => 'publish', )); if ($post_id) { update_post_meta($post_id, '_lead_type_source', 'seller_form'); update_post_meta($post_id, '_lead_name', $name); update_post_meta($post_id, '_lead_phone', $phone); update_post_meta($post_id, '_lead_email', $email); update_post_meta($post_id, '_lead_location', $address); update_post_meta($post_id, '_lead_prop_type', $prop_type); update_post_meta($post_id, '_lead_urgency', $urgency); update_post_meta($post_id, '_lead_message', $message); } // 2. Send to Supabase $payload = array( 'source' => 'seller_form', 'name' => $name, 'phone' => $phone, 'email' => $email, 'location' => $address, 'property_type' => $prop_type, 'urgency' => $urgency, 'message' => $message, ); try { firma_re_send_to_supabase($payload); } catch (Throwable $e) { error_log('[Firma] Supabase seller form error: ' . $e->getMessage()); } // 3. Send email notification firma_re_send_lead_email($payload); wp_send_json_success('Lead de vendedor guardado y email enviado.'); } add_action('wp_ajax_submit_seller_lead', 'firma_re_handle_seller_lead'); add_action('wp_ajax_nopriv_submit_seller_lead', 'firma_re_handle_seller_lead'); /* ========================================================= * ADMIN DASHBOARD — GET LEADS (AJAX) * Reads leads from Supabase; requires admin capability. * The service key stays server-side. * ========================================================= */ function firma_leads_get() { // Security: only WP admins if (!current_user_can('manage_options')) { wp_send_json_error('Sin autorización.', 403); return; } check_ajax_referer('firma_dashboard_nonce', 'nonce'); if (!defined('SUPABASE_URL') || !defined('SUPABASE_SERVICE_KEY')) { wp_send_json_error('Credenciales de Supabase no configuradas.'); return; } // Build query params $search = sanitize_text_field($_POST['search'] ?? ''); $source = sanitize_text_field($_POST['source'] ?? ''); $prop_type= sanitize_text_field($_POST['property_type'] ?? ''); $urgency = sanitize_text_field($_POST['urgency'] ?? ''); $status = sanitize_text_field($_POST['status'] ?? ''); $date_from= sanitize_text_field($_POST['date_from'] ?? ''); $date_to = sanitize_text_field($_POST['date_to'] ?? ''); $page = max(1, intval($_POST['page'] ?? 1)); $per_page = 50; $offset = ($page - 1) * $per_page; // Build Supabase REST query $params = array( 'order' => 'created_at.desc', 'limit' => $per_page, 'offset' => $offset, ); // Filters if ($source) $params['source'] = 'eq.' . $source; if ($prop_type) $params['property_type'] = 'eq.' . $prop_type; if ($urgency) $params['urgency'] = 'eq.' . $urgency; if ($status) $params['status'] = 'eq.' . $status; if ($date_from) $params['created_at'] = 'gte.' . $date_from . 'T00:00:00Z'; if ($date_to) { // If both from and to, use between-style — just override with lte for to $params['created_at'] = ($date_from ? 'gte.' . $date_from . 'T00:00:00Z&created_at=lte.' . $date_to . 'T23:59:59Z' : 'lte.' . $date_to . 'T23:59:59Z'); } // Text search across name/email/phone/location using Supabase 'or' if ($search) { $s = addslashes($search); $params['or'] = "(name.ilike.*{$s}*,email.ilike.*{$s}*,phone.ilike.*{$s}*,location.ilike.*{$s}*)"; } $url = rtrim(SUPABASE_URL, '/') . '/rest/v1/leads?' . http_build_query($params); $response = wp_remote_get($url, array( 'headers' => array( 'apikey' => SUPABASE_SERVICE_KEY, 'Authorization' => 'Bearer ' . SUPABASE_SERVICE_KEY, 'Content-Type' => 'application/json', 'Prefer' => 'count=exact', ), 'timeout' => 15, )); if (is_wp_error($response)) { wp_send_json_error('Error de conexión con Supabase: ' . $response->get_error_message()); return; } $code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); // Get total count from Content-Range header $content_range = wp_remote_retrieve_header($response, 'content-range'); $total = 0; if (preg_match('/\/(\d+)$/', $content_range, $m)) { $total = intval($m[1]); } if ($code >= 200 && $code < 300) { wp_send_json_success(array( 'leads' => $data, 'total' => $total, 'page' => $page, 'per_page'=> $per_page, )); } else { wp_send_json_error('Supabase error (' . $code . '): ' . $body); } } add_action('wp_ajax_firma_leads_get', 'firma_leads_get'); /* ========================================================= * ADMIN DASHBOARD — UPDATE LEAD (AJAX) * Updates status / notes for a specific lead. * ========================================================= */ function firma_leads_update() { if (!current_user_can('manage_options')) { wp_send_json_error('Sin autorización.', 403); return; } check_ajax_referer('firma_dashboard_nonce', 'nonce'); if (!defined('SUPABASE_URL') || !defined('SUPABASE_SERVICE_KEY')) { wp_send_json_error('Credenciales de Supabase no configuradas.'); return; } $id = sanitize_text_field($_POST['lead_id'] ?? ''); $status = sanitize_text_field($_POST['status'] ?? ''); $notes = sanitize_textarea_field($_POST['notes'] ?? ''); if (empty($id)) { wp_send_json_error('ID de lead requerido.'); return; } $allowed_statuses = array('nuevo', 'contactado', 'en_proceso', 'cerrado', 'descartado'); if ($status && !in_array($status, $allowed_statuses, true)) { wp_send_json_error('Estado no válido.'); return; } $patch = array(); if ($status) $patch['status'] = $status; $patch['notes'] = $notes; // Can be empty string to clear notes if ($status === 'contactado' || $status === 'en_proceso') { $patch['contacted_at'] = gmdate('c'); } $url = rtrim(SUPABASE_URL, '/') . '/rest/v1/leads?id=eq.' . urlencode($id); $response = wp_remote_request($url, array( 'method' => 'PATCH', 'headers' => array( 'apikey' => SUPABASE_SERVICE_KEY, 'Authorization' => 'Bearer ' . SUPABASE_SERVICE_KEY, 'Content-Type' => 'application/json', 'Prefer' => 'return=representation', ), 'body' => wp_json_encode($patch), 'timeout' => 15, )); if (is_wp_error($response)) { wp_send_json_error('Error al actualizar: ' . $response->get_error_message()); return; } $code = wp_remote_retrieve_response_code($response); if ($code >= 200 && $code < 300) { wp_send_json_success('Lead actualizado correctamente.'); } else { wp_send_json_error('Supabase error (' . $code . '): ' . wp_remote_retrieve_body($response)); } } add_action('wp_ajax_firma_leads_update', 'firma_leads_update');
Warning: Cannot modify header information - headers already sent by (output started at /home/u927131979/domains/firmarealestate.com/public_html/wp-content/themes/wp-theme-firma/functions.php:1) in /home/u927131979/domains/firmarealestate.com/public_html/wp-includes/feed-rss2-comments.php on line 8
Comentarios en: https://firmarealestate.com/?attachment_id=236080 Mon, 09 Mar 2026 19:36:34 +0000 hourly 1 https://wordpress.org/?v=6.8.3