| @@ -1372,8 +1372,14 @@ | ||
| 1372 | 1372 | |
| 1373 | 1373 | // Check for in-progress local indexing and auto-resume |
| 1374 | 1374 | this.checkLocalIndexingProgress(); |
| 1375 | 1375 | |
| 1376 | + // Check for in-progress cloud indexing auto-refresh (survives page reloads) | |
| 1377 | + this.checkForumIndexingAutoRefresh(); | |
| 1378 | + | |
| 1379 | + // Load indexing breakdown asynchronously (cached 1 day) | |
| 1380 | + this.loadIndexingBreakdown(); | |
| 1381 | + | |
| 1376 | 1382 | // Note: Polling is started from PHP inline script based on server-side $is_indexing status |
| 1377 | 1383 | // No need to start it here to avoid duplicate polling |
| 1378 | 1384 | }, |
| 1379 | 1385 | |
| @@ -1633,8 +1639,91 @@ | ||
| 1633 | 1639 | } |
| 1634 | 1640 | }); |
| 1635 | 1641 | }, |
| 1636 | 1642 | |
| 1643 | + /** | |
| 1644 | + * Load indexing breakdown via AJAX (private/unapproved topic counts) | |
| 1645 | + * Data is cached server-side for 1 day to avoid slow GROUP BY queries | |
| 1646 | + */ | |
| 1647 | + loadIndexingBreakdown: function() { | |
| 1648 | + const $container = $('#wpforo-ai-indexing-breakdown-container'); | |
| 1649 | + if (!$container.length) { | |
| 1650 | + return; | |
| 1651 | + } | |
| 1652 | + | |
| 1653 | + const self = this; | |
| 1654 | + const loadingText = $container.data('loading-text') || 'Loading...'; | |
| 1655 | + | |
| 1656 | + // Show small loading spinner | |
| 1657 | + $container.html('<span class="wpforo-ai-breakdown-loading"><span class="dashicons dashicons-update wpforo-spin"></span> ' + loadingText + '</span>'); | |
| 1658 | + | |
| 1659 | + $.ajax({ | |
| 1660 | + url: wpforoAIAdmin.ajaxUrl, | |
| 1661 | + type: 'POST', | |
| 1662 | + data: { | |
| 1663 | + action: 'wpforo_ai_get_indexing_breakdown', | |
| 1664 | + nonce: wpforoAIAdmin.nonce | |
| 1665 | + }, | |
| 1666 | + success: function(response) { | |
| 1667 | + if (response.success && response.data) { | |
| 1668 | + self.renderIndexingBreakdown($container, response.data); | |
| 1669 | + } else { | |
| 1670 | + $container.empty(); | |
| 1671 | + } | |
| 1672 | + }, | |
| 1673 | + error: function() { | |
| 1674 | + $container.empty(); | |
| 1675 | + } | |
| 1676 | + }); | |
| 1677 | + }, | |
| 1678 | + | |
| 1679 | + /** | |
| 1680 | + * Render the indexing breakdown HTML | |
| 1681 | + */ | |
| 1682 | + renderIndexingBreakdown: function($container, data) { | |
| 1683 | + const privateCount = parseInt(data.private, 10) || 0; | |
| 1684 | + const unapprovedCount = parseInt(data.unapproved, 10) || 0; | |
| 1685 | + | |
| 1686 | + if (privateCount === 0 && unapprovedCount === 0) { | |
| 1687 | + $container.empty(); | |
| 1688 | + return; | |
| 1689 | + } | |
| 1690 | + | |
| 1691 | + const excludedCount = privateCount + unapprovedCount; | |
| 1692 | + const excludedText = $container.data('excluded-text') || '%s topics are excluded from indexing'; | |
| 1693 | + const introText = $container.data('intro-text') || 'The following topics are automatically excluded from AI indexing:'; | |
| 1694 | + const privateText = $container.data('private-text') || 'private topics - these are only visible to their authors'; | |
| 1695 | + const unapprovedText = $container.data('unapproved-text') || 'unapproved topics - these will be indexed once approved by moderators'; | |
| 1696 | + const noteText = $container.data('note-text') || 'Private topics are never indexed to protect user privacy. Unapproved topics will be automatically indexed when approved.'; | |
| 1697 | + | |
| 1698 | + let html = '<div class="wpforo-ai-indexing-breakdown">'; | |
| 1699 | + html += '<details class="wpforo-ai-breakdown-details">'; | |
| 1700 | + html += '<summary class="wpforo-ai-breakdown-summary">'; | |
| 1701 | + html += '<span class="dashicons dashicons-info-outline"></span>'; | |
| 1702 | + html += excludedText.replace('%s', '<strong>' + this.formatNumber(excludedCount) + '</strong>'); | |
| 1703 | + html += '<span class="dashicons dashicons-arrow-down-alt2 wpforo-ai-breakdown-arrow"></span>'; | |
| 1704 | + html += '</summary>'; | |
| 1705 | + html += '<div class="wpforo-ai-breakdown-content">'; | |
| 1706 | + html += '<p class="wpforo-ai-breakdown-intro">' + introText + '</p>'; | |
| 1707 | + html += '<ul class="wpforo-ai-breakdown-list">'; | |
| 1708 | + | |
| 1709 | + if (privateCount > 0) { | |
| 1710 | + html += '<li><span class="dashicons dashicons-lock"></span>'; | |
| 1711 | + html += '<strong>' + this.formatNumber(privateCount) + '</strong> ' + privateText + '</li>'; | |
| 1712 | + } | |
| 1713 | + | |
| 1714 | + if (unapprovedCount > 0) { | |
| 1715 | + html += '<li><span class="dashicons dashicons-clock"></span>'; | |
| 1716 | + html += '<strong>' + this.formatNumber(unapprovedCount) + '</strong> ' + unapprovedText + '</li>'; | |
| 1717 | + } | |
| 1718 | + | |
| 1719 | + html += '</ul>'; | |
| 1720 | + html += '<p class="wpforo-ai-breakdown-note"><em>' + noteText + '</em></p>'; | |
| 1721 | + html += '</div></details></div>'; | |
| 1722 | + | |
| 1723 | + $container.html(html); | |
| 1724 | + }, | |
| 1725 | + | |
| 1637 | 1726 | // WordPress Content Indexing Methods have been moved to |
| 1638 | 1727 | // ai-features-wp-indexing.js for the dedicated WordPress Indexing tab. |
| 1639 | 1728 | // See: WpForoWPIndexing in admin/assets/js/ai-features-wp-indexing.js |
| 1640 | 1729 | |
| @@ -1993,8 +2082,11 @@ | ||
| 1993 | 2082 | // Use localStorage to persist across page reloads |
| 1994 | 2083 | this.indexingStopping = true; |
| 1995 | 2084 | localStorage.setItem('wpforo_indexing_stopping', 'true'); |
| 1996 | 2085 | |
| 2086 | + // Clear auto-refresh flag so page doesn't keep reloading after stop | |
| 2087 | + this.stopForumIndexingAutoRefresh(); | |
| 2088 | + | |
| 1997 | 2089 | // Immediately update status to show "Stopping..." |
| 1998 | 2090 | const $statusElement = $('#rag-indexing-status'); |
| 1999 | 2091 | if ($statusElement.length) { |
| 2000 | 2092 | $statusElement.text('Stopping...'); |
| @@ -2066,16 +2158,18 @@ | ||
| 2066 | 2158 | const originalHtml = $button.html(); |
| 2067 | 2159 | $button.prop('disabled', true).html('<span class="dashicons dashicons-update"></span> Cleaning up...'); |
| 2068 | 2160 | |
| 2069 | 2161 | // Clear any browser-side stuck state first — regardless of AJAX |
| 2070 | - // outcome. This is the only client-side flag the plugin sets for | |
| 2162 | + // outcome. These are the client-side flags the plugin sets for | |
| 2071 | 2163 | // indexing (see handleStopIndexing / checkLocalIndexingProgress). |
| 2072 | 2164 | try { |
| 2073 | 2165 | localStorage.removeItem('wpforo_indexing_stopping'); |
| 2074 | 2166 | localStorage.removeItem('wpforo_wp_indexing_auto_refresh'); |
| 2167 | + localStorage.removeItem('wpforo_forum_indexing_auto_refresh'); | |
| 2075 | 2168 | } catch (err) { /* localStorage may be blocked in some contexts */ } |
| 2076 | 2169 | this.indexingStopping = false; |
| 2077 | 2170 | this.stopWPIndexingAutoRefresh(); |
| 2171 | + this.stopForumIndexingAutoRefresh(); | |
| 2078 | 2172 | |
| 2079 | 2173 | const self = this; |
| 2080 | 2174 | $.ajax({ |
| 2081 | 2175 | url: ajaxurl, |
| @@ -2183,8 +2277,15 @@ | ||
| 2183 | 2277 | |
| 2184 | 2278 | // Add loading state to button |
| 2185 | 2279 | $button.addClass('loading').prop('disabled', true); |
| 2186 | 2280 | |
| 2281 | + // Set localStorage flag for reindex actions so auto-refresh survives page reloads | |
| 2282 | + if (action === 'reindex_all' || action === 'clear_and_reindex' || action === 'reindex_images') { | |
| 2283 | + try { | |
| 2284 | + localStorage.setItem('wpforo_forum_indexing_auto_refresh', '1'); | |
| 2285 | + } catch (e) { /* localStorage may be blocked */ } | |
| 2286 | + } | |
| 2287 | + | |
| 2187 | 2288 | // Append form to body and submit |
| 2188 | 2289 | $('body').append($form); |
| 2189 | 2290 | $form.submit(); |
| 2190 | 2291 | }, |
| @@ -2436,8 +2537,95 @@ | ||
| 2436 | 2537 | } |
| 2437 | 2538 | }, |
| 2438 | 2539 | |
| 2439 | 2540 | /** |
| 2541 | + * Start auto page refresh for forum content indexing (cloud mode). | |
| 2542 | + * Sets localStorage flag so polling survives page reloads. | |
| 2543 | + */ | |
| 2544 | + startForumIndexingAutoRefresh: function() { | |
| 2545 | + try { | |
| 2546 | + localStorage.setItem('wpforo_forum_indexing_auto_refresh', '1'); | |
| 2547 | + } catch (e) { /* localStorage may be blocked */ } | |
| 2548 | + | |
| 2549 | + console.log('Forum indexing: reloading page to start auto-refresh...'); | |
| 2550 | + window.location.hash = 'rag-status-section'; | |
| 2551 | + window.location.reload(); | |
| 2552 | + }, | |
| 2553 | + | |
| 2554 | + /** | |
| 2555 | + * Check on page load if forum indexing auto-refresh should continue. | |
| 2556 | + * Polls API and schedules next reload if still indexing. | |
| 2557 | + */ | |
| 2558 | + checkForumIndexingAutoRefresh: function() { | |
| 2559 | + const self = this; | |
| 2560 | + | |
| 2561 | + let inAutoRefresh = false; | |
| 2562 | + try { | |
| 2563 | + inAutoRefresh = localStorage.getItem('wpforo_forum_indexing_auto_refresh') === '1'; | |
| 2564 | + } catch (e) { /* localStorage may be blocked */ } | |
| 2565 | + | |
| 2566 | + if (!inAutoRefresh) { | |
| 2567 | + return; | |
| 2568 | + } | |
| 2569 | + | |
| 2570 | + $.ajax({ | |
| 2571 | + url: ajaxurl, | |
| 2572 | + type: 'POST', | |
| 2573 | + data: { | |
| 2574 | + action: 'wpforo_ai_get_rag_status', | |
| 2575 | + _wpnonce: wpforoAIAdmin.nonce | |
| 2576 | + }, | |
| 2577 | + success: function(response) { | |
| 2578 | + if (response.success && response.data) { | |
| 2579 | + const cronActive = response.data.pending_cron_jobs && response.data.pending_cron_jobs.is_actively_processing; | |
| 2580 | + const hasPendingJobs = response.data.pending_cron_jobs && response.data.pending_cron_jobs.has_pending_jobs; | |
| 2581 | + const isActivelyProcessing = response.data.is_indexing || cronActive || hasPendingJobs; | |
| 2582 | + | |
| 2583 | + if (isActivelyProcessing) { | |
| 2584 | + console.log('Forum indexing in progress, will refresh in 20 seconds...'); | |
| 2585 | + self._forumAutoRefreshTimeout = setTimeout(function() { | |
| 2586 | + window.location.hash = 'rag-status-section'; | |
| 2587 | + window.location.reload(); | |
| 2588 | + }, 20000); | |
| 2589 | + } else { | |
| 2590 | + console.log('Forum indexing complete, final reload'); | |
| 2591 | + self.stopForumIndexingAutoRefresh(); | |
| 2592 | + window.location.hash = 'rag-status-section'; | |
| 2593 | + window.location.reload(); | |
| 2594 | + } | |
| 2595 | + } | |
| 2596 | + }, | |
| 2597 | + error: function() { | |
| 2598 | + self.stopForumIndexingAutoRefresh(); | |
| 2599 | + } | |
| 2600 | + }); | |
| 2601 | + }, | |
| 2602 | + | |
| 2603 | + /** | |
| 2604 | + * Stop forum indexing auto page refresh and clear the flag. | |
| 2605 | + */ | |
| 2606 | + stopForumIndexingAutoRefresh: function() { | |
| 2607 | + try { | |
| 2608 | + localStorage.removeItem('wpforo_forum_indexing_auto_refresh'); | |
| 2609 | + } catch (e) { /* localStorage may be blocked */ } | |
| 2610 | + | |
| 2611 | + if (this._forumAutoRefreshTimeout) { | |
| 2612 | + clearTimeout(this._forumAutoRefreshTimeout); | |
| 2613 | + this._forumAutoRefreshTimeout = null; | |
| 2614 | + } | |
| 2615 | + }, | |
| 2616 | + | |
| 2617 | + /** | |
| 2618 | + * Stop WP indexing auto refresh (stub for cleanup handler). | |
| 2619 | + * The actual implementation lives in ai-features-wp-indexing.js. | |
| 2620 | + */ | |
| 2621 | + stopWPIndexingAutoRefresh: function() { | |
| 2622 | + try { | |
| 2623 | + localStorage.removeItem('wpforo_wp_indexing_auto_refresh'); | |
| 2624 | + } catch (e) { /* localStorage may be blocked */ } | |
| 2625 | + }, | |
| 2626 | + | |
| 2627 | + /** | |
| 2440 | 2628 | * Handle search test form submission |
| 2441 | 2629 | */ |
| 2442 | 2630 | handleSearchTest: function(e) { |
| 2443 | 2631 | e.preventDefault(); |
| @@ -2690,11 +2878,12 @@ | ||
| 2690 | 2878 | success: function(response) { |
| 2691 | 2879 | if (response.success) { |
| 2692 | 2880 | console.log('Local indexing started:', response.data); |
| 2693 | 2881 | |
| 2694 | - // Reload page — checkLocalIndexingProgress() will detect | |
| 2695 | - // the queue on load and start the AJAX batch loop | |
| 2696 | - window.location.reload(); | |
| 2882 | + // Use auto-refresh mechanism for consistent UX with cloud mode. | |
| 2883 | + // Sets localStorage flag and reloads; checkForumIndexingAutoRefresh() | |
| 2884 | + // will continue refreshing every 20s while indexing is in progress. | |
| 2885 | + WpForoAI.startForumIndexingAutoRefresh(); | |
| 2697 | 2886 | } else { |
| 2698 | 2887 | const errorMsg = response.data && response.data.message |
| 2699 | 2888 | ? response.data.message |
| 2700 | 2889 | : 'Failed to start indexing'; |