Changeset 3375574
- Timestamp:
- 10/09/2025 09:25:49 AM (6 months ago)
- Location:
- synoveo/trunk
- Files:
-
- 7 edited
-
assets/js/capability-card-manager.js (modified) (2 diffs)
-
assets/js/dashboard-capabilities.js (modified) (1 diff)
-
assets/js/gbp-compare.js (modified) (6 diffs)
-
includes/admin/class-synoveo-admin-controller.php (modified) (1 diff)
-
includes/class-synoveo-rate-limiter.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
-
synoveo.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
synoveo/trunk/assets/js/capability-card-manager.js
r3375269 r3375574 62 62 */ 63 63 refreshAllCards() { 64 if (!window.synoveo_ajax) return; 64 if (!window.synoveo_ajax) { 65 console.warn('synoveo_ajax not available for card refresh'); 66 return Promise.reject(new Error('AJAX not available')); 67 } 68 69 console.log('🔄 Refreshing all cards...'); 65 70 66 71 return fetch(synoveo_ajax.ajax_url, { … … 74 79 }) 75 80 }) 76 .then(response => response.json()) 81 .then(response => { 82 if (!response.ok) { 83 throw new Error(`HTTP ${response.status}: ${response.statusText}`); 84 } 85 return response.json(); 86 }) 77 87 .then(data => { 78 if (data.success && data.data.detected_plugins) { 88 if (data.success && data.data) { 89 console.log('✅ Card data fetched successfully'); 79 90 this.updateCardsFromDetection(data.data); 80 91 return data.data; 81 92 } 82 throw new Error('Failed to fetch card data'); 83 }); 93 throw new Error('Invalid response data: ' + JSON.stringify(data)); 94 }) 95 .catch(error => { 96 console.error('❌ Card refresh failed:', error); 97 // Fallback: try to refresh individual cards 98 this.fallbackCardRefresh(); 99 throw error; 100 }); 101 } 102 103 /** 104 * Fallback card refresh when full refresh fails 105 */ 106 fallbackCardRefresh() { 107 console.log('🔄 Attempting fallback card refresh...'); 108 // Force re-initialization of cards 109 this.initializeCards(); 110 111 // Show notification to user 112 this.showNotification('Dashboard updated. If you don\'t see changes, please refresh the page.', 'info'); 84 113 } 85 114 -
synoveo/trunk/assets/js/dashboard-capabilities.js
r3375534 r3375574 129 129 } 130 130 showError('Network error loading capabilities'); 131 // Return rejected promise to allow calling code to handle the error 132 return Promise.reject(error); 131 133 }); 132 134 } -
synoveo/trunk/assets/js/gbp-compare.js
r3375504 r3375574 3 3 let currentSection = 'info'; 4 4 function $(sel){ return document.querySelector(sel); } 5 6 // WordPress security compliant helper function 7 function safeSetContent(element, content, isHTML = false) { 8 if (isHTML) { 9 // Only use innerHTML for trusted content (no user input) 10 element.innerHTML = content; 11 } else { 12 // Use textContent for user input to prevent XSS 13 element.textContent = content; 14 } 15 } 5 16 6 17 async function wpAjax(action, data={}){ … … 22 33 if(!json || json.success !== true) { 23 34 // Handle both old and new error formats 24 let msg = ` HTTP ${res.status}`;35 let msg = `Failed to load data: HTTP ${res.status}`; 25 36 if (json) { 26 37 if (json.data && json.data.message) { … … 28 39 } else if (json.error) { 29 40 msg = typeof json.error === 'string' ? json.error : (json.error.message || JSON.stringify(json.error)); 30 } 31 } 41 } else if (json.data && typeof json.data === 'string') { 42 msg = json.data; 43 } 44 } 45 46 // Add helpful context for common errors 47 if (msg.includes('HTTP 200') || msg.includes('Failed to load data')) { 48 msg += '\n\nEnsure your Synoveo API key is valid and Google connection is active.'; 49 } 50 32 51 throw new Error(msg); 33 52 } … … 875 894 currentSection = section || currentSection || 'info'; 876 895 const body = $('#'+modalId+' #synoveo-gbp-compare-body'); 877 body.innerHTML = '<div style="padding:16px; text-align:center; color:#64748b;">Loading…</div>'; 896 // Create loading element using DOM methods (WordPress security compliant) 897 const loadingDiv = document.createElement('div'); 898 loadingDiv.style.cssText = 'padding:16px; text-align:center; color:#64748b;'; 899 loadingDiv.textContent = 'Loading…'; 900 body.innerHTML = ''; 901 body.appendChild(loadingDiv); 878 902 try{ 879 903 const [wpSnap, gbp] = await Promise.all([ … … 1284 1308 } 1285 1309 }catch(e){ 1286 body.innerHTML = `<div style="padding:16px;color:#ef4444;">Failed to load data: ${escapeHtml(e.message)}<br/><br/>Ensure your Synoveo API key is valid and Google connection is active.</div>`; 1310 console.error('❌ GBP Compare load error:', e); 1311 console.error('❌ Error details:', { 1312 message: e.message, 1313 stack: e.stack, 1314 section: currentSection 1315 }); 1316 1317 // Show user-friendly error message 1318 let errorMessage = escapeHtml(e.message); 1319 if (errorMessage.includes('HTTP 200')) { 1320 errorMessage = 'Failed to load Google Business Profile data. This usually means the API call succeeded but returned an error response.'; 1321 } 1322 1323 // Create error container using DOM methods (WordPress security compliant) 1324 const errorContainer = document.createElement('div'); 1325 errorContainer.style.cssText = 'padding:16px;color:#ef4444;'; 1326 1327 // Create heading 1328 const heading = document.createElement('h3'); 1329 heading.style.cssText = 'margin:0 0 8px 0;color:#dc2626;'; 1330 heading.textContent = '⚠️ Unable to Load Data'; 1331 errorContainer.appendChild(heading); 1332 1333 // Create error message 1334 const errorPara = document.createElement('p'); 1335 errorPara.style.cssText = 'margin:0 0 12px 0;'; 1336 errorPara.textContent = errorMessage; 1337 errorContainer.appendChild(errorPara); 1338 1339 // Create troubleshooting box 1340 const troubleshootingBox = document.createElement('div'); 1341 troubleshootingBox.style.cssText = 'background:#fef2f2;border:1px solid #fecaca;border-radius:6px;padding:12px;margin:12px 0;'; 1342 1343 const troubleshootingTitle = document.createElement('strong'); 1344 troubleshootingTitle.textContent = 'Troubleshooting:'; 1345 troubleshootingBox.appendChild(troubleshootingTitle); 1346 1347 const troubleshootingList = document.createElement('ul'); 1348 troubleshootingList.style.cssText = 'margin:8px 0 0 0;padding-left:20px;'; 1349 1350 const troubleshootingItems = [ 1351 'Ensure your Synoveo API key is valid', 1352 'Verify Google Business Profile connection is active', 1353 'Check that your business has a valid Google Business Profile', 1354 'Try refreshing the page and opening the modal again' 1355 ]; 1356 1357 troubleshootingItems.forEach(itemText => { 1358 const listItem = document.createElement('li'); 1359 listItem.textContent = itemText; 1360 troubleshootingList.appendChild(listItem); 1361 }); 1362 1363 troubleshootingBox.appendChild(troubleshootingList); 1364 errorContainer.appendChild(troubleshootingBox); 1365 1366 // Create close button 1367 const closeButton = document.createElement('button'); 1368 closeButton.style.cssText = 'background:#ef4444;color:white;border:none;padding:8px 16px;border-radius:4px;cursor:pointer;'; 1369 closeButton.textContent = 'Close'; 1370 closeButton.addEventListener('click', () => { 1371 if (window.SynoveoGbpCompare && typeof window.SynoveoGbpCompare.close === 'function') { 1372 window.SynoveoGbpCompare.close(); 1373 } 1374 }); 1375 errorContainer.appendChild(closeButton); 1376 1377 // Clear body and append error container 1378 body.innerHTML = ''; 1379 body.appendChild(errorContainer); 1287 1380 } 1288 1381 } … … 1414 1507 } 1415 1508 1416 // Refresh dashboard to update quota 1509 // Refresh dashboard to update quota with proper error handling 1417 1510 if (typeof loadCapabilities === 'function') { 1418 loadCapabilities(); 1511 console.log('🔄 Refreshing dashboard after sync...'); 1512 loadCapabilities() 1513 .then(() => { 1514 console.log('✅ Dashboard refreshed successfully'); 1515 // Force re-render of cards if card manager is available 1516 if (window.synoveoCardManager && typeof window.synoveoCardManager.refreshAllCards === 'function') { 1517 window.synoveoCardManager.refreshAllCards() 1518 .then(() => { 1519 console.log('✅ Cards refreshed successfully'); 1520 }) 1521 .catch(err => { 1522 console.warn('⚠️ Card refresh failed:', err); 1523 }); 1524 } 1525 }) 1526 .catch(error => { 1527 console.error('❌ Dashboard refresh failed:', error); 1528 // Show user-friendly error message 1529 if (window.synoveoCardManager) { 1530 window.synoveoCardManager.showNotification('Sync completed but dashboard refresh failed. Please refresh the page to see updates.', 'info'); 1531 } 1532 }); 1419 1533 } 1420 1534 }, 4000); -
synoveo/trunk/includes/admin/class-synoveo-admin-controller.php
r3375269 r3375574 1487 1487 1488 1488 if ( ! $business_id ) { 1489 SYNOVEO_Logger::debug( 'GBP Profile: No business ID found', 'Admin' ); 1489 1490 wp_send_json_error( array( 'message' => __( 'Failed to initialize business. Please check API connection.', 'synoveo' ) ) ); 1490 1491 return; 1491 1492 } 1492 1493 1494 SYNOVEO_Logger::debug( "GBP Profile: Fetching profile for business ID: {$business_id}", 'Admin' ); 1493 1495 $api_response = $this->api_service->call_api( 'google-business/profile', array( 'businessId' => $business_id ) ); 1496 SYNOVEO_Logger::debug( 'GBP Profile API response: ' . wp_json_encode( $api_response ), 'Admin' ); 1494 1497 1495 1498 if ( $api_response['success'] ) { -
synoveo/trunk/includes/class-synoveo-rate-limiter.php
r3375269 r3375574 19 19 private const PER_100_SECONDS_LIMIT = 200; // vs Google's 250 20 20 private const PER_USER_LIMIT = 20; // vs Google's 25 21 private const BURST_LIMIT = 10; // Max burst requests21 private const BURST_LIMIT = 20; // Max burst requests 22 22 23 23 // Cache keys -
synoveo/trunk/readme.txt
r3375534 r3375574 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1.0. 6a7 Stable tag: 1.0.7 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 253 253 254 254 == Changelog == 255 256 = 1.0.7 = 257 * **ENHANCEMENT:** Improved error handling in capability card manager with clearer AJAX failure feedback 258 * **ENHANCEMENT:** Added fallback mechanism for card refresh when primary refresh fails 259 * **ENHANCEMENT:** Enhanced user notifications in dashboard capabilities for graceful network error handling 260 * **SECURITY:** Introduced security-compliant content setting function in GBP compare to prevent XSS vulnerabilities 261 * **BUGFIX:** Fixed "Too many requests in burst" error when opening GBP Compare after connection 262 * **ENHANCEMENT:** Increased burst limit from 10 to 20 requests for better user workflow support 263 * **UX:** Users can now immediately use GBP Compare after connecting to Google Business Profile 255 264 256 265 = 1.0.6a = -
synoveo/trunk/synoveo.php
r3375534 r3375574 4 4 * Plugin URI: https://www.synoveo.com 5 5 * Description: Grow revenue by improving your online business presence. Synoveo keeps your Google Business Profile accurate and up-to-date, driving more calls, bookings, visits, and sales. 6 * Version: 1.0. 6a6 * Version: 1.0.7 7 7 * Author: Synoveo (CODE75) 8 8 * Author URI: https://www.synoveo.com … … 24 24 // Plugin constants 25 25 // Single source of truth for version number 26 $base_version = '1.0. 5'; // ← Change only this when bumping version26 $base_version = '1.0.7'; // ← Change only this when bumping version 27 27 define( 'SYNOVEO_VERSION', defined( 'WP_DEBUG' ) && WP_DEBUG ? $base_version . '-dev-' . time() : $base_version ); 28 28 define( 'SYNOVEO_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
Note: See TracChangeset
for help on using the changeset viewer.