403]); } global $wpdb; $repo = new Repository(); $previous = $wpdb->suppress_errors(true); $count = $wpdb->get_var($wpdb->prepare("SELECT COUNT(DISTINCT pv.product_id) FROM %i pv INNER JOIN %i f ON f.id=pv.facet_id WHERE f.slug LIKE %s", $repo->productValueTable(), $repo->facetTable(), 'wc-attribute-%')); $error = $wpdb->last_error !== ''; $wpdb->suppress_errors($previous); $sync = get_option('facetcore_sync_status', []); echo '

' . esc_html__('FacetCore Index', 'facetcore') . '

' . esc_html__('WooCommerce is the source of truth. The facet index updates automatically when product attributes change. Name, SKU, category and visibility changes are read directly from WooCommerce.', 'facetcore') . '

'; // The result flag is display-only; the redirect from rebuild() signs it with its own nonce. $nonce = isset($_GET['_fcnonce']) ? sanitize_text_field(wp_unslash($_GET['_fcnonce'])) : ''; $rebuilt = isset($_GET['rebuilt']) && wp_verify_nonce($nonce, 'facetcore_rebuilt') ? sanitize_text_field(wp_unslash($_GET['rebuilt'])) : null; if ($rebuilt !== null) { echo '

' . ($rebuilt === '1' ? esc_html__('Index rebuilt successfully.', 'facetcore') : esc_html__('Rebuild failed. Retry after checking database availability.', 'facetcore')) . '

'; } echo '

' . esc_html__('Indexed products with configured attributes:', 'facetcore') . ' ' . ($error ? esc_html__('Unavailable', 'facetcore') : (int) $count) . '

' . esc_html__('Last attribute sync:', 'facetcore') . ' ' . esc_html($sync['time'] ?? __('Not yet run', 'facetcore')) . '

' . esc_html__('Status:', 'facetcore') . ' ' . ($error || !empty($sync['error']) ? esc_html__('Needs attention', 'facetcore') : esc_html__('Available', 'facetcore')) . '

'; echo '

' . esc_html__('Products without configured attributes can still appear in finders. Rebuild refreshes the selected attributes of all saved finders. A large catalog may take a while.', 'facetcore') . '

'; wp_nonce_field('facetcore_rebuild'); echo ''; submit_button(__('Rebuild Index', 'facetcore')); echo '
'; } public function rebuild(): void { if (!current_user_can('manage_woocommerce')) { wp_die(esc_html__('Forbidden', 'facetcore'), '', ['response' => 403]); } if (!isset($_REQUEST['_wpnonce']) || !is_string($_REQUEST['_wpnonce'])) { wp_die(esc_html__('Invalid request.', 'facetcore'), '', ['response' => 403]); } check_admin_referer('facetcore_rebuild'); global $wpdb; $previous = $wpdb->suppress_errors(true); $ok = true; try { Installer::activate(); $attributes = []; $page = 1; do { $posts = get_posts(['post_type' => TableConfiguration::TYPE, 'post_status' => ['publish', 'draft', 'private', 'pending', 'future'], 'numberposts' => 100, 'paged' => $page++, 'fields' => 'ids', 'orderby' => 'ID', 'order' => 'ASC']); foreach ($posts as $id) { $config = TableConfiguration::get((int) $id, false); if ($config) { $attributes = array_merge($attributes, $config['filters']); } } } while (count($posts) === 100); (new AttributeIndex())->ensure(['filters' => array_unique($attributes)]); } catch (\Throwable $e) { $ok = false; } finally { $wpdb->suppress_errors($previous); } update_option('facetcore_sync_status', ['time' => current_time('mysql'), 'error' => !$ok], false); wp_safe_redirect(add_query_arg(['rebuilt' => $ok ? '1' : '0', '_fcnonce' => wp_create_nonce('facetcore_rebuilt')], admin_url('edit.php?post_type=' . TableConfiguration::TYPE . '&page=facetcore-index'))); exit; } }