Compare commits
5 Commits
2026.01.12
...
2026.01.24
| Author | SHA1 | Date | |
|---|---|---|---|
| 59df695530 | |||
| 03b6e5d70f | |||
| f8919af31a | |||
| 3e4dff5c4e | |||
| f0806d7e67 |
45
CLAUDE.md
45
CLAUDE.md
@@ -6,6 +6,7 @@
|
|||||||
- **URL**: `https://phone.cloud-hosting.io/`
|
- **URL**: `https://phone.cloud-hosting.io/`
|
||||||
- **Deployment**: rsync to Docker (remote server only, not local)
|
- **Deployment**: rsync to Docker (remote server only, not local)
|
||||||
- **SDK**: Twilio PHP SDK v8.7.0
|
- **SDK**: Twilio PHP SDK v8.7.0
|
||||||
|
- **External SDK**: `wp-content/twilio-sdk/` (survives plugin updates)
|
||||||
|
|
||||||
## Phone Variable Names
|
## Phone Variable Names
|
||||||
**Use**: `incoming_number`, `agent_number`, `customer_number`, `workflow_number`, `queue_number`, `default_number`
|
**Use**: `incoming_number`, `agent_number`, `customer_number`, `workflow_number`, `queue_number`, `default_number`
|
||||||
@@ -47,6 +48,25 @@ $api->update_call($customer_call_sid, ['twiml' => $twiml_xml]);
|
|||||||
- Firefox support added
|
- Firefox support added
|
||||||
- 1-min agent status auto-revert
|
- 1-min agent status auto-revert
|
||||||
|
|
||||||
|
## SDK Installation
|
||||||
|
- **External SDK (Recommended)**: Use `install-twilio-sdk-external.sh` to install SDK to `wp-content/twilio-sdk/`
|
||||||
|
- Survives WordPress plugin updates
|
||||||
|
- SDK location defined by `TWP_EXTERNAL_SDK_DIR` constant
|
||||||
|
- Loading priority: External first, then internal `vendor/` fallback
|
||||||
|
- **Internal SDK (Alternative)**: Use `install-twilio-sdk.sh` to install to `vendor/`
|
||||||
|
- Will be deleted when WordPress updates the plugin
|
||||||
|
- Requires reinstallation after each plugin update
|
||||||
|
- **SDK Loading**: Plugin checks external location first via autoloader, falls back to internal
|
||||||
|
- **Post-Update Detection**: Hook on `upgrader_process_complete` checks SDK status and shows warning
|
||||||
|
|
||||||
|
## Browser Phone Configuration
|
||||||
|
- **Edge Location Setting**: Configurable via Settings → Twilio Edge Location
|
||||||
|
- Default: `roaming` (auto-select closest edge)
|
||||||
|
- Options: ashburn, umatilla, dublin, frankfurt, singapore, sydney, tokyo, sao-paulo
|
||||||
|
- Stored in: `twp_twilio_edge` option
|
||||||
|
- Used by: Browser phone JavaScript for WebRTC connection
|
||||||
|
- Critical: Wrong edge causes immediate call failures (e.g., US calls with Sydney edge)
|
||||||
|
|
||||||
## Development Notes
|
## Development Notes
|
||||||
- **API**: E.164 format (+1XXXXXXXXXX)
|
- **API**: E.164 format (+1XXXXXXXXXX)
|
||||||
- **Database**: Use `$wpdb`, prepared statements
|
- **Database**: Use `$wpdb`, prepared statements
|
||||||
@@ -61,5 +81,28 @@ $api->update_call($customer_call_sid, ['twiml' => $twiml_xml]);
|
|||||||
- ElevenLabs TTS with Alice fallback
|
- ElevenLabs TTS with Alice fallback
|
||||||
- 68 AJAX actions, 26 REST endpoints
|
- 68 AJAX actions, 26 REST endpoints
|
||||||
|
|
||||||
|
## Recent Technical Changes (v2.8.9)
|
||||||
|
|
||||||
|
### SDK Persistence Between Plugin Updates
|
||||||
|
- **Problem**: WordPress plugin updates delete entire plugin folder including `vendor/` SDK
|
||||||
|
- **Solution**: External SDK installation at `wp-content/twilio-sdk/` survives updates
|
||||||
|
- **Implementation**:
|
||||||
|
- New constant: `TWP_EXTERNAL_SDK_DIR` points to `wp-content/twilio-sdk/`
|
||||||
|
- Loading priority in `twp_check_sdk_installation()`: External first, internal fallback
|
||||||
|
- Classes updated: `TWP_Twilio_API`, `TWP_Webhooks` constructors check external location first
|
||||||
|
- New script: `install-twilio-sdk-external.sh` automates external installation
|
||||||
|
- Post-update hook: `twp_check_sdk_after_update()` detects missing SDK after updates
|
||||||
|
- Admin notices: `twp_sdk_missing_notice()` shows both installation options
|
||||||
|
- Warning system: `twp_show_sdk_update_warning()` via transient after plugin updates
|
||||||
|
|
||||||
|
### US Calls Failing Fix (Browser Phone)
|
||||||
|
- **Problem**: Browser phone had hardcoded `edge: 'sydney'`, causing US calls to fail with immediate HANGUP
|
||||||
|
- **Solution**: Configurable edge location via WordPress settings
|
||||||
|
- **Implementation**:
|
||||||
|
- New setting: `twp_twilio_edge` with default value `roaming`
|
||||||
|
- Settings UI: Dropdown in admin settings with 8 edge options
|
||||||
|
- Browser phone JS: Uses `get_option('twp_twilio_edge', 'roaming')` instead of hardcoded value
|
||||||
|
- Edge options: roaming, ashburn, umatilla, dublin, frankfurt, singapore, sydney, tokyo, sao-paulo
|
||||||
|
|
||||||
---
|
---
|
||||||
*Updated: Sept 2025*
|
*Updated: Jan 2026*
|
||||||
427
DEBUGGING-TABLET.md
Normal file
427
DEBUGGING-TABLET.md
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
# Debugging Browser Phone on Android Tablet
|
||||||
|
|
||||||
|
This guide explains how to debug the Twilio WordPress Plugin browser phone on Android tablets (Samsung and other devices).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Android tablet with Chrome browser
|
||||||
|
- USB cable to connect tablet to computer
|
||||||
|
- Computer with Chrome browser installed
|
||||||
|
- USB debugging enabled on tablet
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 1: Enable USB Debugging on Android Tablet
|
||||||
|
|
||||||
|
### Step 1: Enable Developer Options
|
||||||
|
|
||||||
|
1. Open **Settings** on your Android tablet
|
||||||
|
2. Scroll down to **About tablet** (or **About device**)
|
||||||
|
3. Find **Build number** (may be under "Software information")
|
||||||
|
4. Tap **Build number** 7 times rapidly
|
||||||
|
5. You'll see a message: "You are now a developer!"
|
||||||
|
|
||||||
|
### Step 2: Enable USB Debugging
|
||||||
|
|
||||||
|
1. Go back to **Settings**
|
||||||
|
2. Scroll down to **Developer options** (newly appeared)
|
||||||
|
3. Toggle **Developer options** to ON
|
||||||
|
4. Find **USB debugging** in the list
|
||||||
|
5. Toggle **USB debugging** to ON
|
||||||
|
6. Confirm the prompt if asked
|
||||||
|
|
||||||
|
### Step 3: Trust Your Computer
|
||||||
|
|
||||||
|
1. Connect tablet to computer via USB cable
|
||||||
|
2. Unlock your tablet screen
|
||||||
|
3. A popup will appear: "Allow USB debugging?"
|
||||||
|
4. Check **Always allow from this computer**
|
||||||
|
5. Tap **OK** or **Allow**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 2: Connect Chrome DevTools
|
||||||
|
|
||||||
|
### On Your Computer
|
||||||
|
|
||||||
|
1. Open **Chrome browser** on your computer
|
||||||
|
2. Navigate to: `chrome://inspect`
|
||||||
|
3. You should see your tablet device listed under "Remote Target"
|
||||||
|
4. Wait a few seconds for the device to appear
|
||||||
|
|
||||||
|
### Inspect the Browser Phone Page
|
||||||
|
|
||||||
|
1. On your tablet, open Chrome and navigate to:
|
||||||
|
```
|
||||||
|
https://phone.cloud-hosting.io/wp-admin/admin.php?page=twilio-wp-browser-phone
|
||||||
|
```
|
||||||
|
2. On your computer's Chrome DevTools (`chrome://inspect`), you'll see the page listed
|
||||||
|
3. Click **inspect** next to the browser phone page
|
||||||
|
4. A DevTools window will open showing your tablet's browser
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 3: Debug Browser Phone Issues
|
||||||
|
|
||||||
|
### Check Console Logs
|
||||||
|
|
||||||
|
In the **Console** tab, look for key messages:
|
||||||
|
|
||||||
|
#### Successful Connection
|
||||||
|
```
|
||||||
|
Twilio SDK loaded successfully
|
||||||
|
Setting up Twilio Device...
|
||||||
|
Device detection - Android: true, Mobile: true
|
||||||
|
AudioContext created, state: running
|
||||||
|
Device registered successfully
|
||||||
|
Device connection state: connected
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connection Issues
|
||||||
|
```
|
||||||
|
Device not connected, state: disconnected
|
||||||
|
Failed to register device
|
||||||
|
Connection error: 31005
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Call Issues
|
||||||
|
```
|
||||||
|
Answer button clicked
|
||||||
|
Device connection state: connected
|
||||||
|
Accepting call...
|
||||||
|
Call accepted and connected
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitor Network Requests
|
||||||
|
|
||||||
|
1. Switch to **Network** tab in DevTools
|
||||||
|
2. Filter by: `twp_` or `twilio`
|
||||||
|
3. Check for failed requests (red status codes)
|
||||||
|
4. Look for:
|
||||||
|
- `twp_generate_capability_token` - Should return 200
|
||||||
|
- `twp_get_phone_numbers` - Should return 200
|
||||||
|
- WebSocket connections to Twilio
|
||||||
|
|
||||||
|
### Check Device Registration
|
||||||
|
|
||||||
|
In the **Console** tab, type:
|
||||||
|
```javascript
|
||||||
|
device
|
||||||
|
```
|
||||||
|
This shows the current Twilio Device object. Check:
|
||||||
|
- `device.state` - Should be "registered"
|
||||||
|
- `device.token` - Should exist (long string)
|
||||||
|
|
||||||
|
### Check AudioContext State
|
||||||
|
|
||||||
|
In the **Console** tab, type:
|
||||||
|
```javascript
|
||||||
|
audioContext
|
||||||
|
```
|
||||||
|
Check:
|
||||||
|
- Should exist (not null)
|
||||||
|
- `audioContext.state` - Should be "running" (not "suspended")
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 4: Common Issues & Solutions
|
||||||
|
|
||||||
|
### Issue 1: "Device not connected" Error
|
||||||
|
|
||||||
|
**Symptoms**: Status shows "Disconnected", calls hang up immediately
|
||||||
|
|
||||||
|
**Debugging**:
|
||||||
|
```javascript
|
||||||
|
// In console, check:
|
||||||
|
deviceConnectionState
|
||||||
|
// Should be: "connected"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Check network connection (try WiFi vs cellular)
|
||||||
|
2. Refresh the page with cache cleared
|
||||||
|
3. Check console for token errors
|
||||||
|
4. Verify Twilio credentials in plugin settings
|
||||||
|
|
||||||
|
### Issue 2: Call Hangs Up Immediately When Answered
|
||||||
|
|
||||||
|
**Symptoms**: Click "Answer" button, call disconnects instantly
|
||||||
|
|
||||||
|
**Debugging**:
|
||||||
|
```javascript
|
||||||
|
// Check device state before answering:
|
||||||
|
deviceConnectionState
|
||||||
|
// Check for errors in call handler
|
||||||
|
```
|
||||||
|
|
||||||
|
**Look for console errors**:
|
||||||
|
- `31005` - Connection/network error
|
||||||
|
- `31201` - ICE connection failure
|
||||||
|
- `31208` - Media connection error
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Grant microphone permissions (Settings > Site settings > Microphone)
|
||||||
|
2. Check AudioContext is running: `audioContext.state`
|
||||||
|
3. Try switching between WiFi and cellular data
|
||||||
|
4. Clear Chrome cache and reload
|
||||||
|
|
||||||
|
### Issue 3: No Sound/Ringtone on Incoming Call
|
||||||
|
|
||||||
|
**Symptoms**: Call arrives but no audio plays, no vibration
|
||||||
|
|
||||||
|
**Debugging**:
|
||||||
|
```javascript
|
||||||
|
// Check audio setup:
|
||||||
|
ringtoneAudio
|
||||||
|
audioContext.state
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Tap screen to unlock AudioContext (mobile restriction)
|
||||||
|
2. Check if ringtone file exists (optional, vibration is fallback)
|
||||||
|
3. Verify device supports Vibration API: `'vibrate' in navigator`
|
||||||
|
4. Check browser volume settings
|
||||||
|
|
||||||
|
### Issue 4: No Browser Notifications
|
||||||
|
|
||||||
|
**Symptoms**: No notification when call arrives in background
|
||||||
|
|
||||||
|
**Debugging**:
|
||||||
|
```javascript
|
||||||
|
// Check notification permission:
|
||||||
|
Notification.permission
|
||||||
|
// Should be: "granted"
|
||||||
|
|
||||||
|
// Check service worker:
|
||||||
|
navigator.serviceWorker.controller
|
||||||
|
// Should exist
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Grant notification permission: Settings > Site settings > Notifications
|
||||||
|
2. Check service worker registration in **Application** tab of DevTools
|
||||||
|
3. Look for service worker logs in console
|
||||||
|
4. Reinstall PWA if installed
|
||||||
|
|
||||||
|
### Issue 5: Microphone Permission Denied
|
||||||
|
|
||||||
|
**Symptoms**: Error message about microphone access
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
1. Chrome Settings > Site settings > Microphone
|
||||||
|
2. Find `phone.cloud-hosting.io`
|
||||||
|
3. Change permission to **Allow**
|
||||||
|
4. Refresh the browser phone page
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 5: Tablet-Specific Checks
|
||||||
|
|
||||||
|
### Check User Agent
|
||||||
|
|
||||||
|
In console:
|
||||||
|
```javascript
|
||||||
|
navigator.userAgent
|
||||||
|
```
|
||||||
|
Should include "Android" and "Chrome"
|
||||||
|
|
||||||
|
### Check WebRTC Support
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Check getUserMedia support:
|
||||||
|
navigator.mediaDevices.getUserMedia
|
||||||
|
// Should be: function
|
||||||
|
|
||||||
|
// Check Notification support:
|
||||||
|
'Notification' in window
|
||||||
|
// Should be: true
|
||||||
|
|
||||||
|
// Check Service Worker support:
|
||||||
|
'serviceWorker' in navigator
|
||||||
|
// Should be: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Audio Constraints
|
||||||
|
|
||||||
|
Look in console for:
|
||||||
|
```
|
||||||
|
Twilio Device created with audio constraints: {
|
||||||
|
echoCancellation: true,
|
||||||
|
noiseSuppression: true,
|
||||||
|
autoGainControl: true,
|
||||||
|
googEchoCancellation: true,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Vibration
|
||||||
|
|
||||||
|
In console:
|
||||||
|
```javascript
|
||||||
|
navigator.vibrate([300, 200, 300])
|
||||||
|
```
|
||||||
|
Tablet should vibrate if supported.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 6: Advanced Debugging
|
||||||
|
|
||||||
|
### Monitor Twilio Device Events
|
||||||
|
|
||||||
|
In console, add event listeners:
|
||||||
|
```javascript
|
||||||
|
device.on('registered', () => console.log('Device registered!'));
|
||||||
|
device.on('error', (error) => console.error('Device error:', error));
|
||||||
|
device.on('incoming', (call) => console.log('Incoming call:', call));
|
||||||
|
device.on('unregistered', () => console.log('Device unregistered'));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Call State During Active Call
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// When in a call:
|
||||||
|
currentCall
|
||||||
|
currentCall.status()
|
||||||
|
currentCall.parameters
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Audio Context Resume
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Try to resume manually:
|
||||||
|
audioContext.resume().then(() => {
|
||||||
|
console.log('AudioContext state:', audioContext.state);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Token Expiry
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// See when token expires:
|
||||||
|
new Date(tokenExpiry)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 7: Logging Important Information
|
||||||
|
|
||||||
|
### Collect Debugging Info
|
||||||
|
|
||||||
|
Run this in console to get a debug report:
|
||||||
|
```javascript
|
||||||
|
console.log('=== Browser Phone Debug Report ===');
|
||||||
|
console.log('User Agent:', navigator.userAgent);
|
||||||
|
console.log('Device State:', deviceConnectionState);
|
||||||
|
console.log('Device Registered:', device ? device.state : 'no device');
|
||||||
|
console.log('AudioContext:', audioContext ? audioContext.state : 'no context');
|
||||||
|
console.log('Notification Permission:', Notification.permission);
|
||||||
|
console.log('Service Worker:', navigator.serviceWorker.controller ? 'active' : 'inactive');
|
||||||
|
console.log('Current Call:', currentCall ? 'in call' : 'no call');
|
||||||
|
console.log('Token Expiry:', tokenExpiry ? new Date(tokenExpiry) : 'unknown');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check WordPress AJAX Responses
|
||||||
|
|
||||||
|
In **Network** tab:
|
||||||
|
1. Filter: `admin-ajax.php`
|
||||||
|
2. Click on request
|
||||||
|
3. Check **Preview** tab for response
|
||||||
|
4. Look for `success: true` or error messages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 8: Testing Checklist
|
||||||
|
|
||||||
|
After fixing issues, test these scenarios:
|
||||||
|
|
||||||
|
- [ ] Device shows "Connected" status
|
||||||
|
- [ ] Can make outbound call successfully
|
||||||
|
- [ ] Can receive incoming call (see notification)
|
||||||
|
- [ ] Can answer incoming call without hang-up
|
||||||
|
- [ ] Ringtone plays or tablet vibrates
|
||||||
|
- [ ] Call audio is clear (echo cancellation working)
|
||||||
|
- [ ] Can switch between WiFi and cellular during call
|
||||||
|
- [ ] Browser notification appears when app in background
|
||||||
|
- [ ] Service worker logs appear in console
|
||||||
|
- [ ] No errors in console during call lifecycle
|
||||||
|
- [ ] Can put call on hold
|
||||||
|
- [ ] Can transfer call
|
||||||
|
- [ ] Call timer updates correctly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting Specific Error Codes
|
||||||
|
|
||||||
|
### Twilio Error 31005
|
||||||
|
**Meaning**: WebSocket connection failed
|
||||||
|
|
||||||
|
**Causes**:
|
||||||
|
- Network connectivity issues
|
||||||
|
- Firewall blocking WebSocket
|
||||||
|
- Mobile network switching (WiFi ↔ cellular)
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
- Check internet connection
|
||||||
|
- Try different network
|
||||||
|
- Wait for ICE restart (enabled in config)
|
||||||
|
|
||||||
|
### Twilio Error 31201
|
||||||
|
**Meaning**: ICE connection failed
|
||||||
|
|
||||||
|
**Causes**:
|
||||||
|
- Restrictive NAT/firewall
|
||||||
|
- Mobile network issues
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
- Try different network
|
||||||
|
- Check WebRTC connectivity
|
||||||
|
- Enable mobile data if on WiFi only
|
||||||
|
|
||||||
|
### Twilio Error 31204
|
||||||
|
**Meaning**: Connection error
|
||||||
|
|
||||||
|
**Causes**:
|
||||||
|
- Media connection setup failed
|
||||||
|
- Signaling timeout
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
- Refresh page
|
||||||
|
- Check microphone permissions
|
||||||
|
- Verify audio constraints applied
|
||||||
|
|
||||||
|
### Twilio Error 31208
|
||||||
|
**Meaning**: Media connection failed
|
||||||
|
|
||||||
|
**Causes**:
|
||||||
|
- Microphone access denied
|
||||||
|
- Audio device issues
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
- Grant microphone permission
|
||||||
|
- Check device audio settings
|
||||||
|
- Restart browser
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
- Twilio Voice SDK Errors: https://www.twilio.com/docs/voice/sdks/javascript/errors
|
||||||
|
- Chrome DevTools Remote Debugging: https://developer.chrome.com/docs/devtools/remote-debugging/
|
||||||
|
- WebRTC Troubleshooting: https://webrtc.github.io/samples/
|
||||||
|
- Service Worker Debugging: https://developer.chrome.com/docs/workbox/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contact & Support
|
||||||
|
|
||||||
|
If issues persist after following this guide:
|
||||||
|
|
||||||
|
1. Collect debug report (see Part 7)
|
||||||
|
2. Take screenshots of console errors
|
||||||
|
3. Note tablet model and Android version
|
||||||
|
4. Report issue with collected information
|
||||||
|
|
||||||
|
**Important Files**:
|
||||||
|
- Browser phone implementation: `admin/class-twp-admin.php` (lines 7400-8300)
|
||||||
|
- Service worker: `assets/js/twp-service-worker.js`
|
||||||
|
- CLAUDE.md: Quick reference guide
|
||||||
62
README.md
62
README.md
@@ -8,11 +8,20 @@ This plugin **requires** the Twilio PHP SDK v8.7.0 to function. The plugin will
|
|||||||
|
|
||||||
## Quick Installation
|
## Quick Installation
|
||||||
|
|
||||||
1. **Install the Twilio SDK** (Required):
|
1. **Install the Twilio SDK** (Required - Recommended Method):
|
||||||
|
```bash
|
||||||
|
chmod +x install-twilio-sdk-external.sh
|
||||||
|
./install-twilio-sdk-external.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs the SDK to `wp-content/twilio-sdk/` which **survives WordPress plugin updates**. The plugin will automatically detect and use this external SDK.
|
||||||
|
|
||||||
|
**Alternative Method** (SDK will be deleted during plugin updates):
|
||||||
```bash
|
```bash
|
||||||
chmod +x install-twilio-sdk.sh
|
chmod +x install-twilio-sdk.sh
|
||||||
./install-twilio-sdk.sh
|
./install-twilio-sdk.sh
|
||||||
```
|
```
|
||||||
|
This installs the SDK inside the plugin folder. You'll need to reinstall the SDK after each plugin update.
|
||||||
|
|
||||||
2. **Test the SDK installation**:
|
2. **Test the SDK installation**:
|
||||||
```bash
|
```bash
|
||||||
@@ -23,6 +32,7 @@ This plugin **requires** the Twilio PHP SDK v8.7.0 to function. The plugin will
|
|||||||
- Go to **Twilio** → **Settings**
|
- Go to **Twilio** → **Settings**
|
||||||
- Enter Account SID and Auth Token
|
- Enter Account SID and Auth Token
|
||||||
- Configure default phone numbers
|
- Configure default phone numbers
|
||||||
|
- Set Twilio Edge Location (for browser phone - see Browser Phone Setup below)
|
||||||
|
|
||||||
4. **Set up Phone Numbers** in Twilio Console:
|
4. **Set up Phone Numbers** in Twilio Console:
|
||||||
- Configure webhook URLs for voice and SMS
|
- Configure webhook URLs for voice and SMS
|
||||||
@@ -333,8 +343,20 @@ Comprehensive redesign of hold, transfer, and requeue functionality with profess
|
|||||||
2. **Configure in WordPress**:
|
2. **Configure in WordPress**:
|
||||||
- Go to **Twilio** → **Settings**
|
- Go to **Twilio** → **Settings**
|
||||||
- Enter TwiML App SID
|
- Enter TwiML App SID
|
||||||
|
- **Set Twilio Edge Location**: Select the edge location closest to your users (IMPORTANT)
|
||||||
|
- **Auto-select closest (Recommended)**: Automatically selects the best edge
|
||||||
|
- **US East (Ashburn)**: For East Coast USA users
|
||||||
|
- **US West (Umatilla)**: For West Coast USA users
|
||||||
|
- **Europe - Ireland (Dublin)**: For European users
|
||||||
|
- **Europe - Germany (Frankfurt)**: For Central European users
|
||||||
|
- **Singapore**: For Southeast Asian users
|
||||||
|
- **Sydney**: For Australian users
|
||||||
|
- **Tokyo**: For Japanese users
|
||||||
|
- **Sao Paulo**: For South American users
|
||||||
- Save settings
|
- Save settings
|
||||||
|
|
||||||
|
**Note**: Selecting the wrong edge location can cause calls to fail immediately. If you're experiencing browser phone connection issues, verify your edge location is appropriate for your region.
|
||||||
|
|
||||||
3. **Access Browser Phone** (Admin Only):
|
3. **Access Browser Phone** (Admin Only):
|
||||||
- Navigate to **WordPress Admin** → **Twilio** → **Browser Phone**
|
- Navigate to **WordPress Admin** → **Twilio** → **Browser Phone**
|
||||||
- Select caller ID from available numbers
|
- Select caller ID from available numbers
|
||||||
@@ -432,14 +454,34 @@ Access the full browser phone interface at: **WordPress Admin → Twilio → Bro
|
|||||||
- **Login Required**: Users must be logged in to access browser phone functionality
|
- **Login Required**: Users must be logged in to access browser phone functionality
|
||||||
- Check TwiML App SID is configured in WordPress admin settings
|
- Check TwiML App SID is configured in WordPress admin settings
|
||||||
|
|
||||||
|
#### Browser Phone Calls Failing Immediately
|
||||||
|
If browser phone calls disconnect immediately or show HANGUP errors:
|
||||||
|
- **Check Edge Location Setting**: Go to **Twilio** → **Settings** → **Twilio Edge Location**
|
||||||
|
- **US Users**: Should use "Auto-select closest" (roaming), "US East (Ashburn)", or "US West (Umatilla)"
|
||||||
|
- **International Users**: Select the edge location closest to your region
|
||||||
|
- **Problem**: Wrong edge location causes gateway to immediately reject calls
|
||||||
|
- **Solution**: Change edge location setting and try the call again (no restart needed)
|
||||||
|
|
||||||
#### "Twilio SDK classes not available"
|
#### "Twilio SDK classes not available"
|
||||||
|
|
||||||
|
**Recommended Solution** (SDK survives plugin updates):
|
||||||
```bash
|
```bash
|
||||||
# Reinstall SDK
|
# Install SDK to external location
|
||||||
|
./install-twilio-sdk-external.sh
|
||||||
|
# Test installation
|
||||||
|
php test-sdk.php
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alternative Solution** (will need reinstall after plugin updates):
|
||||||
|
```bash
|
||||||
|
# Install SDK inside plugin folder
|
||||||
./install-twilio-sdk.sh
|
./install-twilio-sdk.sh
|
||||||
# Test installation
|
# Test installation
|
||||||
php test-sdk.php
|
php test-sdk.php
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**After WordPress Plugin Update**: If you get this error after updating the plugin and used the internal SDK method, you'll need to reinstall the SDK. This won't happen if you use the external SDK method.
|
||||||
|
|
||||||
#### Calls Not Routing to Queues
|
#### Calls Not Routing to Queues
|
||||||
- Verify queue exists and is active
|
- Verify queue exists and is active
|
||||||
- Check agent group has members
|
- Check agent group has members
|
||||||
@@ -551,7 +593,19 @@ All webhooks are REST API endpoints under `/wp-json/twilio-webhook/v1/`:
|
|||||||
|
|
||||||
## Version History
|
## Version History
|
||||||
|
|
||||||
### v2.3.0 (Current - September 2025) - ENTERPRISE READY
|
### v2.8.9 (Current - January 2026) - SDK PERSISTENCE & BROWSER PHONE FIXES
|
||||||
|
- **SDK PERSISTENCE**: External SDK installation option that survives WordPress plugin updates
|
||||||
|
- New installation script: `install-twilio-sdk-external.sh` installs SDK to `wp-content/twilio-sdk/`
|
||||||
|
- Automatic detection: Plugin checks external SDK location first, falls back to internal
|
||||||
|
- Post-update warnings: Notifies if SDK was deleted during plugin update
|
||||||
|
- Zero downtime: Phone system continues working through plugin updates
|
||||||
|
- **BROWSER PHONE FIX**: Resolved US calls failing immediately with HANGUP errors
|
||||||
|
- Made Twilio Edge Location configurable (was hardcoded to Sydney)
|
||||||
|
- New setting: Twilio Edge Location with 8 options (roaming/auto-select, ashburn, umatilla, dublin, frankfurt, singapore, sydney, tokyo, sao-paulo)
|
||||||
|
- Default: "roaming" (auto-select closest edge for optimal performance)
|
||||||
|
- Critical fix: US users can now make calls successfully (were failing with Sydney edge)
|
||||||
|
|
||||||
|
### v2.3.0 (September 2025) - ENTERPRISE READY
|
||||||
- **SECURITY ENHANCEMENT**: Removed frontend browser phone interface, moved to admin-only access for enhanced security
|
- **SECURITY ENHANCEMENT**: Removed frontend browser phone interface, moved to admin-only access for enhanced security
|
||||||
- **ASSET REDUCTION**: Eliminated 108KB of frontend assets (browser-phone-frontend.js and CSS files)
|
- **ASSET REDUCTION**: Eliminated 108KB of frontend assets (browser-phone-frontend.js and CSS files)
|
||||||
- **SHORTCODE SECURITY**: Browser phone shortcode now provides secure redirect with authentication checks
|
- **SHORTCODE SECURITY**: Browser phone shortcode now provides secure redirect with authentication checks
|
||||||
@@ -611,4 +665,4 @@ This plugin integrates with Twilio services and requires a Twilio account.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Enterprise Ready v2.3.0** - Extension transfers, browser phone compatibility, and automatic agent management now production-ready with comprehensive reliability improvements.
|
**Production Ready v2.8.9** - SDK persistence through plugin updates and configurable edge locations ensure zero-downtime phone operations.
|
||||||
@@ -340,13 +340,32 @@ class TWP_Admin {
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">TwiML App SID</th>
|
<th scope="row">TwiML App SID</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="twp_twiml_app_sid"
|
<input type="text" name="twp_twiml_app_sid"
|
||||||
value="<?php echo esc_attr(get_option('twp_twiml_app_sid')); ?>"
|
value="<?php echo esc_attr(get_option('twp_twiml_app_sid')); ?>"
|
||||||
class="regular-text" />
|
class="regular-text" />
|
||||||
<p class="description">TwiML Application SID for Browser Phone (optional). <a href="#twiml-app-instructions">See setup instructions below</a></p>
|
<p class="description">TwiML Application SID for Browser Phone (optional). <a href="#twiml-app-instructions">See setup instructions below</a></p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Twilio Edge Location</th>
|
||||||
|
<td>
|
||||||
|
<?php $current_edge = get_option('twp_twilio_edge', 'roaming'); ?>
|
||||||
|
<select name="twp_twilio_edge" class="regular-text">
|
||||||
|
<option value="roaming" <?php selected($current_edge, 'roaming'); ?>>Auto-select closest (Recommended)</option>
|
||||||
|
<option value="ashburn" <?php selected($current_edge, 'ashburn'); ?>>US East (Ashburn)</option>
|
||||||
|
<option value="umatilla" <?php selected($current_edge, 'umatilla'); ?>>US West (Umatilla)</option>
|
||||||
|
<option value="dublin" <?php selected($current_edge, 'dublin'); ?>>Europe - Ireland (Dublin)</option>
|
||||||
|
<option value="frankfurt" <?php selected($current_edge, 'frankfurt'); ?>>Europe - Germany (Frankfurt)</option>
|
||||||
|
<option value="singapore" <?php selected($current_edge, 'singapore'); ?>>Asia Pacific (Singapore)</option>
|
||||||
|
<option value="sydney" <?php selected($current_edge, 'sydney'); ?>>Australia (Sydney)</option>
|
||||||
|
<option value="tokyo" <?php selected($current_edge, 'tokyo'); ?>>Japan (Tokyo)</option>
|
||||||
|
<option value="sao-paulo" <?php selected($current_edge, 'sao-paulo'); ?>>South America (Sao Paulo)</option>
|
||||||
|
</select>
|
||||||
|
<p class="description">Edge location for browser phone calls. Use "Auto-select closest" for best call quality, or select a specific region.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Eleven Labs API Settings</h2>
|
<h2>Eleven Labs API Settings</h2>
|
||||||
@@ -3818,6 +3837,7 @@ class TWP_Admin {
|
|||||||
register_setting('twilio-wp-settings-group', 'twp_twilio_account_sid');
|
register_setting('twilio-wp-settings-group', 'twp_twilio_account_sid');
|
||||||
register_setting('twilio-wp-settings-group', 'twp_twilio_auth_token');
|
register_setting('twilio-wp-settings-group', 'twp_twilio_auth_token');
|
||||||
register_setting('twilio-wp-settings-group', 'twp_twiml_app_sid');
|
register_setting('twilio-wp-settings-group', 'twp_twiml_app_sid');
|
||||||
|
register_setting('twilio-wp-settings-group', 'twp_twilio_edge');
|
||||||
register_setting('twilio-wp-settings-group', 'twp_elevenlabs_api_key');
|
register_setting('twilio-wp-settings-group', 'twp_elevenlabs_api_key');
|
||||||
register_setting('twilio-wp-settings-group', 'twp_elevenlabs_voice_id');
|
register_setting('twilio-wp-settings-group', 'twp_elevenlabs_voice_id');
|
||||||
register_setting('twilio-wp-settings-group', 'twp_elevenlabs_model_id');
|
register_setting('twilio-wp-settings-group', 'twp_elevenlabs_model_id');
|
||||||
@@ -6996,7 +7016,8 @@ class TWP_Admin {
|
|||||||
<div class="phone-interface">
|
<div class="phone-interface">
|
||||||
<div class="phone-display">
|
<div class="phone-display">
|
||||||
<div id="phone-status">Ready</div>
|
<div id="phone-status">Ready</div>
|
||||||
<div id="device-connection-status" style="font-size: 12px; color: #999; margin-top: 5px;">Connecting...</div>
|
<div id="device-connection-status" style="font-size: 12px; color: #999; margin-top: 5px;">Loading...</div>
|
||||||
|
<div id="twp-debug-info" style="font-size: 10px; color: #666; margin-top: 3px;"></div>
|
||||||
<div id="phone-number-display"></div>
|
<div id="phone-number-display"></div>
|
||||||
<div id="call-timer" style="display: none;">00:00</div>
|
<div id="call-timer" style="display: none;">00:00</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -7654,6 +7675,7 @@ class TWP_Admin {
|
|||||||
|
|
||||||
// Initialize the browser phone
|
// Initialize the browser phone
|
||||||
function initializeBrowserPhone() {
|
function initializeBrowserPhone() {
|
||||||
|
debugLog('initializeBrowserPhone called');
|
||||||
$('#phone-status').text('Initializing...');
|
$('#phone-status').text('Initializing...');
|
||||||
updateConnectionStatus('connecting');
|
updateConnectionStatus('connecting');
|
||||||
|
|
||||||
@@ -7731,13 +7753,16 @@ class TWP_Admin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setupTwilioDevice(token) {
|
async function setupTwilioDevice(token) {
|
||||||
|
debugLog('setupTwilioDevice called');
|
||||||
try {
|
try {
|
||||||
// Check if Twilio SDK is available
|
// Check if Twilio SDK is available
|
||||||
|
debugLog('Twilio check: ' + (typeof Twilio) + ', Device: ' + (typeof Twilio !== 'undefined' ? typeof Twilio.Device : 'N/A'));
|
||||||
if (typeof Twilio === 'undefined' || !Twilio.Device) {
|
if (typeof Twilio === 'undefined' || !Twilio.Device) {
|
||||||
throw new Error('Twilio Voice SDK not loaded');
|
throw new Error('Twilio Voice SDK not loaded');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Setting up Twilio Device...');
|
console.log('Setting up Twilio Device...');
|
||||||
|
debugLog('Creating Twilio.Device...');
|
||||||
updateConnectionStatus('connecting');
|
updateConnectionStatus('connecting');
|
||||||
|
|
||||||
// Request media permissions before setting up device
|
// Request media permissions before setting up device
|
||||||
@@ -7779,7 +7804,7 @@ class TWP_Admin {
|
|||||||
device = new Twilio.Device(token, {
|
device = new Twilio.Device(token, {
|
||||||
logLevel: 1, // 0 = TRACE, 1 = DEBUG
|
logLevel: 1, // 0 = TRACE, 1 = DEBUG
|
||||||
codecPreferences: ['opus', 'pcmu'],
|
codecPreferences: ['opus', 'pcmu'],
|
||||||
edge: 'sydney', // Or closest edge location
|
edge: '<?php echo esc_js(get_option('twp_twilio_edge', 'roaming')); ?>',
|
||||||
enableIceRestart: true, // Important for mobile network switching
|
enableIceRestart: true, // Important for mobile network switching
|
||||||
audioConstraints: audioConstraints,
|
audioConstraints: audioConstraints,
|
||||||
maxCallSignalingTimeoutMs: 30000, // 30 seconds timeout for mobile
|
maxCallSignalingTimeoutMs: 30000, // 30 seconds timeout for mobile
|
||||||
@@ -7787,11 +7812,13 @@ class TWP_Admin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
console.log('Twilio Device created with audio constraints:', audioConstraints);
|
console.log('Twilio Device created with audio constraints:', audioConstraints);
|
||||||
|
debugLog('Device created, setting up handlers...');
|
||||||
|
|
||||||
// Set up event handlers BEFORE registering
|
// Set up event handlers BEFORE registering
|
||||||
// Device registered and ready
|
// Device registered and ready
|
||||||
device.on('registered', function() {
|
device.on('registered', function() {
|
||||||
console.log('Device registered successfully');
|
console.log('Device registered successfully');
|
||||||
|
debugLog('Device REGISTERED!');
|
||||||
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
||||||
$('#call-btn').prop('disabled', false);
|
$('#call-btn').prop('disabled', false);
|
||||||
updateConnectionStatus('connected');
|
updateConnectionStatus('connected');
|
||||||
@@ -7873,10 +7900,13 @@ class TWP_Admin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Register device AFTER setting up event handlers
|
// Register device AFTER setting up event handlers
|
||||||
|
debugLog('Calling device.register()...');
|
||||||
await device.register();
|
await device.register();
|
||||||
|
debugLog('device.register() completed');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error setting up Twilio Device:', error);
|
console.error('Error setting up Twilio Device:', error);
|
||||||
|
debugLog('ERROR: ' + error.message);
|
||||||
showError('Failed to setup device: ' + error.message);
|
showError('Failed to setup device: ' + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8212,17 +8242,51 @@ class TWP_Admin {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Debug helper
|
||||||
|
function debugLog(msg) {
|
||||||
|
console.log('TWP Debug: ' + msg);
|
||||||
|
var debugEl = $('#twp-debug-info');
|
||||||
|
if (debugEl.length) {
|
||||||
|
debugEl.append(msg + '<br>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if SDK loaded and initialize
|
// Check if SDK loaded and initialize
|
||||||
|
debugLog('jQuery ready');
|
||||||
|
|
||||||
|
// Don't wait for window.load - it may not fire on mobile
|
||||||
|
// Instead, poll for Twilio SDK availability
|
||||||
|
var sdkCheckAttempts = 0;
|
||||||
|
var maxSdkCheckAttempts = 50; // 5 seconds max
|
||||||
|
|
||||||
|
function checkAndInitialize() {
|
||||||
|
sdkCheckAttempts++;
|
||||||
|
debugLog('SDK check #' + sdkCheckAttempts + ': ' + (typeof Twilio));
|
||||||
|
|
||||||
|
if (typeof Twilio !== 'undefined' && Twilio.Device) {
|
||||||
|
console.log('Twilio SDK loaded successfully');
|
||||||
|
debugLog('SDK OK, initializing...');
|
||||||
|
initializeBrowserPhone();
|
||||||
|
} else if (sdkCheckAttempts < maxSdkCheckAttempts) {
|
||||||
|
// Keep checking every 100ms
|
||||||
|
setTimeout(checkAndInitialize, 100);
|
||||||
|
} else {
|
||||||
|
showError('Twilio Voice SDK failed to load. Please check your internet connection and try refreshing the page.');
|
||||||
|
console.error('Twilio SDK not found after ' + sdkCheckAttempts + ' attempts.');
|
||||||
|
debugLog('SDK FAILED after ' + sdkCheckAttempts + ' attempts');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start checking after a brief delay
|
||||||
|
setTimeout(checkAndInitialize, 500);
|
||||||
|
|
||||||
|
// Also keep the window.load as backup for desktop
|
||||||
$(window).on('load', function() {
|
$(window).on('load', function() {
|
||||||
setTimeout(function() {
|
debugLog('Window loaded (backup)');
|
||||||
if (typeof Twilio === 'undefined') {
|
if (typeof Twilio !== 'undefined' && !device) {
|
||||||
showError('Twilio Voice SDK failed to load. Please check your internet connection and try refreshing the page.');
|
debugLog('Backup init triggered');
|
||||||
console.error('Twilio SDK not found. Script may be blocked or failed to load.');
|
initializeBrowserPhone();
|
||||||
} else {
|
}
|
||||||
console.log('Twilio SDK loaded successfully');
|
|
||||||
initializeBrowserPhone();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clean up on page unload
|
// Clean up on page unload
|
||||||
|
|||||||
@@ -41,13 +41,28 @@ class TWP_Twilio_API {
|
|||||||
* Initialize Twilio SDK client
|
* Initialize Twilio SDK client
|
||||||
*/
|
*/
|
||||||
private function init_sdk_client() {
|
private function init_sdk_client() {
|
||||||
// Check if autoloader exists
|
// Check for SDK autoloader - external location first (survives plugin updates)
|
||||||
$autoloader_path = TWP_PLUGIN_DIR . 'vendor/autoload.php';
|
$autoloader_path = null;
|
||||||
if (!file_exists($autoloader_path)) {
|
|
||||||
error_log('TWP Plugin: Autoloader not found at: ' . $autoloader_path);
|
// Priority 1: External SDK location (recommended)
|
||||||
throw new Exception('Twilio SDK not found. Please run: ./install-twilio-sdk.sh');
|
$external_autoloader = TWP_EXTERNAL_SDK_DIR . 'autoload.php';
|
||||||
|
if (file_exists($external_autoloader)) {
|
||||||
|
$autoloader_path = $external_autoloader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Priority 2: Internal vendor directory (fallback)
|
||||||
|
if (!$autoloader_path) {
|
||||||
|
$internal_autoloader = TWP_PLUGIN_DIR . 'vendor/autoload.php';
|
||||||
|
if (file_exists($internal_autoloader)) {
|
||||||
|
$autoloader_path = $internal_autoloader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$autoloader_path) {
|
||||||
|
error_log('TWP Plugin: Autoloader not found. Checked: ' . $external_autoloader . ' and ' . TWP_PLUGIN_DIR . 'vendor/autoload.php');
|
||||||
|
throw new Exception('Twilio SDK not found. Please run: ./install-twilio-sdk-external.sh');
|
||||||
|
}
|
||||||
|
|
||||||
// Load the autoloader
|
// Load the autoloader
|
||||||
require_once $autoloader_path;
|
require_once $autoloader_path;
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,25 @@ class TWP_Webhooks {
|
|||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
// Load Twilio SDK if not already loaded
|
// Load Twilio SDK if not already loaded
|
||||||
|
// Check external location first (survives plugin updates), then internal
|
||||||
if (!class_exists('\Twilio\Rest\Client')) {
|
if (!class_exists('\Twilio\Rest\Client')) {
|
||||||
$autoloader_path = plugin_dir_path(dirname(__FILE__)) . 'vendor/autoload.php';
|
$autoloader_path = null;
|
||||||
if (file_exists($autoloader_path)) {
|
|
||||||
|
// Priority 1: External SDK location
|
||||||
|
$external_autoloader = dirname(dirname(plugin_dir_path(dirname(__FILE__)))) . '/twilio-sdk/autoload.php';
|
||||||
|
if (file_exists($external_autoloader)) {
|
||||||
|
$autoloader_path = $external_autoloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 2: Internal vendor directory
|
||||||
|
if (!$autoloader_path) {
|
||||||
|
$internal_autoloader = plugin_dir_path(dirname(__FILE__)) . 'vendor/autoload.php';
|
||||||
|
if (file_exists($internal_autoloader)) {
|
||||||
|
$autoloader_path = $internal_autoloader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($autoloader_path) {
|
||||||
require_once $autoloader_path;
|
require_once $autoloader_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
158
install-twilio-sdk-external.sh
Executable file
158
install-twilio-sdk-external.sh
Executable file
@@ -0,0 +1,158 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to install Twilio PHP SDK to an external location
|
||||||
|
# This prevents SDK from being deleted when WordPress updates the plugin
|
||||||
|
#
|
||||||
|
# Location: wp-content/twilio-sdk/ (outside plugin folder)
|
||||||
|
|
||||||
|
echo "Installing Twilio PHP SDK v8.7.0 to external location..."
|
||||||
|
echo "This will install the SDK outside the plugin folder to survive plugin updates."
|
||||||
|
|
||||||
|
# Check if we can download files
|
||||||
|
if ! command -v curl &> /dev/null; then
|
||||||
|
echo "ERROR: curl is required to download the SDK"
|
||||||
|
echo "Please install curl and try again"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v tar &> /dev/null; then
|
||||||
|
echo "ERROR: tar is required to extract the SDK"
|
||||||
|
echo "Please install tar and try again"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the script directory (plugin directory)
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Calculate wp-content directory (two levels up from plugin)
|
||||||
|
# Plugin is at: wp-content/plugins/twilio-wp-plugin/
|
||||||
|
# We want: wp-content/twilio-sdk/
|
||||||
|
WP_CONTENT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||||
|
SDK_DIR="$WP_CONTENT_DIR/twilio-sdk"
|
||||||
|
|
||||||
|
echo "Plugin directory: $SCRIPT_DIR"
|
||||||
|
echo "SDK will be installed to: $SDK_DIR"
|
||||||
|
|
||||||
|
# Create SDK directory
|
||||||
|
mkdir -p "$SDK_DIR/twilio/sdk"
|
||||||
|
|
||||||
|
# Download the latest release (8.7.0)
|
||||||
|
echo "Downloading Twilio SDK from GitHub..."
|
||||||
|
TEMP_DIR=$(mktemp -d)
|
||||||
|
cd "$TEMP_DIR"
|
||||||
|
|
||||||
|
if ! curl -L https://github.com/twilio/twilio-php/archive/refs/tags/8.7.0.tar.gz -o twilio-sdk.tar.gz; then
|
||||||
|
echo "ERROR: Failed to download Twilio SDK"
|
||||||
|
echo "Please check your internet connection and try again"
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the archive
|
||||||
|
echo "Extracting SDK files..."
|
||||||
|
if ! tar -xzf twilio-sdk.tar.gz; then
|
||||||
|
echo "ERROR: Failed to extract SDK files"
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the extracted directory exists
|
||||||
|
if [ ! -d "twilio-php-8.7.0/src" ]; then
|
||||||
|
echo "ERROR: Extracted SDK directory structure is unexpected"
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove existing SDK if it exists
|
||||||
|
if [ -d "$SDK_DIR/twilio/sdk" ]; then
|
||||||
|
echo "Removing existing SDK installation..."
|
||||||
|
rm -rf "$SDK_DIR/twilio/sdk"
|
||||||
|
mkdir -p "$SDK_DIR/twilio/sdk"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move the entire src directory to be the sdk
|
||||||
|
echo "Installing SDK files..."
|
||||||
|
if ! mv twilio-php-8.7.0/src/* "$SDK_DIR/twilio/sdk/"; then
|
||||||
|
echo "ERROR: Failed to move SDK files"
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a comprehensive autoloader
|
||||||
|
cat > "$SDK_DIR/autoload.php" << 'EOF'
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Twilio SDK v8.7.0 Autoloader (External Installation)
|
||||||
|
* This file loads the Twilio PHP SDK classes
|
||||||
|
*
|
||||||
|
* Location: wp-content/twilio-sdk/autoload.php
|
||||||
|
* This location survives WordPress plugin updates
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Prevent multiple registrations
|
||||||
|
if (!defined('TWILIO_AUTOLOADER_REGISTERED')) {
|
||||||
|
define('TWILIO_AUTOLOADER_REGISTERED', true);
|
||||||
|
|
||||||
|
// Register the autoloader
|
||||||
|
spl_autoload_register(function ($class) {
|
||||||
|
// Only handle Twilio classes
|
||||||
|
if (strpos($class, 'Twilio\\') !== 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert class name to file path
|
||||||
|
// The SDK structure is: twilio/sdk/Twilio/Rest/Client.php for Twilio\Rest\Client
|
||||||
|
$file = __DIR__ . '/twilio/sdk/' . str_replace('\\', '/', $class) . '.php';
|
||||||
|
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require_once $file;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to load the SDK's own autoloader if it exists
|
||||||
|
$sdk_autoloader = __DIR__ . '/twilio/sdk/autoload.php';
|
||||||
|
if (file_exists($sdk_autoloader)) {
|
||||||
|
require_once $sdk_autoloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load essential Twilio classes manually to ensure they're available
|
||||||
|
$essential_classes = [
|
||||||
|
__DIR__ . '/twilio/sdk/Twilio/Rest/Client.php',
|
||||||
|
__DIR__ . '/twilio/sdk/Twilio/TwiML/VoiceResponse.php',
|
||||||
|
__DIR__ . '/twilio/sdk/Twilio/Exceptions/TwilioException.php',
|
||||||
|
__DIR__ . '/twilio/sdk/Twilio/Security/RequestValidator.php'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($essential_classes as $class_file) {
|
||||||
|
if (file_exists($class_file)) {
|
||||||
|
require_once $class_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Clean up temp directory
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
echo ""
|
||||||
|
echo "Verifying installation..."
|
||||||
|
if [ -f "$SDK_DIR/autoload.php" ] && [ -d "$SDK_DIR/twilio/sdk" ]; then
|
||||||
|
echo "=============================================="
|
||||||
|
echo "Twilio SDK v8.7.0 installed successfully!"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
echo "Installation location: $SDK_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "This SDK is installed OUTSIDE the plugin folder,"
|
||||||
|
echo "so it will NOT be deleted when WordPress updates the plugin."
|
||||||
|
echo ""
|
||||||
|
echo "The plugin will automatically detect this external SDK."
|
||||||
|
else
|
||||||
|
echo "Installation failed - files not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -77,16 +77,16 @@ if (!defined('TWILIO_AUTOLOADER_REGISTERED')) {
|
|||||||
if (strpos($class, 'Twilio\\') !== 0) {
|
if (strpos($class, 'Twilio\\') !== 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert class name to file path
|
// Convert class name to file path
|
||||||
$relative_class = substr($class, 7); // Remove 'Twilio\'
|
// The SDK structure is: twilio/sdk/Twilio/Rest/Client.php for Twilio\Rest\Client
|
||||||
$file = __DIR__ . '/twilio/sdk/' . str_replace('\\', '/', $relative_class) . '.php';
|
$file = __DIR__ . '/twilio/sdk/' . str_replace('\\', '/', $class) . '.php';
|
||||||
|
|
||||||
if (file_exists($file)) {
|
if (file_exists($file)) {
|
||||||
require_once $file;
|
require_once $file;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -98,10 +98,10 @@ if (!defined('TWILIO_AUTOLOADER_REGISTERED')) {
|
|||||||
|
|
||||||
// Load essential Twilio classes manually to ensure they're available
|
// Load essential Twilio classes manually to ensure they're available
|
||||||
$essential_classes = [
|
$essential_classes = [
|
||||||
__DIR__ . '/twilio/sdk/Rest/Client.php',
|
__DIR__ . '/twilio/sdk/Twilio/Rest/Client.php',
|
||||||
__DIR__ . '/twilio/sdk/TwiML/VoiceResponse.php',
|
__DIR__ . '/twilio/sdk/Twilio/TwiML/VoiceResponse.php',
|
||||||
__DIR__ . '/twilio/sdk/Exceptions/TwilioException.php',
|
__DIR__ . '/twilio/sdk/Twilio/Exceptions/TwilioException.php',
|
||||||
__DIR__ . '/twilio/sdk/Security/RequestValidator.php'
|
__DIR__ . '/twilio/sdk/Twilio/Security/RequestValidator.php'
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($essential_classes as $class_file) {
|
foreach ($essential_classes as $class_file) {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ define('TWP_DB_VERSION', '1.6.2'); // Track database version separately
|
|||||||
define('TWP_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
define('TWP_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||||
define('TWP_PLUGIN_URL', plugin_dir_url(__FILE__));
|
define('TWP_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||||
define('TWP_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
define('TWP_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
||||||
|
// External SDK location - survives plugin updates (wp-content/twilio-sdk/)
|
||||||
|
define('TWP_EXTERNAL_SDK_DIR', dirname(dirname(TWP_PLUGIN_DIR)) . '/twilio-sdk/');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin activation hook
|
* Plugin activation hook
|
||||||
@@ -31,17 +33,27 @@ function twp_activate() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if Twilio SDK is installed and show admin notice if not
|
* Check if Twilio SDK is installed and show admin notice if not
|
||||||
|
* Checks external location first (survives plugin updates), then internal fallback
|
||||||
*/
|
*/
|
||||||
function twp_check_sdk_installation() {
|
function twp_check_sdk_installation() {
|
||||||
$autoloader_path = TWP_PLUGIN_DIR . 'vendor/autoload.php';
|
|
||||||
$sdk_installed = false;
|
$sdk_installed = false;
|
||||||
|
|
||||||
if (file_exists($autoloader_path)) {
|
// Priority 1: Check external SDK location (survives plugin updates)
|
||||||
// Try to load autoloader and check for classes
|
$external_autoloader = TWP_EXTERNAL_SDK_DIR . 'autoload.php';
|
||||||
require_once $autoloader_path;
|
if (file_exists($external_autoloader)) {
|
||||||
|
require_once $external_autoloader;
|
||||||
$sdk_installed = class_exists('Twilio\Rest\Client');
|
$sdk_installed = class_exists('Twilio\Rest\Client');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Priority 2: Fall back to internal vendor directory
|
||||||
|
if (!$sdk_installed) {
|
||||||
|
$internal_autoloader = TWP_PLUGIN_DIR . 'vendor/autoload.php';
|
||||||
|
if (file_exists($internal_autoloader)) {
|
||||||
|
require_once $internal_autoloader;
|
||||||
|
$sdk_installed = class_exists('Twilio\Rest\Client');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$sdk_installed) {
|
if (!$sdk_installed) {
|
||||||
add_action('admin_notices', 'twp_sdk_missing_notice');
|
add_action('admin_notices', 'twp_sdk_missing_notice');
|
||||||
}
|
}
|
||||||
@@ -55,10 +67,12 @@ function twp_sdk_missing_notice() {
|
|||||||
<div class="notice notice-error is-dismissible">
|
<div class="notice notice-error is-dismissible">
|
||||||
<h3>Twilio WordPress Plugin - SDK Required</h3>
|
<h3>Twilio WordPress Plugin - SDK Required</h3>
|
||||||
<p><strong>The Twilio PHP SDK is required for this plugin to work.</strong></p>
|
<p><strong>The Twilio PHP SDK is required for this plugin to work.</strong></p>
|
||||||
<p>To install the SDK, run this command in your plugin directory:</p>
|
<p><strong>Recommended:</strong> Install SDK to external location (survives plugin updates):</p>
|
||||||
|
<code>chmod +x install-twilio-sdk-external.sh && ./install-twilio-sdk-external.sh</code>
|
||||||
|
<p style="margin-top: 10px;"><strong>Alternative:</strong> Install SDK inside plugin folder:</p>
|
||||||
<code>chmod +x install-twilio-sdk.sh && ./install-twilio-sdk.sh</code>
|
<code>chmod +x install-twilio-sdk.sh && ./install-twilio-sdk.sh</code>
|
||||||
<p>Or install via Composer: <code>composer install</code></p>
|
<p style="margin-top: 10px;"><em>Plugin path: <?php echo esc_html(TWP_PLUGIN_DIR); ?></em></p>
|
||||||
<p><em>Plugin path: <?php echo TWP_PLUGIN_DIR; ?></em></p>
|
<p><em>External SDK path: <?php echo esc_html(TWP_EXTERNAL_SDK_DIR); ?></em></p>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
@@ -126,6 +140,52 @@ function twp_deactivate() {
|
|||||||
register_activation_hook(__FILE__, 'twp_activate');
|
register_activation_hook(__FILE__, 'twp_activate');
|
||||||
register_deactivation_hook(__FILE__, 'twp_deactivate');
|
register_deactivation_hook(__FILE__, 'twp_deactivate');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check SDK status after plugin updates
|
||||||
|
* Shows warning if SDK was deleted during update and external SDK not available
|
||||||
|
*/
|
||||||
|
function twp_check_sdk_after_update($upgrader_object, $options) {
|
||||||
|
// Only run for plugin updates
|
||||||
|
if ($options['action'] !== 'update' || $options['type'] !== 'plugin') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this plugin was updated
|
||||||
|
$updated_plugins = isset($options['plugins']) ? $options['plugins'] : array();
|
||||||
|
if (!in_array(TWP_PLUGIN_BASENAME, $updated_plugins)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if SDK is available
|
||||||
|
$external_sdk = file_exists(TWP_EXTERNAL_SDK_DIR . 'autoload.php');
|
||||||
|
$internal_sdk = file_exists(TWP_PLUGIN_DIR . 'vendor/autoload.php');
|
||||||
|
|
||||||
|
if (!$external_sdk && !$internal_sdk) {
|
||||||
|
// Set a transient to show warning on next admin page load
|
||||||
|
set_transient('twp_sdk_update_warning', true, 60 * 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('upgrader_process_complete', 'twp_check_sdk_after_update', 10, 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show SDK update warning
|
||||||
|
*/
|
||||||
|
function twp_show_sdk_update_warning() {
|
||||||
|
if (get_transient('twp_sdk_update_warning')) {
|
||||||
|
delete_transient('twp_sdk_update_warning');
|
||||||
|
?>
|
||||||
|
<div class="notice notice-warning is-dismissible">
|
||||||
|
<h3>Twilio WordPress Plugin - SDK Reinstall Required</h3>
|
||||||
|
<p><strong>The plugin was updated and the Twilio SDK needs to be reinstalled.</strong></p>
|
||||||
|
<p>To prevent this in the future, install the SDK to the external location:</p>
|
||||||
|
<code>cd <?php echo esc_html(TWP_PLUGIN_DIR); ?> && ./install-twilio-sdk-external.sh</code>
|
||||||
|
<p style="margin-top: 10px;">The external SDK at <code><?php echo esc_html(TWP_EXTERNAL_SDK_DIR); ?></code> survives plugin updates.</p>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('admin_notices', 'twp_show_sdk_update_warning');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core plugin class
|
* Core plugin class
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user