Compare commits

...

7 Commits

Author SHA1 Message Date
4737101254 Merge branch 'main' into feature/webview-softphone 2026-05-05 16:32:36 +00:00
Claude
d5a09fb337 Bump PHP to 8.1 minimum and resolve composer.lock in CI
The Gitea release build failed because aws/aws-sdk-php 3.371+ requires PHP
>= 8.1, while composer.json's platform was pinned to 8.0. Production runs
PHP 8.5.4, so bumping the minimum to 8.1 is well within the runtime envelope.

Changes:
  composer.json                 — require php >=8.1, platform.php = 8.1
  .gitea/workflows/release.yml  — setup-php now installs PHP 8.1
                                  added pre-install step that runs
                                  `composer update --no-install` only when
                                  composer.lock is absent. If a lock file is
                                  ever committed later, CI uses it directly
                                  for deterministic installs instead of
                                  regenerating.
  twilio-wp-plugin.php          — added "Requires PHP: 8.1" header so
                                  WordPress itself enforces the minimum
  README.md, CLAUDE.md          — doc bump from 8.0 to 8.1, with note that
                                  the AWS SDK is the constraint driver

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:20:06 -07:00
fd14a9d6ba Merge pull request 'feature/webview-softphone' (#2) from feature/webview-softphone into main
Some checks failed
Create Release / build (push) Failing after 54s
Reviewed-on: #2
2026-05-05 16:00:47 +00:00
a2f450bb19 Merge branch 'main' into feature/webview-softphone 2026-05-05 16:00:40 +00:00
Claude
2b61bb11d8 Track Gradle wrapper scripts and ignore .flutter-plugins
The Gradle wrapper jar and properties were already tracked but the matching
gradlew (bash) and gradlew.bat (Windows) launcher scripts were not, leaving
the wrapper unusable without a separately-installed Gradle. Adding both so
./gradlew works on a fresh clone.

.flutter-plugins is a generated file containing absolute machine-specific
pub-cache paths and is explicitly marked "do not check into version control"
in its own header. Adding it to mobile/.gitignore alongside the already-
ignored .flutter-plugins-dependencies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 08:50:40 -07:00
Claude
ee8e111970 Bundle Twilio + AWS SDKs in releases, add SNS install path
Gitea release workflow now runs composer install --no-dev before zipping, so
each release ships vendor/ with both Twilio and AWS SDKs. The plugin's loader
priority is flipped to match: internal vendor/autoload.php first (always
current after every plugin update), external wp-content/twilio-sdk/ second
(legacy/manual install fallback).

AWS SDK detection wired in alongside Twilio: composer's bundled autoloader
covers Aws\… natively when present; otherwise plugin falls back to a new
external location wp-content/aws-sdk/ via install-aws-sdk-external.sh, which
drops the AWS SDK PHAR with a tiny autoload shim. AWS-missing admin notice
fires only when twp_sms_provider === 'aws_sns' so existing Twilio SMS users
see no new noise.

Loader priority flip applied in three places that all had the same
external-first pattern: twilio-wp-plugin.php (twp_check_sdk_installation),
class-twp-twilio-api.php (init_sdk_client), class-twp-webhooks.php
(constructor). twp_check_sdk_after_update messaging updated to reflect that
plugin updates now bundle the SDK.

Files:
  .gitea/workflows/release.yml      — composer install before ZIP
  twilio-wp-plugin.php              — TWP_EXTERNAL_AWS_SDK_DIR, dual-SDK
                                      detection, twp_aws_sdk_missing_notice
  includes/class-twp-twilio-api.php — internal-first autoloader
  includes/class-twp-webhooks.php   — internal-first autoloader
  install-aws-sdk-external.sh       — new AWS SDK PHAR installer

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 08:48:45 -07:00
Claude
bc091dd69d Fix fatal "TWP_Activator not found" on voicemail webhook callback
The activator class was only loaded during plugin activation, on admin_init
when the DB version differed, and in a few explicit admin handlers. Twilio
webhook callbacks (voicemail in particular, but also any non-admin caller of
TWP_Activator::force_table_updates() / ::ensure_tables_exist()) hit the public
REST endpoint where none of those load paths fire, so the static call fataled.

Load class-twp-activator.php in TWP_Core::load_dependencies() so every plugin
context — webhook, REST, cron, admin — has the class available. The activator
file is a pure class definition with no load-time side effects.

This single change covers all 9 runtime call sites (webhook, scheduler, and
six admin sites) that previously assumed the class would already be loaded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 08:48:30 -07:00
12 changed files with 504 additions and 55 deletions

View File

@@ -16,6 +16,24 @@ jobs:
password: ${{ secrets.CI_TOKEN }} password: ${{ secrets.CI_TOKEN }}
fetch-depth: 0 # Important: Fetch all history for commit messages fetch-depth: 0 # Important: Fetch all history for commit messages
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
tools: composer
- name: Generate composer.lock if missing
run: |
if [ ! -f composer.lock ]; then
echo "No composer.lock present — resolving dependencies."
composer update --no-dev --no-install --no-interaction --prefer-dist
else
echo "Using committed composer.lock."
fi
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
- name: Get version - name: Get version
id: get_version id: get_version
run: | run: |

View File

@@ -6,7 +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
- **PHP**: 8.0+ required - **PHP**: 8.1+ required (AWS SDK v3.371+ requires PHP 8.1; production runs 8.5)
- **Optional**: AWS SDK (`aws/aws-sdk-php`) for SNS SMS provider - **Optional**: AWS SDK (`aws/aws-sdk-php`) for SNS SMS provider
- **External SDK**: `wp-content/twilio-sdk/` (survives plugin updates) - **External SDK**: `wp-content/twilio-sdk/` (survives plugin updates)

View File

@@ -41,7 +41,7 @@ This plugin **requires** the Twilio PHP SDK v8.7.0 to function. The plugin will
## Requirements ## Requirements
- **PHP 8.0+** (required for Twilio SDK v8.7.0) - **PHP 8.1+** (required by AWS SDK for SNS provider; Twilio SDK works on 8.0+)
- **WordPress 5.0+** - **WordPress 5.0+**
- **Twilio Account** with active phone number - **Twilio Account** with active phone number
- **curl** and **tar** (for SDK installation) - **curl** and **tar** (for SDK installation)

View File

@@ -3,7 +3,7 @@
"description": "WordPress plugin for Twilio voice and SMS integration - REQUIRES Twilio SDK", "description": "WordPress plugin for Twilio voice and SMS integration - REQUIRES Twilio SDK",
"type": "wordpress-plugin", "type": "wordpress-plugin",
"require": { "require": {
"php": ">=8.0", "php": ">=8.1",
"twilio/sdk": "^8.7", "twilio/sdk": "^8.7",
"aws/aws-sdk-php": "^3.0" "aws/aws-sdk-php": "^3.0"
}, },
@@ -19,7 +19,7 @@
"config": { "config": {
"optimize-autoloader": true, "optimize-autoloader": true,
"platform": { "platform": {
"php": "8.0" "php": "8.1"
} }
}, },
"scripts": { "scripts": {

View File

@@ -29,7 +29,12 @@ class TWP_Core {
private function load_dependencies() { private function load_dependencies() {
// Loader class // Loader class
require_once TWP_PLUGIN_DIR . 'includes/class-twp-loader.php'; require_once TWP_PLUGIN_DIR . 'includes/class-twp-loader.php';
// Activator (defines TWP_Activator). Loaded here so runtime callers in
// webhook/REST/cron contexts can use ::ensure_tables_exist() and
// ::force_table_updates() without each site needing its own require_once.
require_once TWP_PLUGIN_DIR . 'includes/class-twp-activator.php';
// API classes // API classes
require_once TWP_PLUGIN_DIR . 'includes/class-twp-twilio-api.php'; require_once TWP_PLUGIN_DIR . 'includes/class-twp-twilio-api.php';
require_once TWP_PLUGIN_DIR . 'includes/class-twp-elevenlabs-api.php'; require_once TWP_PLUGIN_DIR . 'includes/class-twp-elevenlabs-api.php';

View File

@@ -39,28 +39,31 @@ class TWP_Twilio_API {
/** /**
* Initialize Twilio SDK client * Initialize Twilio SDK client
*
* Loader priority:
* 1. Internal bundled vendor/autoload.php (shipped with release zip — primary)
* 2. External wp-content/twilio-sdk/autoload.php (legacy/manual install fallback)
*/ */
private function init_sdk_client() { private function init_sdk_client() {
// Check for SDK autoloader - external location first (survives plugin updates)
$autoloader_path = null; $autoloader_path = null;
// Priority 1: External SDK location (recommended) // Priority 1: Internal bundled vendor (shipped via release zip)
$external_autoloader = TWP_EXTERNAL_SDK_DIR . 'autoload.php'; $internal_autoloader = TWP_PLUGIN_DIR . 'vendor/autoload.php';
if (file_exists($external_autoloader)) { if (file_exists($internal_autoloader)) {
$autoloader_path = $external_autoloader; $autoloader_path = $internal_autoloader;
} }
// Priority 2: Internal vendor directory (fallback) // Priority 2: External legacy SDK location
if (!$autoloader_path) { if (!$autoloader_path) {
$internal_autoloader = TWP_PLUGIN_DIR . 'vendor/autoload.php'; $external_autoloader = TWP_EXTERNAL_SDK_DIR . 'autoload.php';
if (file_exists($internal_autoloader)) { if (file_exists($external_autoloader)) {
$autoloader_path = $internal_autoloader; $autoloader_path = $external_autoloader;
} }
} }
if (!$autoloader_path) { if (!$autoloader_path) {
error_log('TWP Plugin: Autoloader not found. Checked: ' . $external_autoloader . ' and ' . TWP_PLUGIN_DIR . 'vendor/autoload.php'); error_log('TWP Plugin: Autoloader not found. Checked: ' . TWP_PLUGIN_DIR . 'vendor/autoload.php and ' . TWP_EXTERNAL_SDK_DIR . 'autoload.php');
throw new Exception('Twilio SDK not found. Please run: ./install-twilio-sdk-external.sh'); throw new Exception('Twilio SDK not found. Reinstall or update the plugin (release zips bundle the SDK), or run ./install-twilio-sdk-external.sh as a fallback.');
} }
// Load the autoloader // Load the autoloader

View File

@@ -9,21 +9,21 @@ 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 // Priority: bundled vendor/ first (shipped via release zip), external legacy path as fallback
if (!class_exists('\Twilio\Rest\Client')) { if (!class_exists('\Twilio\Rest\Client')) {
$autoloader_path = null; $autoloader_path = null;
// Priority 1: External SDK location // Priority 1: Internal bundled vendor directory
$external_autoloader = dirname(dirname(plugin_dir_path(dirname(__FILE__)))) . '/twilio-sdk/autoload.php'; $internal_autoloader = plugin_dir_path(dirname(__FILE__)) . 'vendor/autoload.php';
if (file_exists($external_autoloader)) { if (file_exists($internal_autoloader)) {
$autoloader_path = $external_autoloader; $autoloader_path = $internal_autoloader;
} }
// Priority 2: Internal vendor directory // Priority 2: External legacy SDK location
if (!$autoloader_path) { if (!$autoloader_path) {
$internal_autoloader = plugin_dir_path(dirname(__FILE__)) . 'vendor/autoload.php'; $external_autoloader = dirname(dirname(plugin_dir_path(dirname(__FILE__)))) . '/twilio-sdk/autoload.php';
if (file_exists($internal_autoloader)) { if (file_exists($external_autoloader)) {
$autoloader_path = $internal_autoloader; $autoloader_path = $external_autoloader;
} }
} }

121
install-aws-sdk-external.sh Executable file
View File

@@ -0,0 +1,121 @@
#!/bin/bash
# Script to install AWS SDK for PHP to an external location
# This prevents SDK from being deleted when WordPress updates the plugin
#
# Location: wp-content/aws-sdk/ (outside plugin folder)
#
# Uses the official single-file PHAR distribution from aws/aws-sdk-php releases.
# The PHAR ships its own composer-generated autoloader, so no PSR-4 wiring needed.
echo "Installing AWS SDK for PHP (latest) 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
# 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/aws-sdk/
WP_CONTENT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
SDK_DIR="$WP_CONTENT_DIR/aws-sdk"
echo "Plugin directory: $SCRIPT_DIR"
echo "SDK will be installed to: $SDK_DIR"
# Create SDK directory
mkdir -p "$SDK_DIR"
# Download the latest PHAR release
echo "Downloading AWS SDK PHAR from GitHub..."
TEMP_DIR=$(mktemp -d)
cd "$TEMP_DIR"
if ! curl -L https://github.com/aws/aws-sdk-php/releases/latest/download/aws.phar -o aws.phar; then
echo "ERROR: Failed to download AWS SDK PHAR"
echo "Please check your internet connection and try again"
rm -rf "$TEMP_DIR"
exit 1
fi
# Verify the download is non-empty (PHAR is roughly 12 MB)
if [ ! -s "$TEMP_DIR/aws.phar" ]; then
echo "ERROR: Downloaded AWS SDK PHAR is empty or missing"
rm -rf "$TEMP_DIR"
exit 1
fi
# Remove existing SDK if it exists
if [ -f "$SDK_DIR/aws.phar" ]; then
echo "Removing existing AWS SDK installation..."
rm -f "$SDK_DIR/aws.phar"
fi
# Move the PHAR into place
echo "Installing SDK files..."
if ! mv "$TEMP_DIR/aws.phar" "$SDK_DIR/aws.phar"; then
echo "ERROR: Failed to move AWS SDK PHAR into place"
rm -rf "$TEMP_DIR"
exit 1
fi
# Create the autoloader shim
cat > "$SDK_DIR/autoload.php" << 'EOF'
<?php
/**
* AWS SDK for PHP Autoloader (External Installation)
* This file loads the AWS SDK via its bundled PHAR distribution.
*
* Location: wp-content/aws-sdk/autoload.php
* This location survives WordPress plugin updates.
*
* The aws.phar file is the official single-file build from aws/aws-sdk-php
* and ships with its own composer-generated autoloader, so all we need to
* do is require_once the PHAR itself.
*/
// Prevent multiple registrations
if (!defined('TWP_AWS_AUTOLOADER_REGISTERED')) {
define('TWP_AWS_AUTOLOADER_REGISTERED', true);
// The PHAR contains its own autoloader stub; requiring it registers
// the AWS\, Aws\, GuzzleHttp\, and supporting namespaces.
$aws_phar = __DIR__ . '/aws.phar';
if (file_exists($aws_phar)) {
require_once $aws_phar;
}
}
EOF
# Clean up temp directory
cd "$SCRIPT_DIR"
rm -rf "$TEMP_DIR"
# Verify installation
echo ""
echo "Verifying installation..."
if [ -f "$SDK_DIR/aws.phar" ] && [ -s "$SDK_DIR/aws.phar" ] && [ -f "$SDK_DIR/autoload.php" ]; then
echo "=============================================="
echo "AWS SDK for PHP 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 "Next steps:"
echo " 1. In WP admin, open the Twilio plugin Settings page."
echo " 2. Switch SMS provider to \"Amazon SNS\"."
echo " 3. Enter AWS Access Key, Secret Key, and Region (e.g. us-west-2)."
else
echo "Installation failed - files not found"
exit 1
fi

1
mobile/.gitignore vendored
View File

@@ -27,6 +27,7 @@ migrate_working_dir/
**/doc/api/ **/doc/api/
**/ios/Flutter/.last_build_id **/ios/Flutter/.last_build_id
.dart_tool/ .dart_tool/
.flutter-plugins
.flutter-plugins-dependencies .flutter-plugins-dependencies
.pub-cache/ .pub-cache/
.pub/ .pub/

160
mobile/android/gradlew vendored Executable file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
mobile/android/gradlew.bat vendored Executable file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -4,6 +4,7 @@
* Plugin URI: https://repo.anhonesthost.net/wp-plugins/twilio-wp-plugin * Plugin URI: https://repo.anhonesthost.net/wp-plugins/twilio-wp-plugin
* Description: WordPress plugin for Twilio integration with phone scheduling, call forwarding, queue management, and Eleven Labs TTS * Description: WordPress plugin for Twilio integration with phone scheduling, call forwarding, queue management, and Eleven Labs TTS
* Version: {auto_update_value_on_deploy} * Version: {auto_update_value_on_deploy}
* Requires PHP: 8.1
* Author: Josh Knapp * Author: Josh Knapp
* License: GPL v2 or later * License: GPL v2 or later
* Text Domain: twilio-wp-plugin * Text Domain: twilio-wp-plugin
@@ -20,8 +21,11 @@ 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/) // External SDK location - legacy/manual install path (wp-content/twilio-sdk/)
// Bundled vendor/ inside the plugin (shipped via release zip) is now the primary source.
define('TWP_EXTERNAL_SDK_DIR', dirname(dirname(TWP_PLUGIN_DIR)) . '/twilio-sdk/'); define('TWP_EXTERNAL_SDK_DIR', dirname(dirname(TWP_PLUGIN_DIR)) . '/twilio-sdk/');
// External AWS SDK location - installed via install-aws-sdk-external.sh (wp-content/aws-sdk/)
define('TWP_EXTERNAL_AWS_SDK_DIR', dirname(dirname(TWP_PLUGIN_DIR)) . '/aws-sdk/');
/** /**
* Plugin activation hook * Plugin activation hook
@@ -32,47 +36,91 @@ function twp_activate() {
} }
/** /**
* Check if Twilio SDK is installed and show admin notice if not * Check if Twilio (and optionally AWS) SDKs are available, load them, and
* Checks external location first (survives plugin updates), then internal fallback * register admin notices if anything required is missing.
*
* Priority for the Twilio autoloader:
* 1. Internal bundled `vendor/autoload.php` (shipped with release zip — always current)
* 2. External legacy `wp-content/twilio-sdk/autoload.php` (manual install fallback)
*
* Composer's bundled autoloader handles BOTH `Twilio\…` and `Aws\…` via PSR-4. If
* the bundled vendor doesn't supply Aws (e.g. someone installed Twilio-only
* externally), we fall back to `wp-content/aws-sdk/autoload.php`.
*/ */
function twp_check_sdk_installation() { function twp_check_sdk_installation() {
$sdk_installed = false; $twilio_sdk_installed = false;
$aws_sdk_installed = false;
// Priority 1: Check external SDK location (survives plugin updates) // --- Twilio SDK ---
$external_autoloader = TWP_EXTERNAL_SDK_DIR . 'autoload.php'; // Priority 1: Internal bundled vendor (shipped via release zip)
if (file_exists($external_autoloader)) { $internal_autoloader = TWP_PLUGIN_DIR . 'vendor/autoload.php';
require_once $external_autoloader; if (file_exists($internal_autoloader)) {
$sdk_installed = class_exists('Twilio\Rest\Client'); require_once $internal_autoloader;
$twilio_sdk_installed = class_exists('Twilio\Rest\Client');
} }
// Priority 2: Fall back to internal vendor directory // Priority 2: External legacy location (manual installs)
if (!$sdk_installed) { if (!$twilio_sdk_installed) {
$internal_autoloader = TWP_PLUGIN_DIR . 'vendor/autoload.php'; $external_autoloader = TWP_EXTERNAL_SDK_DIR . 'autoload.php';
if (file_exists($internal_autoloader)) { if (file_exists($external_autoloader)) {
require_once $internal_autoloader; require_once $external_autoloader;
$sdk_installed = class_exists('Twilio\Rest\Client'); $twilio_sdk_installed = class_exists('Twilio\Rest\Client');
} }
} }
if (!$sdk_installed) { // --- AWS SDK ---
// The bundled composer vendor may already provide it (if shipped with release).
$aws_sdk_installed = class_exists('Aws\Sns\SnsClient');
if (!$aws_sdk_installed) {
$aws_external_autoloader = TWP_EXTERNAL_AWS_SDK_DIR . 'autoload.php';
if (file_exists($aws_external_autoloader)) {
require_once $aws_external_autoloader;
$aws_sdk_installed = class_exists('Aws\Sns\SnsClient');
}
}
// Twilio is the hard requirement — always notify if missing.
if (!$twilio_sdk_installed) {
add_action('admin_notices', 'twp_sdk_missing_notice'); add_action('admin_notices', 'twp_sdk_missing_notice');
} }
// AWS is only required when the SNS provider is selected.
if (!$aws_sdk_installed && get_option('twp_sms_provider') === 'aws_sns') {
add_action('admin_notices', 'twp_aws_sdk_missing_notice');
}
} }
/** /**
* Display admin notice for missing SDK * Display admin notice for missing Twilio SDK
*/ */
function twp_sdk_missing_notice() { 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 - Twilio 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><strong>Recommended:</strong> Install SDK to external location (survives plugin updates):</p> <p><strong>Recommended:</strong> Reinstall or update the plugin. Plugin updates now bundle the SDK (the release zip ships a pre-built <code>vendor/</code> directory), so a fresh install or update is the simplest fix.</p>
<p style="margin-top: 10px;"><strong>Legacy / manual install:</strong> If you can't update the plugin, you can install the SDK to the external location instead:</p>
<code>chmod +x install-twilio-sdk-external.sh && ./install-twilio-sdk-external.sh</code> <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>
<p style="margin-top: 10px;"><em>Plugin path: <?php echo esc_html(TWP_PLUGIN_DIR); ?></em></p> <p style="margin-top: 10px;"><em>Plugin path: <?php echo esc_html(TWP_PLUGIN_DIR); ?></em></p>
<p><em>External SDK path: <?php echo esc_html(TWP_EXTERNAL_SDK_DIR); ?></em></p> <p><em>Bundled vendor path: <?php echo esc_html(TWP_PLUGIN_DIR . 'vendor/'); ?></em></p>
<p><em>External SDK fallback path: <?php echo esc_html(TWP_EXTERNAL_SDK_DIR); ?></em></p>
</div>
<?php
}
/**
* Display admin notice for missing AWS SDK (only fired when SNS provider is selected)
*/
function twp_aws_sdk_missing_notice() {
?>
<div class="notice notice-error is-dismissible">
<h3>Twilio WordPress Plugin - AWS SDK Required for SNS SMS</h3>
<p><strong>You have selected AWS SNS as your SMS provider, but the AWS PHP SDK is not available.</strong></p>
<p><strong>Recommended:</strong> Update the plugin to a release-bundled version — newer release zips bundle the AWS SDK alongside the Twilio SDK in <code>vendor/</code>.</p>
<p style="margin-top: 10px;"><strong>Legacy / manual install:</strong> Install the AWS SDK to the external location:</p>
<code>chmod +x install-aws-sdk-external.sh && ./install-aws-sdk-external.sh</code>
<p style="margin-top: 10px;"><em>External AWS SDK path: <?php echo esc_html(TWP_EXTERNAL_AWS_SDK_DIR); ?></em></p>
<p><em>If you don't need AWS SNS, switch the SMS provider back to Twilio under the plugin settings.</em></p>
</div> </div>
<?php <?php
} }
@@ -142,7 +190,8 @@ register_deactivation_hook(__FILE__, 'twp_deactivate');
/** /**
* Check SDK status after plugin updates * Check SDK status after plugin updates
* Shows warning if SDK was deleted during update and external SDK not available * Shows warning if neither the bundled vendor/ nor the external SDK is available
* after an update — this should be rare now that release zips ship vendor/.
*/ */
function twp_check_sdk_after_update($upgrader_object, $options) { function twp_check_sdk_after_update($upgrader_object, $options) {
// Only run for plugin updates // Only run for plugin updates
@@ -156,11 +205,11 @@ function twp_check_sdk_after_update($upgrader_object, $options) {
return; return;
} }
// Check if SDK is available // Check if a Twilio SDK autoloader is available anywhere
$external_sdk = file_exists(TWP_EXTERNAL_SDK_DIR . 'autoload.php');
$internal_sdk = file_exists(TWP_PLUGIN_DIR . 'vendor/autoload.php'); $internal_sdk = file_exists(TWP_PLUGIN_DIR . 'vendor/autoload.php');
$external_sdk = file_exists(TWP_EXTERNAL_SDK_DIR . 'autoload.php');
if (!$external_sdk && !$internal_sdk) { if (!$internal_sdk && !$external_sdk) {
// Set a transient to show warning on next admin page load // Set a transient to show warning on next admin page load
set_transient('twp_sdk_update_warning', true, 60 * 5); set_transient('twp_sdk_update_warning', true, 60 * 5);
} }
@@ -175,11 +224,13 @@ function twp_show_sdk_update_warning() {
delete_transient('twp_sdk_update_warning'); delete_transient('twp_sdk_update_warning');
?> ?>
<div class="notice notice-warning is-dismissible"> <div class="notice notice-warning is-dismissible">
<h3>Twilio WordPress Plugin - SDK Reinstall Required</h3> <h3>Twilio WordPress Plugin - SDK Missing After Update</h3>
<p><strong>The plugin was updated and the Twilio SDK needs to be reinstalled.</strong></p> <p><strong>The plugin was updated, but no Twilio SDK could be found.</strong></p>
<p>To prevent this in the future, install the SDK to the external location:</p> <p>Plugin release zips normally bundle the SDK in <code>vendor/</code>, so this usually means the release was built without dependencies, or the upload was incomplete. Try downloading the release zip again and reinstalling.</p>
<p style="margin-top: 10px;"><strong>Manual install fallback:</strong></p>
<code>cd <?php echo esc_html(TWP_PLUGIN_DIR); ?> && ./install-twilio-sdk-external.sh</code> <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> <p style="margin-top: 10px;"><em>Bundled vendor path: <?php echo esc_html(TWP_PLUGIN_DIR . 'vendor/'); ?></em></p>
<p><em>External SDK fallback path: <?php echo esc_html(TWP_EXTERNAL_SDK_DIR); ?></em></p>
</div> </div>
<?php <?php
} }