diff --git a/config/install/sms.gateway.config.yml b/config/install/sms.gateway.config.yml new file mode 100644 index 0000000..aa353e0 --- /dev/null +++ b/config/install/sms.gateway.config.yml @@ -0,0 +1,3 @@ +log: + enabled: true + plugin_id: log diff --git a/config/schema/sms.schema.yml b/config/schema/sms.schema.yml index d8ddd63..28d51f5 100644 --- a/config/schema/sms.schema.yml +++ b/config/schema/sms.schema.yml @@ -20,11 +20,30 @@ sms.settings: - type: string # Configuration for sms gateways. -sms.gateway.*: +sms.gateway.config: + type: sequence + label: 'All SMS Gateway configuration' + sequence: + - type: sms.gateway.setting + +sms.gateway.setting: type: mapping - label: 'SMS Gateway settings' + label: 'Single SMS Gateway configuration' mapping: - settings: + label: + type: label + label: 'Human readable gateway label' + name: + type: string + label: 'Gateway machine name' + plugin_id: + type: string + label: 'Gateway plugin type' + enabled: + type: boolean + label: 'Gateway enabled status' + custom: type: sequence + label: 'Additional gateway-specific configuration' sequence: - type: string diff --git a/sms.links.action.yml b/sms.links.action.yml index eec7b10..f4f6527 100644 --- a/sms.links.action.yml +++ b/sms.links.action.yml @@ -4,3 +4,10 @@ sms.carrier_add: weight: 1 appears_on: - sms.carrier_admin + +sms.gateway_add: + title: 'Add Configurable Gateway' + route_name: sms.gateway_add + weight: 1 + appears_on: + - sms.gateway_admin diff --git a/sms.module b/sms.module index edbd314..4db981b 100644 --- a/sms.module +++ b/sms.module @@ -20,7 +20,7 @@ define('SMS_CARRIER_OVERRIDDEN', 1); define('SMS_CARRIER_NORMAL', 3); use Drupal\sms\Gateway\GatewayInterface; -use Drupal\sms\Message\Message; +use Drupal\sms\Message\SmsMessage; /** * Implements hook_theme(). @@ -60,8 +60,8 @@ function sms_cron_queue_info() { * @see sms_handle_result(). */ function sms_send($number, $message, $options = array()) { - $sms = new Message($options['sender'], explode(',', $number), $message, $options); - return \Drupal::service('sms.provider')->send($sms, $options); + $sms = new SmsMessage($options['sender'], explode(',', $number), $message, $options); + return \Drupal::service('sms_provider.default')->send($sms, $options); } /** @@ -93,44 +93,17 @@ function sms_incoming_queue_worker(array $item) { * An array of additional options. */ function sms_incoming($number, $message, $options = array()) { - if (\Drupal::moduleHandler()->moduleExists('rules')) { - $options += array('number' => $number, 'message' => $message); - rules_invoke_event('sms_incoming', $options); - } - - // Execute three phases - \Drupal::moduleHandler()->invokeAll('sms_incoming', ['pre process', $number, $message, $options]); - \Drupal::moduleHandler()->invokeAll('sms_incoming', ['process', $number, $message, $options]); - \Drupal::moduleHandler()->invokeAll('sms_incoming', ['post process', $number, $message, $options]); + $sms = new SmsMessage($options['sender'], explode(',', $number), $message, $options); + \Drupal::service('sms_provider.default')->incoming($sms, $options); } /** * Handles responses to message transactions. * - * Allows gateways modules to pass message receipts and other responses to - * messages in a standard format for processing, and provides a basic set of - * status codes for common code handling. - * - * Allowed message status codes are defined as constants in this module. - * - * The gateway status code and message should be provided in the $options array - * as 'gateway_message_status' and 'gateway_message_status_text'. - * - * @param string $number - * The sender's mobile number. - * @param string $reference - * Unique message reference code, as provided when message is sent. - * @param int $message_status - * (optional) An SMS Framework message status code, per the defined constants. - * Defaults to SMS_GW_UNKNOWN_STATUS. - * @param array $options - * (optional) Extended options passed by the receipt receiver. + * @see \Drupal\sms\Gateway\SmsProviderInterface::receipt */ function sms_receipt($number, $reference, $message_status = GatewayInterface::STATUS_UNKNOWN, $options = array()) { - // Execute three phases - \Drupal::moduleHandler()->invokeAll('sms_receipt', 'pre process', $number, $reference, $message_status, $options); - \Drupal::moduleHandler()->invokeAll('sms_receipt', 'process', $number, $reference, $message_status, $options); - \Drupal::moduleHandler()->invokeAll('sms_receipt', 'post process', $number, $reference, $message_status, $options); + \Drupal::service('sms_provider.default')->incoming($number, $reference, $message_status, $options); } /** @@ -139,7 +112,7 @@ function sms_receipt($number, $reference, $message_status = GatewayInterface::ST * @return \Drupal\sms\Gateway\GatewayInterface */ function sms_default_gateway() { - return \Drupal::service('plugin.manager.smsgateway')->getDefaultGateway(); + return \Drupal::service('plugin.manager.sms_gateway')->getDefaultGateway(); } /** @@ -168,15 +141,15 @@ function sms_default_gateway_id() { * * @deprecated as of 8.x-1.x. Use @code GatewayManager @endcode methods instead. * @code - * Drupal::service('plugin.manager.smsgateway')->getAvailableGateways(); - * Drupal::service('plugin.manager.smsgateway')->getEnabledGateways(); - * Drupal::service('plugin.manager.smsgateway')->getDefaultGateway(); - * Drupal::service('plugin.manager.smsgateway')->getGateway($name); + * Drupal::service('plugin.manager.sms_gateway')->getAvailableGateways(); + * Drupal::service('plugin.manager.sms_gateway')->getEnabledGateways(); + * Drupal::service('plugin.manager.sms_gateway')->getDefaultGateway(); + * Drupal::service('plugin.manager.sms_gateway')->getGateway($name); * @endcode */ function sms_gateways($op = 'gateways', $gateway = NULL) { /** @var \Drupal\sms\Gateway\GatewayManagerInterface $gateway_manager */ - $gateway_manager = \Drupal::service('plugin.manager.smsgateway'); + $gateway_manager = \Drupal::service('plugin.manager.sms_gateway'); switch ($op) { case 'gateway': @@ -486,10 +459,8 @@ function sms_validate_number(&$number, $options = array()) { else { $gateway = sms_default_gateway(); } - if (isset($gateway['validate number']) && function_exists($gateway['validate number'])) { - if ($error = $gateway['validate number']($number, $options)) { - $errors += (array) $error; - } + if ($error = $gateway->validateNumbers([$number], $options)) { + $errors += (array) $error; } return $errors; diff --git a/sms.routing.yml b/sms.routing.yml index bd2ef6c..099d5ff 100644 --- a/sms.routing.yml +++ b/sms.routing.yml @@ -14,6 +14,14 @@ sms.gateway_admin: requirements: _permission: 'administer smsframework' +sms.gateway_add: + path: '/admin/config/smsframework/gateways/add' + defaults: + _form: '\Drupal\sms\Form\GatewayAddForm' + _title: 'Add new gateway configuration' + requirements: + _permission: 'administer smsframework' + sms.gateway_config: path: '/admin/config/smsframework/gateways/{gateway_id}' defaults: @@ -62,3 +70,10 @@ sms.bootstrap_admin: _title: 'Incoming SMS bootstrap' requirements: _permission: 'administer smsframework' + +sms.delivery_report: + path: '/sms/deliveryreport/{gateway_id}' + defaults: + _controller: '\Drupal\sms\DeliveryReportController::acknowledgeDelivery' + requirements: + _access: 'TRUE' diff --git a/sms.services.yml b/sms.services.yml index c5e4aa5..39f9257 100644 --- a/sms.services.yml +++ b/sms.services.yml @@ -1,8 +1,8 @@ services: - sms.provider: - class: Drupal\sms\Sms - arguments: ['@plugin.manager.smsgateway', '@module_handler'] + sms_provider.default: + class: Drupal\sms\Provider\DefaultSmsProvider + arguments: ['@plugin.manager.sms_gateway', '@module_handler'] - plugin.manager.smsgateway: + plugin.manager.sms_gateway: class: Drupal\sms\Gateway\GatewayManager - arguments: ['@container.namespaces', '@config.factory', '@module_handler'] + arguments: ['@container.namespaces', '@config.factory', '@cache.discovery', '@module_handler'] diff --git a/src/Annotation/SmsGateway.php b/src/Annotation/SmsGateway.php index 1cf32b0..a2293f7 100644 --- a/src/Annotation/SmsGateway.php +++ b/src/Annotation/SmsGateway.php @@ -1,4 +1,5 @@ gatewayManager = $gateway_manager; + $this->requestStack = $request_stack; + } + + /** + * Acknowledges delivery reports and passes them to the correct gateway. + * + * @param string $gateway_id + * The gateway id of the gateway that is to handle the delivery report. + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function acknowledgeDelivery($gateway_id) { + $acknowledgement = $this->gatewayManager->getGateway($gateway_id)->deliveryReport($this->requestStack->getCurrentRequest()); + return new Response($acknowledgement); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.sms_gateway'), + $container->get('request_stack') + ); + } + +} diff --git a/src/Form/GatewayAddForm.php b/src/Form/GatewayAddForm.php new file mode 100644 index 0000000..850483b --- /dev/null +++ b/src/Form/GatewayAddForm.php @@ -0,0 +1,106 @@ +setConfigFactory($config_factory); + $this->gatewayManager = $gateway_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('plugin.manager.sms_gateway') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'gateway_create_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $form['label'] = [ + '#type' => 'textfield', + '#title' => $this->t('Gateway name'), + ]; + $form['name'] = [ + '#type' => 'machine_name', + '#title' => '', + '#machine_name' => [ + 'exists' => [$this, 'gatewayExists'], + 'source' => ['label'], + ], + ]; + $form['plugin_id'] = [ + '#type' => 'select', + '#title' => $this->t('Gateway type'), + // List plugins that are configurable so user can choose. + '#options' => array_filter(array_map(function ($definition) { + return $definition['configurable'] ? $definition['label'] : FALSE; + }, $this->gatewayManager->getGatewayPlugins())), + '#required' => TRUE, + ]; + + $form['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Create'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // Create the gateway and redirect to the configure form. + $configuration = $form_state->cleanValues()->getValues(); + $this->gatewayManager->addGateway($configuration['plugin_id'], $configuration); + $form_state->setRedirect('sms.gateway_config', ['gateway_id' => $configuration['name']]); + } + + /** + * Helper function for machine_name form element. + */ + public function gatewayExists($machine_name) { + return (bool) $this->gatewayManager->getGateway($machine_name); + } + +} diff --git a/src/Form/GatewayConfigForm.php b/src/Form/GatewayConfigForm.php index 3b74719..ff97ddb 100644 --- a/src/Form/GatewayConfigForm.php +++ b/src/Form/GatewayConfigForm.php @@ -2,13 +2,16 @@ /** * @file - * Contains GatewayConfigForm class + * Contains \Drupal\sms\Form\GatewayConfigForm */ namespace Drupal\sms\Form; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\sms\Gateway\GatewayManagerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -18,6 +21,29 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * form more streamlined */ class GatewayConfigForm extends ConfigFormBase { + + /** + * The gateway manager. + * + * @var \Drupal\sms\Gateway\GatewayManagerInterface + */ + protected $gatewayManager; + + public function __construct(ConfigFactoryInterface $config_factory, GatewayManagerInterface $gateway_manager) { + $this->setConfigFactory($config_factory); + $this->gatewayManager = $gateway_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('plugin.manager.sms_gateway') + ); + } + /** * {@inheritdoc} */ @@ -29,18 +55,27 @@ class GatewayConfigForm extends ConfigFormBase { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, $gateway_id = NULL) { - $gateway = sms_gateways('gateway', $gateway_id); - if ($gateway && !empty($gateway['configure form']) && function_exists($gateway['configure form'])) { - $form = $gateway['configure form']($gateway['configuration']); - $form['#title'] = $this->t('@gateway configuration', array('@gateway' => $gateway['name'])); - + $gateway = $this->gatewayManager->getGateway($gateway_id); + if ($gateway && $gateway->isConfigurable()) { + $form['title'] = [ + '#type' => 'item', + '#title' => $gateway->getLabel(), + '#markup' => '(' . $gateway->getName() . ')', + ]; + $form['enabled'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Enable this gateway'), + '#default_value' => $gateway->isEnabled(), + ]; + $form = $gateway->buildConfigurationForm($form, $form_state); + $form['#title'] = $this->t('@gateway configuration', array('@gateway' => $gateway->getLabel())); $form['submit'] = array( '#type' => 'submit', - '#value' => $this->t('Save'), + '#value' => $this->t('Save configuration'), ); - $form['gateway'] = array( + $form['gateway_id'] = array( '#type' => 'value', - '#value' => $gateway, + '#value' => $gateway->getIdentifier(), ); return $form; @@ -55,24 +90,24 @@ class GatewayConfigForm extends ConfigFormBase { */ public function validateForm(array &$form, FormStateInterface $form_state) { // Pass validation to gateway. - $function = $form_state->getValue(['gateway', 'configure form']) . '_validate'; - if (function_exists($function)) { - $function($form, $form_state); - } + $gateway = $this->gatewayManager->getGateway($form_state->getValue('gateway_id')); + $gateway->validateConfigurationForm($form, $form_state); } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $gateway = $form_state->getValue('gateway'); - // Remove unnecessary values. - $form_state->cleanValues(); - $form_state->unsetValue('gateway'); - - $this->configFactory()->getEditable('sms.gateway.' . $gateway['identifier']) - ->set('settings' , $form_state->getValues()) - ->save(); + // Call the gateway submission callback before saving configuration. + $gateway = $this->gatewayManager->getGateway($form_state->getValue('gateway_id')); + $gateway->setEnabled($form_state->getValue('enabled')); + $form_state + ->unsetValue('title') + ->unsetValue('enabled') + ->unsetValue('gateway_id') + ->cleanValues(); + $gateway->submitConfigurationForm($form, $form_state); + $this->gatewayManager->saveGateway($gateway); drupal_set_message($this->t('The gateway settings have been saved.')); $form_state->setRedirect('sms.gateway_admin'); } @@ -81,8 +116,7 @@ class GatewayConfigForm extends ConfigFormBase { * Title callback fo the menu */ public function getTitle($gateway_id) { - $gateway = sms_gateways('gateway', $gateway_id); - return sprintf('%s gateway', $gateway['name']); + return $this->t('@name gateway', ['@name' => $this->gatewayManager->getGateway($gateway_id)->getLabel()]); } /** diff --git a/src/Form/GatewayDefaultForm.php b/src/Form/GatewayDefaultForm.php index 3856657..1f08232 100644 --- a/src/Form/GatewayDefaultForm.php +++ b/src/Form/GatewayDefaultForm.php @@ -2,20 +2,49 @@ /** * @file - * Contains GatewayDefaultForm class + * Contains \Drupal\sms\Form\GatewayDefaultForm */ namespace Drupal\sms\Form; use Drupal\Component\Utility\SafeMarkup; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; +use Drupal\sms\Gateway\GatewayManagerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a configuration form for setting the default gateway. */ class GatewayDefaultForm extends ConfigFormBase { + + /** + * The gateway manager. + * + * @var \Drupal\sms\Gateway\GatewayManagerInterface + */ + protected $gatewayManager; + + /** + * Creates new Gateway default selection form. + */ + public function __construct(ConfigFactoryInterface $config_factory, GatewayManagerInterface $gateway_manager) { + $this->setConfigFactory($config_factory); + $this->gatewayManager = $gateway_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('plugin.manager.sms_gateway') + ); + } + /** * {@inheritdoc} */ @@ -28,12 +57,14 @@ class GatewayDefaultForm extends ConfigFormBase { */ public function buildForm(array $form, FormStateInterface $form_state) { $form = parent::buildForm($form, $form_state); - $gateways = sms_gateways(); - $default = sms_default_gateway_id(); + /** @var \Drupal\sms\Gateway\GatewayInterface[] $gateways */ + $gateways = $this->gatewayManager->getAvailableGateways(); + $default = $this->gatewayManager->getDefaultGateway(); $form['gateways'] = array( '#type' => 'table', '#header' => array( + $this->t('Enabled'), $this->t('Default'), $this->t('Name'), array( @@ -47,41 +78,79 @@ class GatewayDefaultForm extends ConfigFormBase { ); foreach ($gateways as $identifier => $gateway) { $form['gateways'][$identifier] = array( + 'enabled' => [ + '#type' => 'checkbox', + '#default_value' => $gateway->isEnabled(), + // The checkbox should be disabled if this gateway is the default. + // @todo Needs tests. + '#states' => [ + 'disabled' => [ + ':input[name="default"]' => ['value' => $identifier], + ], + ], + ], 'default' => [ '#name' => 'default', '#type' => 'radio', - '#default_value' => $default, + '#default_value' => ($default && $default->getName() == $identifier), '#return_value' => $identifier, + // The radio button should be disabled if this gateway not enabled. + // @todo Needs tests. + '#states' => [ + 'disabled' => [ + ':input[name="gateways[' . $identifier . '][enabled]"]' => ['checked' => FALSE], + ], + ], + ], + 'name' => [ + '#markup' => SafeMarkup::checkPlain($gateway->getLabel()), ], - 'name' => ['#markup' => SafeMarkup::checkPlain($gateway['name'])], ); - if (isset($gateway['configure form']) && function_exists($gateway['configure form'] )) { - $form['gateways'][$identifier]['configure'] = ['#markup' => $this->l($this->t('configure'), Url::fromRoute('sms.gateway_config', ['gateway_id' => $identifier]))]; + if ($gateway->isConfigurable()) { + $form['gateways'][$identifier]['configure'] = [ + '#type' => 'link', + '#title' => $this->t('configure'), + '#url' => Url::fromRoute('sms.gateway_config', ['gateway_id' => $identifier]), + ]; } else { - $form['gateways'][$identifier]['configure'] = array('#markup' => $this->t('No configuration options')); + $form['gateways'][$identifier]['configure'] = [ + '#markup' => $this->t('No configuration options') + ]; } } - $form['actions']['submit']['#value'] = $this->t('Set default gateway'); + $form['actions']['submit']['#value'] = $this->t('Save settings'); return $form; } public function validateForm(array &$form, FormStateInterface $form_state) { - $form_state->setValue('default', $form_state->getUserInput()['default']); + if (empty($form_state->getUserInput()['default'])) { + $form_state->setErrorByName('default', $this->t('Default gateway must be set.')); + } + else { + $form_state->setValue('default', $form_state->getUserInput()['default']); + } } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + // Set the enabled status of the gateways. + $new_default = $form_state->getValue('default'); + foreach ($form_state->getValue('gateways') as $identifier => $config) { + $gateway = $this->gatewayManager->getGateway($identifier); + // Selected default gateway is automatically enabled. + if ($gateway->getName() === $new_default) { + $config['enabled'] = TRUE; + } + $gateway->setEnabled($config['enabled']); + $this->gatewayManager->saveGateway($gateway); + } // Process form submission to set the default gateway. - $default = $form_state->getValue('default'); - if (sms_gateways('name', $default)) { + if ($this->gatewayManager->getGateway($new_default)) { drupal_set_message($this->t('Default gateway updated.')); - - $this->config('sms.settings') - ->set('default_gateway', $default) - ->save(); + $this->gatewayManager->setDefaultGateway($new_default); } } diff --git a/src/Gateway/GatewayBase.php b/src/Gateway/GatewayBase.php index 75be6e4..75b8cce 100644 --- a/src/Gateway/GatewayBase.php +++ b/src/Gateway/GatewayBase.php @@ -7,48 +7,57 @@ namespace Drupal\sms\Gateway; -use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginBase; +use Drupal\Core\StringTranslation\StringTranslationTrait; /** * Base class for sms gateway plugins. */ abstract class GatewayBase extends PluginBase implements GatewayInterface { + + /** + * The watchdog logger for this gateway. + * + * @var \Psr\Log\LoggerInterface. + */ + protected $logger; + /** * Construct a new SmsGateway plugin * - * @param array + * @param array $configuration * The configuration to use and build the sms gateway. - * @param string + * @param string $plugin_id * The gateway id. - * @param mixed + * @param mixed $plugin_definition * The gateway plugin definition. */ public function __construct(array $configuration, $plugin_id, $plugin_definition) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->configuration = $this->configuration + $this->defaultConfiguration(); + $this->configuration['custom'] = []; } /** * {@inheritdoc} */ public function getIdentifier() { - return $this->pluginDefinition['id']; + return $this->configuration['name']; } /** - /** * {@inheritdoc} */ public function getName() { - return $this->getLabel(); + return $this->configuration['name']; } /** * {@inheritdoc} */ public function getLabel() { - return $this->pluginDefinition['label']; + return $this->configuration['label']; } /** @@ -68,11 +77,31 @@ abstract class GatewayBase extends PluginBase implements GatewayInterface { /** * {@inheritdoc} */ + public function getCustomConfiguration() { + return $this->configuration['custom']; + } + + /** + * {@inheritdoc} + */ + public function setCustomConfiguration($configuration) { + $this->configuration['custom'] = $configuration; + } + + /** + * {@inheritdoc} + */ public function defaultConfiguration() { - return array( - 'use_ssl' => FALSE, - 'send_method' => 'HTTP_POST', - ); + return [ + 'plugin_id' => $this->pluginDefinition['id'], + 'name' => $this->pluginDefinition['id'], + 'label' => (string) $this->pluginDefinition['label'], + 'enabled' => FALSE, + 'custom' => [ + 'use_ssl' => FALSE, + 'send_method' => 'HTTP_POST', + ] + ]; } /** @@ -85,28 +114,67 @@ abstract class GatewayBase extends PluginBase implements GatewayInterface { /** * {@inheritdoc} */ - public function buildConfigurationForm(array $form, array &$form_state) { + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { return $form; } /** * {@inheritdoc} */ - public function validateConfigurationForm(array &$form, array &$form_state) { + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { return; } /** * {@inheritdoc} */ - public function submitConfigurationForm(array &$form, array &$form_state) { + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { return; } /** * {@inheritdoc} */ - public function sendForm(array &$form, array &$form_state) { + public function sendForm(array &$form, FormStateInterface $form_state) { return array(); } -} \ No newline at end of file + + /** + * {@inheritdoc} + */ + public function isEnabled() { + return isset($this->configuration['enabled']) ? $this->configuration['enabled'] : FALSE; + } + + /** + * {@inheritdoc} + */ + public function setEnabled($enabled = TRUE) { + $this->configuration['enabled'] = (bool) $enabled; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + + /** + * {@inheritdoc} + */ + public function validateNumbers(array $numbers, array $options = array()) { + return []; + } + + /** + * Gets the Psr logger for this gateway. + */ + protected function logger() { + if (!isset($this->logger)) { + $this->logger = \Drupal::logger($this->getName()); + } + return $this->logger; + } + +} diff --git a/src/Gateway/GatewayDefaultImpl.php b/src/Gateway/GatewayDefaultImpl.php deleted file mode 100644 index 7a94328..0000000 --- a/src/Gateway/GatewayDefaultImpl.php +++ /dev/null @@ -1,321 +0,0 @@ -doSend($sms, $options); - - // Invoke sms framework message receipt handlers - // @todo Need to re-look this implementation - foreach ($res as $num => $response) { - @$options += array( - 'gateway_message_status' => $response['error map'], - 'gateway_message_status_text' => $response['error'], - 'gateway_message_status_original' => $response['error code'], - 'gateway' => $this->getName(), - ); - @sms_receipt($num, $response['message id'], $response['message status'], $options); - } - return $res; - } - - - /** - * {@inheritdoc} - */ - public function balance() { - $result = $this->doCommand('credits', array()); - return isset($result['credit_balance']) ? $result['credit_balance'] : 0; - } - - /** - * {@inheritdoc} - */ - public function deliveryReport(Request $request) { - return; - } - - /** - * Provide delivery reports return path for this gateway - * @todo Needs work. - */ - protected function deliveryPath($absolute=TRUE) { - $path = 'sms/deliveryreport/' . $this->getName(); - if ($absolute) { - $path = url($path, array('absolute' => TRUE)); - } - return $path; - } - - /** - * Tests the gateway. - * - * @param array $config - * Optional configuration parameters to test gateway with. - * @return TRUE / FALSE if gateway is through - */ - function test(array $config = array()) { - $result = $this->doCommand('test', array(), $config); - return $result['status']; - } - - /** - * Handles the low-level connection for sending messages. - * - * This method implicitly handles the splitting of messages into smaller - * batches if the number of recipients are too much for a standard HTTP-GET - * or HTTP-POST payload. - * - * @see doSplitSend() - * - * @param Message $message - * The sms message to be sent. - * @param $options - * Additional options. - * @return \Drupal\sms\Message\MessageResult - * The result of the sms messaging operation. - * - * @return array( - * 'status' => TRUE | FALSE, - * 'message' => '', - * 'report' => array( - * '' => array( - * 'status' => TRUE | FALSE // Essentially TRUE if there is a message id - * 'message_id' => '', - * 'error_code' => '', - * 'error_message' => '', - * 'gateway_error_code' => '', - * 'gateway_error_message' => '' - * ), - * 'credit_used' => '', - * 'credit_balance' => '', - * ); - * - * Notes: 'status' of TRUE means successful communication with the sms server and success in message request. Failure of - * individual messages will be captured in the message report status for that number. - */ - protected function doSend(Message $message, array $options) { - return $this->doSplitSend($message, $options); - } - - /** - * Splits and sends messages in batches to recipients. - * - * This method splits messages into smaller batches to avoid exceeding HTTP-GET - * and HTTP-POST data size limits which may cause timeouts, truncated messages, - * and other sorts of wierd behavior depending on the web server. - * - * @param \Drupal\sms\Message\Message $sms - * The message to be sent. - * @param array $options - * Options to be applied while processing this sms. - * @param $maxsize int - * Optional - maximum size of each batch to send. - * @return \Drupal\sms\Message\MessageResult - * The composite MessageResult object. - */ - protected function doSplitSend(Message $sms, array $options, $maxsize = 400) { - // Initialize the composite results array. - $all_res = array( - 'status' => FALSE, - 'error_message' => '', - 'credit_used' => 0, - 'credit_balance' => 0, - 'report' => array(), - ); - $recipients = $sms->getRecipients(); - while (count($recipients) > 0) { - $this_batch = array_slice($recipients, 0, $maxsize); - $recipients = array_slice($recipients, $maxsize); - if (!empty($this_batch)) { - $res = $this->doCommand('send', array('recipients' => $this_batch, 'message' => $sms->getMessage()), $options); - // Combine $res with existing $all_res - @$all_res['status'] |= $res['status']; - @$all_res['error_message'] .= "\n" . $res['message']; - @$all_res['credit_used'] += $res['credit_used']; - @$all_res['credit_balance'] = $res['credit_balance']; - @$all_res['report'] += (array)$res['report']; - } - } - return new MessageResult($all_res); - } - - /** - * Generates random message id's for gateways that don't autogenerate - */ - protected function randomID() { - if (!isset($this->sessid)) { - $this->sessid = str_pad(rand(0, 99), 2, '0', STR_PAD_LEFT); - } - return time() . $this->sessid . str_pad(rand(0, 99999), 5, '0', STR_PAD_LEFT); - } - - /** - * Handles the low-level transmission of messages. - * - * @param string $command - * The command to be executed. - * @param array $data - * An array containing the data pertaining to the message. Information that - * can be in this array is as follows: - * - recipients: An array of recipient numbers. - * - message: The sms message to be dispatched. - * - - * @param array $options - * Optional - additional options for sending the message. - * - * @see \Drupal\sms\Gateway\GatewayInterface::send() for structure of return value - */ - abstract protected function doCommand($command, array $data, array $options = array()); - - /** - * Cleans up message and removes non-compatible characters. - * - * @param string $message - * The message to be cleaned up. - * @return string - * The cleaned up message. - */ - protected function cleanMessage($message) { - $search = str_split('ëí`ìî'); - $replace = str_split('\'\'\'""'); - $message = str_replace($search, $replace, $message); - $message = urlencode($message); - return $message; - } - - /** - * Cleans up sender id and removes non-compatible characters. - * - * @param string $sender - * The sender id to be cleaned up. - * @return string - * The cleaned up sender. - */ - protected function cleanSender($sender) { - return str_replace(' ', '', urlencode(substr($sender, 0, 12))); - } - - /** - * {@inheritdoc} - */ - public function defaultConfiguration() { - return array( - 'ssl' => '', - 'server' => '', - 'username' => '', - 'password' => '', - 'method' => '', - ) + parent::defaultConfiguration(); - } - - /** - * {@inheritdoc} - */ - public function buildConfigurationForm(array $form, array &$form_state) { - $config = $this->getConfiguration(); - - $t_args = array( - '@name' => $this->getName(), - '@label' => $this->getLabel(), - ); - - $form['balance'] = array( - '#type' => 'item', - '#title' => $this->t('Current balance'), - '#markup' => $this->balance(), - ); - $form['method'] = array( - '#type' => 'radios', - '#title' => $this->t('Send Method'), - '#description' => $this->t('The method to use for sending sms.'), - '#options' => array( - $this->t('HTTP GET (Split server request if recipients > 400)'), - $this->t('HTTP GET (Use POST if recipients > 500)'), - $this->t('HTTP POST'), -// $this->t('SMPP (Not yet implemented)'), - ), - '#default_value' => $config['method'], - ); - $form['ssl'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Use SSL Encyption'), - '#description' => $this->t('Drupal\'s built-in HTTP client only supports SSL on PHP 4.3 compiled with OpenSSL.'), - '#default_value' => $config['ssl'], - ); - $form['server'] = array( - '#type' => 'textfield', - '#title' => $this->t('API Server URL'), - '#description' => $this->t('The url for accessing the @label api server.', $t_args), - '#size' => 40, - '#maxlength' => 255, - '#default_value' => $config['server'], - ); - $form['username'] = array( - '#type' => 'textfield', - '#title' => $this->t('Username'), - '#description' => $this->t('The username on the @label account.', $t_args), - '#size' => 40, - '#maxlength' => 255, - '#default_value' => $config['username'], - ); - // @todo should this be a password (masked) text input????? - $form['password'] = array( - '#type' => 'textfield', - '#title' => $this->t('Password'), - '#description' => $this->t('The current password on the @label account.', $t_args), - '#size' => 30, - '#maxlength' => 64, - '#default_value' => $config['password'], - ); - return $form; - } - - /** - * {@inheritdoc} - */ - public function validateConfigurationForm(array &$form, array &$form_state) { - if (!$this->test($form_state['values'])) { - $error = $this->getError(); - form_set_error($error['form_element'], t('The settings could not be validated. A gateway error occurred: @error.', - array('@error' => $error['description']))); - } - } - - /** - * {@inheritdoc} - */ - public function submitConfigurationForm(array &$form, array &$form_state) { - $this->configuration['method'] = $form_state['values']['method']; - $this->configuration['ssl'] = $form_state['values']['ssl']; - $this->configuration['server'] = $form_state['values']['server']; - $this->configuration['username'] = $form_state['values']['username']; - $this->configuration['password'] = $form_state['values']['password']; - } -} diff --git a/src/Gateway/GatewayInterface.php b/src/Gateway/GatewayInterface.php index 0c841bb..9d3aea8 100644 --- a/src/Gateway/GatewayInterface.php +++ b/src/Gateway/GatewayInterface.php @@ -8,17 +8,19 @@ namespace Drupal\sms\Gateway; use Drupal\Component\Plugin\ConfigurablePluginInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginFormInterface; -use Drupal\sms\Message\Message; -use Drupal\sms\Message\MessageInterface; +use Drupal\sms\Message\SmsMessageInterface; use Symfony\Component\HttpFoundation\Request; /** * Default implementation of sms gateway plugin */ interface GatewayInterface extends ConfigurablePluginInterface, PluginFormInterface { + // Gateway response codes // 0=Unknown, 2xx=Positive, 4xx=Negative(likely client err), 5xx=Negative(likely gateway err) + /** * Status Unknown. * @@ -123,14 +125,15 @@ interface GatewayInterface extends ConfigurablePluginInterface, PluginFormInterf /** * Sends an sms and invokes the corresponding sms receipt method. * - * @param \Drupal\sms\Message\MessageInterface $sms + * @param \Drupal\sms\Message\SmsMessageInterface $sms * The sms to be sent. * @param array $options * Options to be applied while processing this sms. - * @return \Drupal\sms\Message\MessageResultInterface + * + * @return \Drupal\sms\Message\SmsMessageResultInterface * The result of the sms messaging operation. */ - public function send(MessageInterface $sms, array $options); + public function send(SmsMessageInterface $sms, array $options); /** * Returns the credit balance available on this gateway. @@ -140,7 +143,7 @@ interface GatewayInterface extends ConfigurablePluginInterface, PluginFormInterf public function balance(); /** - * Handles delivery reports and invoke the corresponding sms_receipt method + * Handles delivery reports and invokes the corresponding sms_receipt method. * * @param \Symfony\Component\HttpFoundation\Request * Request object containing the delivery report in raw format. @@ -165,7 +168,6 @@ interface GatewayInterface extends ConfigurablePluginInterface, PluginFormInterf public function getIdentifier(); /** - /** * Gets the readable name of the gateway. * * @return string @@ -197,5 +199,44 @@ interface GatewayInterface extends ConfigurablePluginInterface, PluginFormInterf * @returns array * The form for additional gateway-specific sending options. */ - public function sendForm(array &$form, array &$form_state); -} \ No newline at end of file + public function sendForm(array &$form, FormStateInterface $form_state); + + /** + * Gets the enabled status of the gateway. + * + * @return bool + */ + public function isEnabled(); + + /** + * Sets the enabled status of the gateway. + * + * @param bool $status + * The enabled status. + */ + public function setEnabled($status = TRUE); + + /** + * Gets the plugin-specific configuration for this gateway. + */ + public function getCustomConfiguration(); + + /** + * Sets the plugin-specific configuration for this gateway. + */ + public function setCustomConfiguration($configuration); + + /** + * Carry out gateway-specific number validation. + * + * @param array $numbers + * The list of phone numbers to be validated. + * @param array $options + * Options to be considered for validation. + * + * @return array + * An array containing an error message for each validation failure. + */ + public function validateNumbers(array $numbers, array $options = array()); + +} diff --git a/src/Gateway/GatewayManager.php b/src/Gateway/GatewayManager.php index bcb9f33..7b77bd6 100644 --- a/src/Gateway/GatewayManager.php +++ b/src/Gateway/GatewayManager.php @@ -7,15 +7,16 @@ namespace Drupal\sms\Gateway; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactory; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\DefaultPluginManager; -use Drupal\sms\Gateway\HookGateway; /** * Manages SMS gateways implemented using AnnotatedClassDiscovery */ class GatewayManager extends DefaultPluginManager implements GatewayManagerInterface { + /** * Configuration factory for this gateway manager. * @@ -38,14 +39,6 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter protected $names; /** - * List of the gateways that have been enabled. - * - * A list of gateway labels is keyed by the gateway ids (names) - * @var array - */ - protected $enabledGateways; - - /** * The default gateway. * * @var string @@ -59,17 +52,23 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter * The namespaces to search for the gateway plugins. * @param \Drupal\Core\Config\ConfigFactory * Handles the instantiation of gateways based on stored configuration. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. * @param \Drupal\Core\Extension\ModuleHandlerInterface * Module handler for calling module hooks. */ - public function __construct(\Traversable $namespaces, ConfigFactory $config, ModuleHandlerInterface $module_handler) { - parent::__construct('Plugin/Gateway', $namespaces, $module_handler, 'Drupal\sms\Annotation\SmsGateway'); -// $this->setCacheBackend($cache_backend, $language_manager, 'mail_backend_plugins'); + public function __construct(\Traversable $namespaces, ConfigFactory $config, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { + parent::__construct('Plugin/Gateway', $namespaces, $module_handler, 'Drupal\sms\Gateway\GatewayInterface', 'Drupal\sms\Annotation\SmsGateway'); + $this->setCacheBackend($cache_backend, 'sms_gateways'); + $this->alterInfo('sms_gateway_info'); $this->config = $config; - $this->enabledGateways = $this->config->get('sms.settings')->get('enabled_gateways'); $this->defaultGateway = $this->config->get('sms.settings')->get('default_gateway'); } + public function getGatewayPlugins() { + return $this->discovery->getDefinitions(); + } + /** * {@inheritdoc} */ @@ -100,14 +99,15 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter */ public function setDefaultGateway($gateway_id) { // Cannot make a disabled gateway default. - if (!$this->enabledGateways[$gateway_id]) { - return; + if ($this->getGateway($gateway_id)->isEnabled()) { + $this->defaultGateway = $gateway_id; + $this->config->getEditable('sms.settings') + ->set('default_gateway', $this->defaultGateway) + ->save(); + } + else { + throw new \LogicException('A disabled gateway cannot be made the default.'); } - $this->defaultGateway = $gateway_id; - - $this->config->get('sms.settings') - ->set('default_gateway', $this->defaultGateway) - ->save(); } /** @@ -124,49 +124,53 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter * {@inheritdoc} */ public function getEnabledGateways() { - if (!isset($this->enabledGateways)) { - $this->enabledGateways = $this->config->get('sms.settings') - ->get('enabled_gateways'); - } - if (!isset($this->enabledGateways)) { - $this->enabledGateways = array(); - } - - // Ensure gateway list has been built. - $gateways = $this->getAvailableGateways(); - - return array_intersect_key($gateways, $this->enabledGateways); + return array_filter($this->getAvailableGateways(), function(GatewayInterface $gateway) { + $gateway->isEnabled(); + }); } /** * {@inheritdoc} */ public function setEnabledGateways(array $gateways) { - $gateways = array_intersect($gateways, array_keys($this->getAvailableGateways())); - $this->enabledGateways = array_fill_keys($gateways, TRUE); - $this->config->get('sms.settings') - ->set('enabled_gateways', $this->enabledGateways) - ->save(); + foreach ($gateways as $gateway_id) { + $gateway = $this->getGateway($gateway_id); + $gateway->setEnabled(TRUE); + $this->saveGateway($gateway); + } } /** * {@inheritdoc} */ public function isGatewayEnabled($gateway_id) { - return !empty($this->enabledGateways[$gateway_id]); + return $this->getGateway($gateway_id)->isEnabled(); } /** * {@inheritdoc} */ - public function saveConfiguration($gateway) { - if (is_string($gateway)) $gateway = $this->getGateway($gateway); - $this->config->get('sms.gateway.config') + public function saveGateway(GatewayInterface $gateway) { + $this->config->getEditable('sms.gateway.config') ->set($gateway->getName(), $gateway->getConfiguration()) ->save(); } /** + * {@inheritdoc} + */ + public function addGateway($plugin_id, array $configuration) { + $name = $configuration['name']; + if (!isset($configuration['name'])) { + throw new \InvalidArgumentException('A machine name is required to create an SMS gateway.'); + } + $configuration['plugin_id'] = $plugin_id; + $this->gateways[$name] = $gateway = $this->createInstance($plugin_id, $configuration); + $this->names[$name] = $this->gateways[$name]->getLabel(); + $this->saveGateway($gateway); + } + + /** * Builds the gateway plugin objects from the definitions. * * @return array @@ -175,25 +179,42 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter * @todo remove BC-shim when all gateways are converted to plugins. */ protected function buildGateways() { + // Non-configurable gateways do not need configuration and so should be + // available by default. + // Get non-configurable plugins and set them up if not already done. $definitions = $this->getDefinitions(); - foreach ($definitions as $id => $definition) { - $settings = $this->config->get('sms.gateway.config')->get($id); + $plugin_config = []; + foreach ($definitions as $plugin_id => $definition) { + if (!$definition['configurable']) { + $plugin_config[$plugin_id] = ['plugin_id' => $plugin_id]; + } + } + + // Get configured gateways. + $instance_configs = $this->config->get('sms.gateway.config')->getRawData(); + $instance_configs += $plugin_config; + foreach ($instance_configs as $id => $instance_config) { // Note that DefaultFactory::createInstance will get the right definitions. - if (!isset($settings)) $settings = array(); - $this->gateways[$id] = $this->createInstance($id, $settings); + if (!isset($instance_config['plugin_id'])) { + // @todo Throw exception here??? +// throw new \InvalidArgumentException(sprintf('Gateway %s configured without a plugin id.', $id)); + continue; + } + $this->gateways[$id] = $this->createInstance($instance_config['plugin_id'], $instance_config); $this->names[$id] = $this->gateways[$id]->getLabel(); } + // BC-shim for gateways still based on hook_gateway_info implementation. - $gateway_array = \Drupal::moduleHandler()->invokeAll('gateway_info'); + $gateway_array = $this->moduleHandler->invokeAll('gateway_info'); + $gateway_configs = $this->config->get('sms.gateway.config'); foreach ($gateway_array as $id => $info) { - $settings = $this->config->get('sms.gateway.config')->get($id); + $settings = $gateway_configs->get($id); if (!isset($settings)) $settings = array(); + $info['identifier'] = $id; $this->gateways[$id] = new HookGateway($info, $settings); $this->names[$id] = $this->gateways[$id]->getLabel(); } - - // Is this needed?? - asort($this->names); } -} \ No newline at end of file + +} diff --git a/src/Gateway/GatewayManagerInterface.php b/src/Gateway/GatewayManagerInterface.php index af296de..5c173ec 100644 --- a/src/Gateway/GatewayManagerInterface.php +++ b/src/Gateway/GatewayManagerInterface.php @@ -10,6 +10,7 @@ namespace Drupal\sms\Gateway; * Manages SMS Gateways. */ interface GatewayManagerInterface { + /** * Get the list of all discoverable SMS gateways. * @@ -67,10 +68,33 @@ interface GatewayManagerInterface { public function isGatewayEnabled($gateway_id); /** + * Creates a new gateway of a type from specified configuration. + * + * @param string $plugin_id + * The plugin ID of an existing gateway plugin. + * @param array $configuration + * The configuration to be saved for the gateway. At a minimum must contain: + * - name: The machine name of the gateway + * - label: The human-readable name of the gateway + * + * @return \Drupal\sms\Gateway\GatewayInterface + * A newly created gateway instance. + */ + public function addGateway($plugin_id, array $configuration); + + /** * Saves the configuration of a gateway. * - * @param \Drupal\sms\Gateway\GatewayInterface|string - * The gateway whose configuration is to be saved. + * @param \Drupal\sms\Gateway\GatewayInterface + * The gateway object whose configuration is to be saved. */ - public function saveConfiguration($gateway); -} \ No newline at end of file + public function saveGateway(GatewayInterface $gateway); + + /** + * Gets the list of gateway plugins discovered by this gateway manager. + * + * @return \Drupal\sms\Gateway\GatewayInterface[] + */ + public function getGatewayPlugins(); + +} diff --git a/src/Gateway/HookGateway.php b/src/Gateway/HookGateway.php index f9da4d1..a57092c 100644 --- a/src/Gateway/HookGateway.php +++ b/src/Gateway/HookGateway.php @@ -7,48 +7,39 @@ namespace Drupal\sms\Gateway; -use Drupal\Component\Plugin\Discovery\DiscoveryTrait; -use Drupal\Core\StringTranslation\StringTranslationTrait; -use Drupal\sms\Message\MessageInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\sms\Message\SmsMessageInterface; +use Drupal\sms\Message\SmsMessageResult; use Symfony\Component\HttpFoundation\Request; /** * BC-shim class to allow hook_gateway_info() based gateways to still work. */ -class HookGateway implements GatewayInterface { - - use StringTranslationTrait; - - /** - * The info that was pulled out from hook implementations. - * - * @var array - */ - protected $info = array(); +class HookGateway extends GatewayBase { /** - * The settings (configuration) that was saved in config. + * Constructs a new hook-based gateway. * - * @var array - */ - protected $settings = array(); - - /** * @param array $info * The info extracted from hook_gateway_info() for this gateway. - * @param array $settings + * @param array $configuration * The settings stored in config for this gateway. */ - public function __construct(array $info, array $settings) { - $this->info = $info; - $this->settings = $settings; + public function __construct(array $info, array $configuration) { + $this->configuration = $configuration + ['custom' => []]; + $this->pluginId = 'sms_hook_gateway'; + $this->pluginDefinition = $info; } /** * {@inheritdoc} */ - public function send(MessageInterface $sms, array $options) { - if (is_callable($this->info['send'])) $this->info['send']($sms, $options); + public function send(SmsMessageInterface $sms, array $options) { + if (is_callable($this->pluginDefinition['send'])) { + $result = $this->pluginDefinition['send']($sms, $options); + return new SmsMessageResult($result); + } + throw new \BadMethodCallException(sprintf('No send method defined for gateway %s', $this->configuration['name'])); } /** @@ -73,75 +64,41 @@ class HookGateway implements GatewayInterface { } /** - * {@inheritdoc} - */ - public function getConfiguration() { - return $this->settings; - } - - /** - * {@inheritdoc} - */ - public function setConfiguration(array $configuration) { - $this->settings = $configuration; - } - - /** - * Returns default configuration for this plugin. - * - * @return array - * An associative array with the default configuration. - */ - public function defaultConfiguration() { - return array( - 'use_ssl' => FALSE, - 'send_method' => 'HTTP_POST', - ); - } - - /** - * {@inheritdoc} - */ - public function calculateDependencies() { - return array(); - } - - /** * Gets the machine name of the gateway. * * @return string */ public function getIdentifier() { - return $this->info['identifier']; + return $this->pluginDefinition['identifier']; } /** * {@inheritdoc} */ public function getName() { - return $this->info['name']; + return $this->pluginDefinition['identifier']; } /** * {@inheritdoc} */ public function getLabel() { - return $this->getName(); + return $this->pluginDefinition['name']; } /** * {@inheritdoc} */ public function isConfigurable() { - return isset($this->info['configure form']) && is_callable($this->info['configure form']); + return isset($this->pluginDefinition['configure form']) && is_callable($this->pluginDefinition['configure form']); } /** * {@inheritdoc} */ - public function buildConfigurationForm(array $form, array &$form_state) { - if (is_callable($this->info['configure form'])) { - return $this->info['configure form']($form, $form_state); + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + if (is_callable($this->pluginDefinition['configure form'])) { + return array_merge($form, $this->pluginDefinition['configure form']($this->getCustomConfiguration())); } else { throw new \RuntimeException($this->t('Configuration form callback not available.')); @@ -151,28 +108,46 @@ class HookGateway implements GatewayInterface { /** * {@inheritdoc} */ - public function validateConfigurationForm(array &$form, array &$form_state) { - $function = $this->info['configure form'] . '_validate'; + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + $function = $this->pluginDefinition['configure form'] . '_validate'; if (is_callable($function)) $function($form, $form_state); } /** * {@inheritdoc} */ - public function submitConfigurationForm(array &$form, array &$form_state) { - $function = $this->info['configure form'] . '_submit'; - if (is_callable($function)) $function($form, $form_state); + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $function = $this->pluginDefinition['configure form'] . '_submit'; + if (is_callable($function)) { + // Call the hook gateway submit callback. + $function($form, $form_state); + } + // Update the configuration. + $this->setCustomConfiguration($form_state->getValues()); } /** * {@inheritdoc} */ - public function sendForm(array &$form, array &$form_state) { - if (is_callable($this->info['send form'])) { - return $this->info['send form']($form, $form_state); + public function sendForm(array &$form, FormStateInterface $form_state) { + if (is_callable($this->pluginDefinition['send form'])) { + return $this->pluginDefinition['send form']($form, $form_state); } else { return array(); } } + + /** + * {@inheritdoc} + */ + public function validateNumbers(array $numbers, array $options = array()) { + if (is_callable($this->pluginDefinition['validate number'])) { + return $this->pluginDefinition['validate number']($numbers, $options); + } + else { + return array(); + } + } + } diff --git a/src/Message/Message.php b/src/Message/SmsMessage.php similarity index 94% rename from src/Message/Message.php rename to src/Message/SmsMessage.php index edf8c8d..55bcb78 100644 --- a/src/Message/Message.php +++ b/src/Message/SmsMessage.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\sms\Message\Message class + * Contains \Drupal\sms\Message\SmsMessage. */ namespace Drupal\sms\Message; @@ -10,7 +10,8 @@ namespace Drupal\sms\Message; /** * Basic implementation of an sms message. */ -class Message implements MessageInterface { +class SmsMessage implements SmsMessageInterface { + /** * The unique identifier for this message. * @@ -99,4 +100,5 @@ class Message implements MessageInterface { } return NULL; } -} \ No newline at end of file + +} diff --git a/src/Message/MessageInterface.php b/src/Message/SmsMessageInterface.php similarity index 89% rename from src/Message/MessageInterface.php rename to src/Message/SmsMessageInterface.php index 86832af..7eccf3c 100644 --- a/src/Message/MessageInterface.php +++ b/src/Message/SmsMessageInterface.php @@ -2,7 +2,7 @@ /** * @file - * Contains definition of \Drupal\sms\Message\MessageInterface + * Contains definition of \Drupal\sms\Message\SmsMessageInterface */ namespace Drupal\sms\Message; @@ -10,9 +10,11 @@ namespace Drupal\sms\Message; /** * Contains information about an sms. */ -interface MessageInterface { -// Message status codes -// 0=Unknown, 2xx=Positive, 3xx=Positive/Neutral (context-dependent), 4xx=Negative +interface SmsMessageInterface { + + // Message status codes + // 0=Unknown, 2xx=Positive, 3xx=Positive/Neutral (context-dependent), 4xx=Negative + /** * Status Unknown. * diff --git a/src/Message/MessageResult.php b/src/Message/SmsMessageResult.php similarity index 85% rename from src/Message/MessageResult.php rename to src/Message/SmsMessageResult.php index 5c3dfce..c21508a 100644 --- a/src/Message/MessageResult.php +++ b/src/Message/SmsMessageResult.php @@ -1,7 +1,7 @@ status = $data['status']; $this->creditBalance = $data['credit_balance']; - $this->creditUsed = $data['credit_used']; + $this->creditsUsed = $data['credit_used']; $this->errorMessage = $data['error_message']; $this->report = $data['report']; } @@ -64,28 +65,28 @@ class MessageResult implements MessageResultInterface { * {@inheritdoc} */ public function getStatus() { - $this->status; + return $this->status; } /** * {@inheritdoc} */ public function getReport() { - $this->report; + return $this->report; } /** * {@inheritdoc} */ public function getBalance() { - $this->creditBalance; + return $this->creditBalance; } /** * {@inheritdoc} */ public function getCreditsUsed() { - $this->creditsUsed; + return $this->creditsUsed; } /** @@ -121,7 +122,7 @@ class MessageResult implements MessageResultInterface { * {@inheritdoc} */ public function getErrorMessage() { - $this->errorMessage; + return $this->errorMessage; } /** @@ -129,10 +130,11 @@ class MessageResult implements MessageResultInterface { */ public function getReportFor($recipient) { if (isset($this->report[$recipient])) { - $this->report[$recipient]; + return $this->report[$recipient]; } else { return null; } } -} \ No newline at end of file + +} diff --git a/src/Message/MessageResultInterface.php b/src/Message/SmsMessageResultInterface.php similarity index 83% rename from src/Message/MessageResultInterface.php rename to src/Message/SmsMessageResultInterface.php index 9a6e80e..737cb0b 100644 --- a/src/Message/MessageResultInterface.php +++ b/src/Message/SmsMessageResultInterface.php @@ -2,13 +2,13 @@ /** * @file - * Contains Drupal\sms\Message\MessageResultInterface + * Contains Drupal\sms\Message\SmsMessageResultInterface */ namespace Drupal\sms\Message; /** - * Contains information on message results. + * Contains information on sms message results. * @return array( * 'status' => TRUE | FALSE, * 'message' => '', @@ -25,7 +25,8 @@ namespace Drupal\sms\Message; * ); * */ -interface MessageResultInterface { +interface SmsMessageResultInterface { + /** * Gets the status of the message. * @@ -44,15 +45,15 @@ interface MessageResultInterface { public function getErrorMessage(); /** - * Gets the report for all recipients. + * Gets the delivery report for all recipients. * * @return array * The report for all recipients. This report is an array keyed by the * recipients' numbers. The value is also an array with the following keys: * - status: TRUE if message was successfully sent, FALSE otherwise. - * - message_id: The message id if the message was successfully sent. Other - * wise 0. - * - error_code: The error code number. + * - message_id: The message id if the message was successfully sent to that + * recipient. Zero means message was not sent to that recipient. + * - error_code: The error code number for a specific message. * - error_message: The description of the error message. * - gateway_error_code: The original error code from the sms gateway. * - gateway_error_message: The original error message from the sms gateway. @@ -89,8 +90,9 @@ interface MessageResultInterface { * @param string $recipient * The number of the recipient for which the report is to be retrieved. * @return array - * An array containing the message report @link see MessageResultInterface::getReport() - * @endlink + * An array containing the message report + * @link see SmsMessageResultInterface::getReport() @endlink */ public function getReportFor($recipient); -} \ No newline at end of file + +} diff --git a/src/Plugin/Gateway/DebugGateway.php b/src/Plugin/Gateway/DebugGateway.php deleted file mode 100644 index 238a119..0000000 --- a/src/Plugin/Gateway/DebugGateway.php +++ /dev/null @@ -1,219 +0,0 @@ - '', - 'message_failure' => '', - 'delivery_failure' => '', - 'retain_dlrs' => '', - ) + parent::defaultConfiguration(); - } - - /** - * {@inheritdoc} - */ - public function getError() { - // TODO: Implement getError() method. - } - - /** - * {@inheritdoc} - */ - protected function doCommand($command, array $data, array $options = NULL) { - $config = $this->getConfiguration(); - switch ($command) { - case 'send': - // Log the messages and generate message id's for each sender - $report = array(); - $msgids = ''; - foreach ($data['recipients'] as $number) { - // Coin toss to determine if message 'fails' or 'succeeds' - if (rand(0, 100) < $config['message_failure']) { // 'Failed' - $err = rand(-13, -1); // Random error message - $msgids .= $err . "\n"; - $report[$number] = array( - 'status' => FALSE, - 'message_id' => '', - 'error_code' => $err, - 'error_message' => self::$errorCodes[$err], - ); - } - else { // 'Succeeded' - $msgid = $this->randomID(); - $msgids .= $msgid . "\n"; - $report[$number] = array( - 'status' => TRUE, - 'message_id' => $msgid, - 'error_code' => 0, - 'error_message' => '', - ); - } - } - $message = $this->cleanMessage($data['message']); - $numbers = implode(',', $data['numbers']); - watchdog('sms_debug', "SMS message sent to %number with the text: @message\nmessage ids: @msgids", array('%number' => $numbers, '@message' => $data['message'], '@msgids' => $msgids), WATCHDOG_INFO); - - // Save the msgids for dlr generation - $delivery_report = \Drupal::config('sms.debug_gateway.reports')->get('delivery_report'); - $delivery_report[] = $report; - // @todo Optimize this later - \Drupal::config('sms.debug_gateway.reports')->set('delivery_report', $delivery_report)->save(); - return array('status' => TRUE, 'report' => $report); - - case 'report': // Delivery report - $dlr_config = \Drupal::config('sms.debug_gateway.reports'); - $reports = array( - 'status' => TRUE, - 'data' => $dlr_config->get('delivery_report'), - ); - if (!$config['retain_dlrs']) { - $dlr_config->delete('delivery_report')->save(); - } - return $reports; - - case 'credits': case 'test': - return array( - 'status' => TRUE, - 'credit_balance' => '100000' - ); - - default: - return array( - 'status' => FALSE, - 'message' => t('An error occurred during the HTTP request: @error', array('@error' => 'Unknown error')) - ); - } - } - - /** - * {@inheritdoc} - */ - public function deliveryReport(Request $request) { - return; - } - - /** - * {@inheritdoc} - */ - public function buildConfigurationForm(array $form, array &$form_state) { - $form['balance'] = array( - '#type' => 'item', - '#title' => t('Current balance'), - '#markup' => $this->balance(), - ); - - $config = $this->getConfiguration() + array( - 'random' => '', - 'message_failure' => '', - 'delivery_failure' => '', - 'retain_dlrs' => '', - ); - - $form['debug'] = array( - '#type' => 'fieldset', - '#title' => t('Random failure settings'), - '#collapsible' => FALSE, - '#tree' => TRUE, - ); - $form['debug']['random'] = array( - '#type' => 'checkbox', - '#title' => t('Enable random failures'), - '#description' => t('Enable generation of random errors to simulate real world messaging.'), - '#default_value' => $config['random'], - ); - $form['debug']['message_failure'] = array( - '#type' => 'textfield', - '#title' => t('Message failure rate'), - '#description' => t('Simulated rate for failure of messages (percentage).'), - '#field_suffix' => '%', - '#size' => 30, - '#maxlength' => 64, - '#default_value' => $config['message_failure'], - ); - $form['debug']['delivery_failure'] = array( - '#type' => 'textfield', - '#title' => t('Message delivery failure rate'), - '#description' => t('Simulated rate for failure of delivery reports (percentage).'), - '#field_suffix' => '%', - '#size' => 30, - '#maxlength' => 64, - '#default_value' => $config['delivery_failure'], - ); - - $form['retain_dlrs'] = array( - '#type' => 'checkbox', - '#title' => t('Retain delivery reports after poll'), - '#description' => t('Retains delivery reports after they have been polled.
Default behaviour is to delete'), - '#default_value' => $config['retain_dlrs'], - ); - - $form['clear_dlrs'] = array( - '#type' => 'checkbox', - '#title' => t('Clear all delivery reports now'), - '#description' => t('Clear all delivery reports and records stored'), - ); - - return $form; - } - - /** - * {@inheritdoc} - */ - public function validateConfigurationForm(array &$form, array &$form_state) { - if (!$this->test($form_state['values']['debug'])) { - $error = $this->getError(); - form_set_error('', t('A Debug gateway error occurred: @error.', array('@error' => $error['description']))); - } - } - - /** - * {@inheritdoc} - */ - public function submitConfigurationForm(array &$form, array &$form_state) { - $this->configuration = $form_state['values']['debug']; - $this->configuration['retain_dlrs'] = $form_state['values']['retain_dlrs']; - - // @todo: Storage for this should be moved to a more appropriate location. - if ($form_state['values']['clear_dlrs']) $this->configuration['debug_reports'] = array(); - } - - /** - * Debug error codes - */ - static $errorCodes = array( - '-1' => 'SEND_ERROR', - '-2' => 'NOT_ENOUGHCREDITS', - '-3' => 'NETWORK_NOTCOVERED', - '-4' => 'SOCKET_EXCEPTION', - '-5' => 'INVALID_USER_OR_PASS', - '-6' => 'MISSING_DESTINATION_ADDRESS', - '-7' => 'MISSING_SMSTEXT', - '-8' => 'MISSING_SENDERNAME', - '-9' => 'DESTADDR_INVALIDFORMAT', - '-10' => 'MISSING_USERNAME', - '-11' => 'MISSING_PASS', - '-12' => 'MISSING_WAPURL', - '-13' => 'INVALID_DESTINATION_ADDRESS', - ); -} diff --git a/src/Plugin/Gateway/LogGateway.php b/src/Plugin/Gateway/LogGateway.php index c37c1f4..a3a8309 100644 --- a/src/Plugin/Gateway/LogGateway.php +++ b/src/Plugin/Gateway/LogGateway.php @@ -7,9 +7,7 @@ namespace Drupal\sms\Plugin\Gateway; use Drupal\sms\Gateway\GatewayBase; -use Drupal\sms\Message\Message; -use Drupal\sms\Message\MessageInterface; -use Drupal\sms\SmsMessage; +use Drupal\sms\Message\SmsMessageInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -20,14 +18,15 @@ use Symfony\Component\HttpFoundation\Request; * ) */ class LogGateway extends GatewayBase { + /** * {@inheritdoc} * * Log sms message to drupal watchdog(). */ - public function send(MessageInterface $sms, array $options) { - watchdog('sms', 'SMS message sent to %number with the text: @message', - array('%number' => implode(', ', $sms->getRecipients()), '@message' => $sms->getMessage()), WATCHDOG_INFO); + public function send(SmsMessageInterface $sms, array $options) { + $this->logger()->notice('SMS message sent to %number with the text: @message', + array('%number' => implode(', ', $sms->getRecipients()), '@message' => $sms->getMessage())); // return array('status' => TRUE); } @@ -52,4 +51,5 @@ class LogGateway extends GatewayBase { public function getError() { return array(); } + } diff --git a/src/Sms.php b/src/Provider/DefaultSmsProvider.php similarity index 51% rename from src/Sms.php rename to src/Provider/DefaultSmsProvider.php index b4c4d9d..17961c6 100644 --- a/src/Sms.php +++ b/src/Provider/DefaultSmsProvider.php @@ -1,22 +1,24 @@ gatewayManager->getDefaultGateway(); + public function send(SmsMessageInterface $sms, array $options = array()) { + // Check if preferred gateway is specified in the $options. + if (isset($options['gateway'])) { + $gateway = $this->gatewayManager->getGateway($options['gateway']); + } + if (empty($gateway)) { + $gateway = $this->gatewayManager->getDefaultGateway(); + } if ($this->preProcess($sms, $options, $gateway)) { - $response = NULL; - $response = $gateway->send($sms, $options); - $result = $this->handleResult($response, $sms); - + $result = $this->process($sms, $options, $gateway); $this->postProcess($sms, $options, $gateway, $result); return $result; } @@ -67,32 +72,40 @@ class Sms implements SmsProviderInterface { } /** + * Processes the sms message and handles the response from the gateway. + */ + protected function process(SmsMessageInterface $sms, array $options, GatewayInterface $gateway) { + $this->moduleHandler->invokeAll('sms_send', [$sms, $options, $gateway]); + // @todo Apply token replacements. + $response = $gateway->send($sms, $options); + $result = $this->handleResult($response, $sms); + return $result; + } + + /** * Calls pre-process hooks and ensures that the action is still permitted. * - * @param \Drupal\sms\Message\MessageInterface $sms + * @param \Drupal\sms\Message\SmsMessageInterface $sms * The sms to be sent. * @param array $options * Additonal options to be passed to the sms gateway. * @param \Drupal\sms\Gateway\GatewayInterface $gateway The default gateway for sending this message. + * * @returns bool * Whether to continue sending or not. */ - protected function preProcess(MessageInterface $sms, array $options, GatewayInterface $gateway) { - // Call the send hook. - // @todo change this later to a preprocess or presend hook - $continue = TRUE; - foreach ($this->moduleHandler->getImplementations('sms_send') as $module) { - $function = $module . '_sms_send'; - $continue &= $function($sms, $options, $gateway); - } - return $continue; + protected function preProcess(SmsMessageInterface $sms, array $options, GatewayInterface $gateway) { + // Call the send pre process hooks. + $return = $this->moduleHandler->invokeAll('sms_send_process', ['pre process', $sms, $options, $gateway]); + // Return FALSE if any of the hooks returned FALSE. + return !in_array(FALSE, $return); } /** * Calls post process hooks. * - * @param \Drupal\sms\Message\MessageInterface $sms + * @param \Drupal\sms\Message\SmsMessageInterface $sms * The sms that was sent. * @param array $options * Additonal options that were passed to the sms gateway. @@ -101,12 +114,9 @@ class Sms implements SmsProviderInterface { * @param bool $result * Whether the sms sending was successful or not. */ - protected function postProcess(MessageInterface $sms, array $options, GatewayInterface $gateway, $result) { - // Post process hook - foreach ($this->moduleHandler->getImplementations('sms_send_process') as $module) { - $function = $module . '_sms_send_process'; - $function('post process', $sms, $options, $gateway, $result); - } + protected function postProcess(SmsMessageInterface $sms, array $options, GatewayInterface $gateway, $result) { + // Call the send post process hooks. + $this->moduleHandler->invokeAll('sms_send_process', ['post process', $sms, $options, $gateway]); } /** @@ -114,7 +124,7 @@ class Sms implements SmsProviderInterface { * * @param array * The result to be handled. - * @param \Drupal\sms\Message\MessageInterface + * @param \Drupal\sms\Message\SmsMessageInterface * The message that was sent. * @return bool * True if message was sent successfully. Throws an SmsException if message @@ -122,25 +132,18 @@ class Sms implements SmsProviderInterface { * @throws \Drupal\sms\SmsException * */ - protected function handleResult($result, MessageInterface $sms) { - if ($result['status']) { + protected function handleResult(SmsMessageResultInterface $result, SmsMessageInterface $sms) { + if ($result->getStatus()) { return TRUE; } else { // @todo Review all of this. - $variables['%number'] = implode(',', $sms->getRecipients()); - if (!empty($result['variables'])) { - $variables = array_merge($variables, $result['variables']); - } - if ($result['message']) { - $variables['%message'] = $result['message']; - $error_message = t('Sending SMS to %number failed. The gateway said: %message', $variables); - } - else { - $error_message = t('Sending SMS to %number failed.', $variables); + $error_message = t('Sending SMS to %number failed.', ['%number' => implode(',', $sms->getRecipients())]); + if ($message = $result->getErrorMessage()) { + $error_message .= t(' The gateway said %message.', ['%message' => $message]); } + \Drupal::logger('sms')->error($message); throw new SmsException($error_message); -// watchdog('sms', $error_message, $variables, WATCHDOG_ERROR); // return FALSE; } } @@ -148,8 +151,15 @@ class Sms implements SmsProviderInterface { /** * {@inheritdoc} */ - public function incoming(MessageInterface $message, array $options) { - + public function incoming(SmsMessageInterface $message, array $options) { +// if (module_exists('rules')) { +// $options += array('number' => $number, 'message' => $message); +// rules_invoke_event('sms_incoming', $options); +// } + // Execute three phases + $this->moduleHandler->invokeAll('sms_incoming', array('pre process', $message, $options)); + $this->moduleHandler->invokeAll('sms_incoming', array('process', $message, $options)); + $this->moduleHandler->invokeAll('sms_incoming', array('post process', $message, $options)); } /** @@ -162,4 +172,4 @@ class Sms implements SmsProviderInterface { $this->moduleHandler->invokeAll('sms_receipt', array('post process', $number, $reference, $message_status, $options)); } -} \ No newline at end of file +} diff --git a/src/SmsProviderInterface.php b/src/Provider/SmsProviderInterface.php similarity index 52% rename from src/SmsProviderInterface.php rename to src/Provider/SmsProviderInterface.php index 646bda1..76b3bfc 100644 --- a/src/SmsProviderInterface.php +++ b/src/Provider/SmsProviderInterface.php @@ -1,60 +1,62 @@ assertEqual(array('log' => t('Log only'), 'test' => t('For testing')), sms_gateways('names')); + $test_gateways = [ + 'log' => 'Log only', + 'test' => 'For testing', + ]; + $this->assertEqual($test_gateways, sms_gateways('names')); } /** @@ -31,20 +35,21 @@ class SmsFrameworkWebTest extends WebTestBase { public function testDefaultGateway() { // Test initial default gateway. $gw = sms_default_gateway(); - $this->assertEqual($gw['identifier'], 'log', 'Initial default gateway is "log".'); + $this->assertEqual($gw->getIdentifier(), 'log', 'Initial default gateway is "log".'); $this->drupalLogin($this->drupalCreateUser(['administer smsframework'])); // Set up default log gateway. - $this->drupalPostForm('admin/config/smsframework/gateways', ['default' => 'log'], t('Set default gateway')); + $this->drupalPostForm('admin/config/smsframework/gateways', ['default' => 'log'], 'Save settings'); $this->assertResponse(200); $gw = sms_default_gateway(); - $this->assertEqual($gw['identifier'], 'log', 'Default gateway set to log.'); + $this->assertEqual($gw->getIdentifier(), 'log', 'Default gateway set to log.'); // Set up default test gateway. - $this->drupalPostForm('admin/config/smsframework/gateways', ['default' => 'test'], t('Set default gateway')); + $this->drupalPostForm('admin/config/smsframework/gateways', ['default' => 'test'], 'Save settings'); $this->assertResponse(200); + $this->rebuildContainer(); $gw = sms_default_gateway(); - $this->assertEqual($gw['identifier'], 'test', 'Default gateway set to test.'); + $this->assertEqual($gw->getIdentifier(), 'test', 'Default gateway set to test.'); } /** @@ -59,10 +64,12 @@ class SmsFrameworkWebTest extends WebTestBase { 'method' => 0, 'ssl' => false, ); - $this->drupalPostForm('admin/config/smsframework/gateways/test', $edit, t('Save')); + $this->drupalPostForm('admin/config/smsframework/gateways/test', $edit, 'Save configuration'); $this->assertResponse(200); + $this->rebuildContainer(); $gateway = sms_gateways('gateway', 'test'); - $this->assertEqual($edit, $gateway['configuration'], 'SMS Test gateway successfully configured.'); +// $this->assertEqual($edit, $gateway->getConfiguration(), 'SMS Test gateway successfully configured.'); + $this->assertEqual($edit, $gateway->getConfiguration()['custom']); } /** diff --git a/tests/modules/sms_test_gateway/sms_test_gateway.module b/tests/modules/sms_test_gateway/sms_test_gateway.module index ad85da1..1d527a8 100644 --- a/tests/modules/sms_test_gateway/sms_test_gateway.module +++ b/tests/modules/sms_test_gateway/sms_test_gateway.module @@ -4,6 +4,7 @@ * @file * A test gateway to be used for testing the sms framework */ +use Drupal\sms\Message\SmsMessageInterface; /** * Implements hook_gateway_info(). @@ -23,18 +24,32 @@ function sms_test_gateway_gateway_info() { } /** - * Send callback + * Send callback. + * + * @param \Drupal\sms\Message\SmsMessageInterface $sms + * The message to be sent + * @param array $options + * Options for sending the message. + * + * @return array */ -function sms_test_gateway_send($number = NULL, $message = NULL, $options = array()) { +function sms_test_gateway_send(SmsMessageInterface $sms, $options = array()) { $result = &drupal_static('sms_test_gateway_result'); - if (!is_null($number) && !is_null($message)) { - $result['number'] = $number; - $result['message'] = $message; + if (!is_null($sms->getRecipients()) && !is_null($sms->getMessage())) { + $result['number'] = implode(',',$sms->getRecipients()); + $result['message'] = $sms->getMessage(); $result['options'] = $options; \Drupal::state()->set('sms_test_gateway_result', $result); } - return array('status' => TRUE, 'result' => $result); + return [ + 'status' => TRUE, + 'result' => $result, + 'credit_balance' => 1000, + 'credit_used' => count($sms->getRecipients()), + 'error_message' => '', + 'report' => $result, + ]; } function sms_test_gateway_result($reset = FALSE) { @@ -144,23 +159,25 @@ function sms_test_gateway_config_form($configuration) { * less than 10 digits, or has non-numeric characters or has country code 990, * 997 or 999. * - * @param string $number - * The number to be validated. + * @param array $numbers + * The numbers to be validated. * * @return array * List of errors. */ -function sms_test_gateway_validate_number($number) { - $code = substr($number, 0, 3); +function sms_test_gateway_validate_number($numbers) { $errors = array(); - if (preg_match('/[^0-9]/', $number)) { - $errors[] = t('Non-numeric character found in number.'); - } - if (strlen($number) > 15 || strlen($number) < 10) { - $errors[] = t('Number longer than 15 digits or shorter than 10 digits.'); - } - if ($code == '990' || $code == '997' || $code == '999') { - $errors[] = t('Country code not allowed'); + foreach ($numbers as $number) { + $code = substr($number, 0, 3); + if (preg_match('/[^0-9]/', $number)) { + $errors[] = t('Non-numeric character found in number.'); + } + if (strlen($number) > 15 || strlen($number) < 10) { + $errors[] = t('Number longer than 15 digits or shorter than 10 digits.'); + } + if ($code == '990' || $code == '997' || $code == '999') { + $errors[] = t('Country code not allowed'); + } } return $errors; }