diff --git a/OpenVBX/controllers/audiofiles.php b/OpenVBX/controllers/audiofiles.php index 3e38ac3d..fbd92237 100644 --- a/OpenVBX/controllers/audiofiles.php +++ b/OpenVBX/controllers/audiofiles.php @@ -243,8 +243,17 @@ function add_from_twilio_recording() function prompt_for_recording_twiml() { - validate_rest_request(); - + if(!validate_rest_request()) { + $response = new TwimlResponse; + $response->say('Could not validate this request. Goodbye.', array( + 'voice' => $ci->vbx_settings->get('voice', $ci->tenant->id), + 'language' => $ci->vbx_settings->get('voice_language', $ci->tenant->id) + )); + $response->hangup(); + $response->respond(); + exit; + } + $response = new TwimlResponse; $audioFile = VBX_Audio_File::get(array('recording_call_sid' => $this->input->get_post('CallSid'))); @@ -266,8 +275,17 @@ function prompt_for_recording_twiml() function replay_recording_twiml() { - validate_rest_request(); - + if(!validate_rest_request()) { + $response = new TwimlResponse; + $response->say('Could not validate this request. Goodbye.', array( + 'voice' => $ci->vbx_settings->get('voice', $ci->tenant->id), + 'language' => $ci->vbx_settings->get('voice_language', $ci->tenant->id) + )); + $response->hangup(); + $response->respond(); + exit; + } + $response = new TwimlResponse; if ($this->input->get_post('RecordingUrl')) @@ -294,8 +312,17 @@ function replay_recording_twiml() function accept_or_reject_recording_twiml() { - validate_rest_request(); - + if(!validate_rest_request()) { + $response = new TwimlResponse; + $response->say('Could not validate this request. Goodbye.', array( + 'voice' => $ci->vbx_settings->get('voice', $ci->tenant->id), + 'language' => $ci->vbx_settings->get('voice_language', $ci->tenant->id) + )); + $response->hangup(); + $response->respond(); + exit; + } + $response = new TwimlResponse; $digits = clean_digits($this->input->get_post('Digits')); $call_sid = $this->input->get_post('CallSid'); diff --git a/OpenVBX/controllers/numbers.php b/OpenVBX/controllers/numbers.php index 4629ba4e..da3092f0 100644 --- a/OpenVBX/controllers/numbers.php +++ b/OpenVBX/controllers/numbers.php @@ -421,4 +421,4 @@ public function refresh_select() { $this->respond('', 'dialer/numbers', $response); } -} \ No newline at end of file +} diff --git a/OpenVBX/controllers/twiml.php b/OpenVBX/controllers/twiml.php index e3a2261f..f10d455e 100644 --- a/OpenVBX/controllers/twiml.php +++ b/OpenVBX/controllers/twiml.php @@ -74,7 +74,12 @@ function index() function start_sms($flow_id) { - validate_rest_request(); + if(!validate_rest_request()) { + $response = new TwimlResponse; + $response->message('Could not validate this request. Goodbye.'); + $response->respond(); + exit; + } log_message("info", "Calling SMS Flow $flow_id"); $body = $this->input->get_post('Body'); @@ -104,8 +109,17 @@ function start_sms($flow_id) function start_voice($flow_id) { - validate_rest_request(); - + if(!validate_rest_request()) { + $response = new TwimlResponse; + $response->say('Could not validate this request. Goodbye.', array( + 'voice' => $ci->vbx_settings->get('voice', $ci->tenant->id), + 'language' => $ci->vbx_settings->get('voice_language', $ci->tenant->id) + )); + $response->hangup(); + $response->respond(); + exit; + } + log_message("info", "Calling Voice Flow $flow_id"); $this->flow_type = 'voice'; diff --git a/OpenVBX/helpers/token_helper.php b/OpenVBX/helpers/token_helper.php new file mode 100644 index 00000000..366bfb0b --- /dev/null +++ b/OpenVBX/helpers/token_helper.php @@ -0,0 +1,67 @@ +load->helper('format_helper'); + +function token_replace($input, $reset = false) { + static $tokens = null; + + if(!isset($tokens) || $reset){ + $tokens = array(); + $token_names = token_names(); + foreach ($token_names as $token) { + if(isset($_REQUEST[$token])) { + $tokens['{' . $token . '}'] = $_REQUEST[$token]; + switch ($token) { + case 'To': + case 'From': + $tokens['{' . $token . '_Formatted}'] = format_phone($_REQUEST[$token]); + break; + default: + } + } + } + } + + return strtr($input, $tokens); +} + +function token_list() { + $token_list = ''; + foreach(token_names() as $token){ + $token_list .= "
  • {{$token}}
  • "; + } + return ""; +} + +function token_names(){ + return array( + 'CallSid', + 'AccountSid', + 'From', + 'To', + 'CallStatus', + 'ApiVersion', + 'Direction', + 'ForwardedFrom', + 'CallerName', + 'FromCity', + 'FromState', + 'FromZip', + 'FromCountry', + 'ToCity', + 'ToState', + 'ToZip', + 'ToCountry', + 'DialCallStatus', + 'DialCallSid', + 'DialCallDuration', + 'RecordingUrl', + + 'MessageSid', + 'Body', + 'NumMedia', + 'MediaContentType1', + 'MediaUrl1', + ); +} diff --git a/OpenVBX/helpers/twilio_helper.php b/OpenVBX/helpers/twilio_helper.php index 092ef36f..5cd732db 100644 --- a/OpenVBX/helpers/twilio_helper.php +++ b/OpenVBX/helpers/twilio_helper.php @@ -61,23 +61,13 @@ function generate_capability_token($allow_incoming = true) { * @param string $failure_message * @return void */ - function validate_rest_request($failure_message = 'Could not validate this request. Goodbye.') { + function validate_rest_request() { $ci =& get_instance(); if ($ci->tenant->type == VBX_Settings::AUTH_TYPE_CONNECT) { - return; - } - - if (!OpenVBX::validateRequest()) { - $response = new TwimlResponse; - $response->say($failure_message, array( - 'voice' => $ci->vbx_settings->get('voice', $ci->tenant->id), - 'language' => $ci->vbx_settings->get('voice_language', $ci->tenant->id) - )); - $response->hangup(); - $response->respond(); - exit; + return true; } + return OpenVBX::validateRequest(); } } diff --git a/OpenVBX/libraries/User_Controller.php b/OpenVBX/libraries/User_Controller.php index a33076ae..9a44120e 100644 --- a/OpenVBX/libraries/User_Controller.php +++ b/OpenVBX/libraries/User_Controller.php @@ -52,6 +52,7 @@ public function __construct() $this->load->model('vbx_rest_access'); $this->load->model('vbx_message'); $this->load->model('vbx_incoming_numbers'); + $this->load->model('vbx_outgoing_caller_ids'); $this->load->model('vbx_device'); // When we're in testing mode, allow access to set Hiccup configuration @@ -373,6 +374,8 @@ protected function get_twilio_numbers() { /* Retrieve twilio numbers w/o sandbox */ $numbers = $this->vbx_incoming_numbers->get_numbers(); + $callerIds = $this->vbx_outgoing_caller_ids->get_caller_ids(); + $numbers = array_merge($numbers, $callerIds); } catch(VBX_IncomingNumberException $e) { @@ -380,6 +383,12 @@ protected function get_twilio_numbers() throw new User_ControllerException($e->getMessage()); /* Silent fail */ } + catch (VBX_OutgoingCallerIdException $e) + { + error_log($e->getMessage()); + throw new User_ControllerException($e->getMessage()); + /* Silent fail */ + } return $numbers; } diff --git a/OpenVBX/models/vbx_outgoing_caller_ids.php b/OpenVBX/models/vbx_outgoing_caller_ids.php new file mode 100644 index 00000000..a70be8e0 --- /dev/null +++ b/OpenVBX/models/vbx_outgoing_caller_ids.php @@ -0,0 +1,88 @@ +api_cache->get($cache_key, __CLASS__, $ci->tenant->id)) + { + return $cache; + } + + $caller_ids = array(); + try { + $account = OpenVBX::getAccount(); + foreach ($account->outgoing_caller_ids as $caller_id) + { + // check that caller_id is a proper instance type + $caller_ids[] = $this->parseOutgoingCallerId($caller_id); + } + } + catch (Exception $e) { + $msg = 'Unable to fetch Numbers: '; + switch ($e->getCode()) + { + case 20003: + $msg .= 'Authentication Failed.'; + break; + default: + $msg .= $e->getMessage(); + } + throw new VBX_OutgoingCallerIdException($msg, $e->getCode()); + } + + $ci->api_cache->set('outgoing-caller-ids', $caller_ids, __CLASS__, $ci->tenant->id); + + return $caller_ids; + } + + private function parseOutgoingCallerId($item) + { + $num = new stdClass(); + $num->id = $item->sid; + $num->name = $item->friendly_name; + $num->phone = format_phone($item->phone_number); + $num->phone_number = $item->phone_number; + $num->capabilities = new stdClass(); + $num->capabilities->voice = true; + $num->capabilities->sms = false; + + return $num; + } + + protected function clear_cache() + { + $ci =& get_instance(); + $ci->api_cache->invalidate(__CLASS__, $ci->tenant->id); + } +} \ No newline at end of file diff --git a/assets/c/applet.css b/assets/c/applet.css index 23e1c97e..61857ff0 100755 --- a/assets/c/applet.css +++ b/assets/c/applet.css @@ -650,7 +650,7 @@ .radio-table .radio-table-row.on .content-cell { background-color: #e5f6ff; } .radio-table .radio-table-row.off .content-cell { background-color: #eee; } -.radio-table .radio-table-row.fist .content-cell { +.radio-table .radio-table-row.first .content-cell { -moz-border-radius-topright: 4px; /* FF1+ */ -webkit-border-top-right-radius: 4px; /* Saf3+, Chrome */ -khtml-border-top-right-radius: 4px; /* Konqueror */ diff --git a/plugins/sms/applets/sms/script.js b/plugins/sms/applets/sms/script.js new file mode 100644 index 00000000..feff4bba --- /dev/null +++ b/plugins/sms/applets/sms/script.js @@ -0,0 +1,10 @@ +$(document).ready(function(){ + var app = $('.flow-instance.standard---sms'); + + $('.radio-table .radio-cell input', app).live('click', function(event) { + var table = $(event.target).closest('.radio-table'); + var table_row = $(event.target).closest('.radio-table-row'); + $('.radio-table-row', table).removeClass('on').addClass('off'); + table_row.removeClass('off').addClass('on'); + }); +}); \ No newline at end of file diff --git a/plugins/sms/applets/sms/twiml.php b/plugins/sms/applets/sms/twiml.php index 08728c81..b5ab0ddb 100644 --- a/plugins/sms/applets/sms/twiml.php +++ b/plugins/sms/applets/sms/twiml.php @@ -1,18 +1,72 @@ load->helper('format_helper'); +$ci->load->helper('token_helper'); +$ci->load->library('DialList'); + $sms = AppletInstance::getValue('sms'); $next = AppletInstance::getDropZoneUrl('next'); +$message_whom_selector = AppletInstance::getValue('message-whom-selector', 'caller'); +$message_whom_user_or_group = AppletInstance::getUserGroupPickerValue('message-whom-user-or-group'); +$message_whom_number = AppletInstance::getValue('message-whom-number'); +$from_number = AppletInstance::getValue('from-number', null); + +switch($message_whom_selector) { + case 'user-or-group': + // create a dial list from the input state + $dial_list = DialList::get($message_whom_user_or_group); + + while ($device = $dial_list->next()) + { + if ($device instanceof VBX_Device && $device->sms) + { + if (strpos($device->value, 'client:') !== false) + { + $to_number = str_replace('client:', '', $device->value); + } + else + { + $to_number = $device->value; + } + break; + } + } + break; + case 'number': + $to_number = normalize_phone_to_E164($message_whom_number); + break; + case 'caller': + default: + $to_number = $_REQUEST['From']; +} + +if($from_number == '') { + $from_number = $_REQUEST['From']; +} +else if($from_number == 'called') { + $from_number = $_REQUEST['To']; +} + +$sms = token_replace($sms); $response = new TwimlResponse; +$message_opts = array( + 'to' => $to_number, + 'from' => $from_number, +); + // Call flows still use the legacy TwiML // for sending messages during calls. if(AppletInstance::getFlowType() == 'voice') { - $response->sms($sms); + $response->sms($sms, $message_opts); } else { - $response->message($sms); + $response->message($sms, $message_opts); } if(!empty($next)) @@ -20,4 +74,4 @@ $response->redirect($next); } -$response->respond(); \ No newline at end of file +$response->respond(); diff --git a/plugins/sms/applets/sms/ui.php b/plugins/sms/applets/sms/ui.php index c39392f9..aeaf5dbe 100644 --- a/plugins/sms/applets/sms/ui.php +++ b/plugins/sms/applets/sms/ui.php @@ -1,14 +1,105 @@ +load->model('vbx_incoming_numbers'); + $ci->load->model('vbx_outgoing_caller_ids'); + $ci->load->helper('token_helper'); + + try { + $numbers = $ci->vbx_incoming_numbers->get_numbers(); + + } + catch (VBX_IncomingNumberException $e) { + log_message('Incoming numbers exception: '.$e->getMessage().' :: '.$e->getCode()); + $numbers = array(); + } + + try { + $outgoingCallerIds = $ci->vbx_outgoing_caller_ids->get_caller_ids(); + } + catch (VBX_OutgoingCallerIdException $e) { + log_message('Outgoing callerids exception: '.$e->getMessage().' :: '.$e->getCode()); + $outgoingCallerIds = array(); + } + + $number_options = array( + '' => "Caller's Number", + 'called' => 'Called Number', + ); + + foreach($numbers as $number) { + $number_options['Incoming Numbers'][$number->phone_number] = $number->phone . ' <' . $number->name . '>'; + } + foreach($outgoingCallerIds as $outgoingCallerId) { + $number_options['Verified Numbers'][$outgoingCallerId->phone_number] = $outgoingCallerId->phone . ' <' . $outgoingCallerId->name . '>'; + } + + $from_number = AppletInstance::getValue('from-number', null); + $message_whom_selector = AppletInstance::getValue('message-whom-selector', 'caller'); + +?>
    -

    Send a text message to the caller if they're on the mobile phone.

    +

    Send a text message to the caller if they're on a mobile phone.

    -

    Send a text message to the sender.

    +

    Send a text message to the sender.

    -

    Note, not currently supported on Toll Free Numbers

    + +
    +

    Message Whom

    +
    + + + + + + + + + + + + + +
    + /> + +

    Message Caller

    +
    + /> + +

    Message a user or group

    + +
    + /> + +

    Message phone number

    +
    + +
    +
    +
    + +
    +

    From Number

    +
    +
    + 'from-number', + 'class' => 'medium', + ); + echo t_form_dropdown($params, $number_options, $from_number); + ?> +
    +
    + +
    +

    Message

    +

    Text can include any of the following tokens: +

    -

    Next

    After the message is sent, continue to the next applet

    diff --git a/plugins/standard/applets/dial/TwimlDial.php b/plugins/standard/applets/dial/TwimlDial.php index 4f0df900..ed9752dd 100644 --- a/plugins/standard/applets/dial/TwimlDial.php +++ b/plugins/standard/applets/dial/TwimlDial.php @@ -53,10 +53,14 @@ public function __construct($settings = array()) $this->version = AppletInstance::getValue('version', null); $this->callerId = AppletInstance::getValue('callerId', null); - if (empty($this->callerId) && !empty($_REQUEST['From'])) + if (empty($this->callerId) && !empty($_REQUEST['From'])) { $this->callerId = $_REQUEST['From']; } + else if($this->callerId == 'called' && !empty($_REQUEST['To'])) + { + $this->callerId = $_REQUEST['To']; + } /* Get current instance */ $this->dial_whom_selector = AppletInstance::getValue('dial-whom-selector'); diff --git a/plugins/standard/applets/dial/ui.php b/plugins/standard/applets/dial/ui.php index 1ee977b1..f8fcdf2d 100644 --- a/plugins/standard/applets/dial/ui.php +++ b/plugins/standard/applets/dial/ui.php @@ -1,15 +1,36 @@ load->model('vbx_incoming_numbers'); - + $ci->load->model('vbx_outgoing_caller_ids'); + try { $numbers = $ci->vbx_incoming_numbers->get_numbers(); } catch (VBX_IncomingNumberException $e) { - log_message('Incoming numbers exception: '.$e->getMessage.' :: '.$e->getCode()); + log_message('Incoming numbers exception: '.$e->getMessage().' :: '.$e->getCode()); $numbers = array(); } - + + try { + $outgoingCallerIds = $ci->vbx_outgoing_caller_ids->get_caller_ids(); + } + catch (VBX_OutgoingCallerIdException $e) { + log_message('Outgoing callerids exception: '.$e->getMessage().' :: '.$e->getCode()); + $outgoingCallerIds = array(); + } + + $number_options = array( + '' => "Caller's Number", + 'called' => 'Called Number', + ); + + foreach($numbers as $number) { + $number_options['Incoming Numbers'][$number->phone_number] = $number->phone . ' <' . $number->name . '>'; + } + foreach($outgoingCallerIds as $outgoingCallerId) { + $number_options['Verified Numbers'][$outgoingCallerId->phone_number] = $outgoingCallerId->phone . ' <' . $outgoingCallerId->name . '>'; + } + $callerId = AppletInstance::getValue('callerId', null); $version = AppletInstance::getValue('version', null); @@ -69,12 +90,13 @@

    Caller ID

    - + 'callerId', + 'class' => 'medium', + ); + echo t_form_dropdown($params, $number_options, $from_number); + ?>
    diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php index 044c426e..826d5484 100644 --- a/system/helpers/form_helper.php +++ b/system/helpers/form_helper.php @@ -312,8 +312,7 @@ function form_dropdown($name = '', $options = array(), $selected = array(), $ext $key = (string) $key; $key = htmlspecialchars($key); - $val = htmlspecialchars($val); - + if (is_array($val)) { $form .= ''."\n"; @@ -322,7 +321,7 @@ function form_dropdown($name = '', $options = array(), $selected = array(), $ext { $optgroup_key = htmlspecialchars($optgroup_key); $optgroup_val = htmlspecialchars($optgroup_val); - + $sel = (in_array($optgroup_key, $selected)) ? ' selected="selected"' : ''; $form .= '\n"; @@ -332,6 +331,8 @@ function form_dropdown($name = '', $options = array(), $selected = array(), $ext } else { + $val = htmlspecialchars($val); + $sel = (in_array($key, $selected)) ? ' selected="selected"' : ''; $form .= '\n"; @@ -1030,4 +1031,4 @@ function &_get_validation_object() /* End of file form_helper.php */ -/* Location: ./system/helpers/form_helper.php */ \ No newline at end of file +/* Location: ./system/helpers/form_helper.php */