diff --git a/modules/sms_blast/src/Tests/SmsBlastWebTest.php b/modules/sms_blast/src/Tests/SmsBlastWebTest.php index b4df3b1..ee48f26 100644 --- a/modules/sms_blast/src/Tests/SmsBlastWebTest.php +++ b/modules/sms_blast/src/Tests/SmsBlastWebTest.php @@ -7,23 +7,23 @@ namespace Drupal\sms_blast\Tests; -use \Drupal\simpletest\WebTestBase; +use Drupal\sms\Tests\SmsFrameworkWebTestBase; /** * Integration tests for the sms_blast module. * * @group SMS Framework */ -class SmsBlastWebTest extends WebTestBase { +class SmsBlastWebTest extends SmsFrameworkWebTestBase { - public static $modules = ['sms', 'sms_test_gateway', 'sms_user', 'sms_blast']; + public static $modules = ['sms_user', 'sms_blast']; /** * Tests sending sms blast. */ function testSendBlast() { // Set up test default gateway and test user. - $this->container->get('config.factory')->getEditable('sms.settings')->set('default_gateway', 'test')->save(); + $this->setDefaultGateway('test'); $user = $this->drupalCreateUser(array('receive sms', 'Send SMS Blast')); $this->drupalLogin($user); $data = array( diff --git a/modules/sms_devel/src/Tests/SmsDevelWebTest.php b/modules/sms_devel/src/Tests/SmsDevelWebTest.php index acd6567..081b64b 100644 --- a/modules/sms_devel/src/Tests/SmsDevelWebTest.php +++ b/modules/sms_devel/src/Tests/SmsDevelWebTest.php @@ -7,16 +7,16 @@ namespace Drupal\sms_devel\Tests; -use Drupal\simpletest\WebTestBase; +use Drupal\sms\Tests\SmsFrameworkWebTestBase; /** * Tests the send/receive form provided by SMS Devel. * * @group SMS Framework */ -class SmsDevelWebTest extends WebTestBase { +class SmsDevelWebTest extends SmsFrameworkWebTestBase { - public static $modules = ['sms', 'sms_test_gateway', 'sms_devel']; + public static $modules = ['sms_devel']; /** * Tests if messages sent using the test send form are stored properly. @@ -28,7 +28,7 @@ class SmsDevelWebTest extends WebTestBase { $this->drupalLogin($user); // Set up test default gateway. - $this->config('sms.settings')->set('default_gateway', 'test')->save(); + $this->setDefaultGateway('test'); $test_message1 = array( 'number' => '1234567890', diff --git a/modules/sms_user/src/Tests/SmsUserWebTest.php b/modules/sms_user/src/Tests/SmsUserWebTest.php index 3b7b20f..380c54c 100644 --- a/modules/sms_user/src/Tests/SmsUserWebTest.php +++ b/modules/sms_user/src/Tests/SmsUserWebTest.php @@ -7,7 +7,7 @@ namespace Drupal\sms_user\Tests; -use Drupal\simpletest\WebTestBase; +use Drupal\sms\Tests\SmsFrameworkWebTestBase; use Drupal\user\Entity\User; /** @@ -18,19 +18,16 @@ use Drupal\user\Entity\User; * @todo Add tests for creation of users via sms. * @todo Add tests for integration with rules and actions modules. */ -class SmsUserWebTest extends WebTestBase { +class SmsUserWebTest extends SmsFrameworkWebTestBase { - public static $modules = ['sms', 'sms_test_gateway', 'sms_user', 'syslog', 'sms_devel']; + public static $modules = ['sms_user', 'syslog', 'sms_devel']; /** * Tests user adding phone number. */ public function testNumberConfirmationAndSmsUserSend() { // Set up test default gateway. - /** @var \Drupal\sms\Gateway\GatewayManagerInterface $gateway_manager */ - $gateway_manager = $this->container->get('plugin.manager.sms_gateway'); - $gateway_manager->setEnabledGateways(['test']); - $gateway_manager->setDefaultGateway('test'); + $this->setDefaultGateway('test'); $user = $this->drupalCreateUser(array('receive sms', 'edit own sms number')); $this->drupalLogin($user); @@ -161,8 +158,7 @@ class SmsUserWebTest extends WebTestBase { $this->drupalLogin($excluded_user); // Set up test default gateway. - $this->config('sms.settings')->set('default_gateway', 'test')->save(); - + $this->setDefaultGateway('test'); $sms_user_settings = array( 'registration_enabled' => TRUE, 'allow_password' => TRUE, diff --git a/src/Gateway/GatewayBase.php b/src/Gateway/GatewayBase.php index 428caa6..2a64c4d 100644 --- a/src/Gateway/GatewayBase.php +++ b/src/Gateway/GatewayBase.php @@ -35,8 +35,7 @@ abstract class GatewayBase extends PluginBase implements GatewayInterface { */ 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'] = []; + $this->configuration = array_merge($this->defaultConfiguration(), $this->configuration); } /** @@ -97,10 +96,7 @@ abstract class GatewayBase extends PluginBase implements GatewayInterface { 'name' => $this->pluginDefinition['id'], 'label' => (string) $this->pluginDefinition['label'], 'enabled' => FALSE, - 'custom' => [ - 'use_ssl' => FALSE, - 'send_method' => 'HTTP_POST', - ] + 'custom' => [], ]; } @@ -136,7 +132,7 @@ abstract class GatewayBase extends PluginBase implements GatewayInterface { * {@inheritdoc} */ public function sendForm(array &$form, FormStateInterface $form_state) { - return array(); + return []; } /** @@ -163,7 +159,7 @@ abstract class GatewayBase extends PluginBase implements GatewayInterface { /** * {@inheritdoc} */ - public function validateNumbers(array $numbers, array $options = array()) { + public function validateNumbers(array $numbers, array $options = []) { return []; } @@ -185,7 +181,7 @@ abstract class GatewayBase extends PluginBase implements GatewayInterface { * {@inheritdoc} */ public function getError() { - return array(); + return []; } /** diff --git a/src/Gateway/GatewayManager.php b/src/Gateway/GatewayManager.php index 63ea7ee..95beef9 100644 --- a/src/Gateway/GatewayManager.php +++ b/src/Gateway/GatewayManager.php @@ -11,6 +11,7 @@ use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactory; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\Core\StringTranslation\TranslationWrapper; /** * Manages SMS gateways implemented using AnnotatedClassDiscovery @@ -65,8 +66,11 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter $this->defaultGateway = $this->config->get('sms.settings')->get('default_gateway'); } + /** + * {@inheritdoc} + */ public function getGatewayPlugins() { - return $this->discovery->getDefinitions(); + return $this->getDefinitions(); } /** @@ -133,11 +137,13 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter * {@inheritdoc} */ public function setEnabledGateways(array $gateway_ids) { + $gateways = []; foreach ($gateway_ids as $gateway_id) { $gateway = $this->getGateway($gateway_id); $gateway->setEnabled(TRUE); - $this->saveGateway($gateway); + $gateways[] = $gateway; } + $this->saveGateways($gateways); } /** @@ -151,15 +157,35 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter * {@inheritdoc} */ public function saveGateway(GatewayInterface $gateway) { - $this->config->getEditable('sms.gateway.config') - ->set($gateway->getName(), $gateway->getConfiguration()) - ->save(); + $this->saveGateways([$gateway]); + } + + /** + * {@inheritdoc} + */ + public function saveGateways(array $gateways) { + $config = $this->config->getEditable('sms.gateway.config'); + /** @var \Drupal\sms\Gateway\GatewayInterface $gateway */ + foreach ($gateways as $gateway) { + $config->set($gateway->getName(), $gateway->getConfiguration()); + } + $config->save(); + // @todo Implement more granular cache invalidations. + // Clear static caches. + $this->gateways = NULL; + $this->names = NULL; + // Clear dynamic caches. + $this->clearCachedDefinitions(); } /** * {@inheritdoc} */ public function addGateway($plugin_id, array $configuration) { + // Initialize existing gateways first. + if (!isset($this->gateways)) { + $this->buildGateways(); + } $name = $configuration['name']; if (!isset($configuration['name'])) { throw new \InvalidArgumentException('A machine name is required to create an SMS gateway.'); @@ -175,44 +201,43 @@ class GatewayManager extends DefaultPluginManager implements GatewayManagerInter * * @return array * \Drupal\sms\Gateway\GatewayInterface[] - * - * @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(); - $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($instance_config['plugin_id'])) { - throw new \InvalidArgumentException(sprintf('Gateway %s configured without a plugin id.', $id)); + throw new \InvalidArgumentException(sprintf('Gateway "%s" configured without a plugin id.', $id)); } $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 = $this->moduleHandler->invokeAll('gateway_info'); - $gateway_configs = $this->config->get('sms.gateway.config'); - foreach ($gateway_array as $id => $info) { - $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(); + /** + * {@inheritdoc} + * + * @todo remove BC-shim when all gateways are converted to plugins. + */ + public function findDefinitions() { + // Get discovered plugin definitions. + $definitions = parent::findDefinitions(); + + // Add hook_gateway_info definitions. + foreach ($this->moduleHandler->invokeAll('gateway_info') as $id => $hook_info) { + // @todo This allows overwriting of annotated plugins by hook plugins. + // @todo Is that acceptable? + $definitions[$id] = [ + 'id' => $id, + 'label' => new TranslationWrapper($hook_info['name']), + 'configurable' => is_callable($hook_info['configure form']), + 'class' => '\Drupal\sms\Gateway\HookGateway', + 'provider' => 'sms', + 'hook_info' => $hook_info, + ]; } + return $definitions; } } diff --git a/src/Gateway/GatewayManagerInterface.php b/src/Gateway/GatewayManagerInterface.php index a127611..ca1443d 100644 --- a/src/Gateway/GatewayManagerInterface.php +++ b/src/Gateway/GatewayManagerInterface.php @@ -83,7 +83,7 @@ interface GatewayManagerInterface { public function addGateway($plugin_id, array $configuration); /** - * Saves the configuration of a gateway. + * Saves the configuration of a single gateway. * * @param \Drupal\sms\Gateway\GatewayInterface * The gateway object whose configuration is to be saved. @@ -91,6 +91,14 @@ interface GatewayManagerInterface { public function saveGateway(GatewayInterface $gateway); /** + * Saves the configuration of multiple gateways. + * + * @param \Drupal\sms\Gateway\GatewayInterface[] + * An array of gateway objects whose configuration is to be saved. + */ + public function saveGateways(array $gateway); + + /** * Gets the list of gateway plugins discovered by this gateway manager. * * @return \Drupal\sms\Gateway\GatewayInterface[] diff --git a/src/Gateway/HookGateway.php b/src/Gateway/HookGateway.php index 9c7c134..ac0fbc3 100644 --- a/src/Gateway/HookGateway.php +++ b/src/Gateway/HookGateway.php @@ -18,25 +18,12 @@ use Symfony\Component\HttpFoundation\Request; class HookGateway extends GatewayBase { /** - * Constructs a new hook-based gateway. - * - * @param array $info - * The info extracted from hook_gateway_info() for this gateway. - * @param array $configuration - * The settings stored in config for this gateway. - */ - public function __construct(array $info, array $configuration) { - $this->configuration = $configuration + ['custom' => []]; - $this->pluginId = 'sms_hook_gateway'; - $this->pluginDefinition = $info; - } - - /** * {@inheritdoc} */ public function send(SmsMessageInterface $sms, array $options) { - if (is_callable($this->pluginDefinition['send'])) { - $result = $this->pluginDefinition['send']($sms, $options); + $hook = $this->pluginDefinition['hook_info']['send']; + if (is_callable($hook)) { + $result = $hook($sms, $options); return new SmsMessageResult($result); } throw new \BadMethodCallException(sprintf('No send method defined for gateway %s', $this->configuration['name'])); @@ -46,8 +33,9 @@ class HookGateway extends GatewayBase { * {@inheritdoc} */ public function balance() { - if (is_callable($this->pluginDefinition['balance'])) { - return $this->pluginDefinition['balance'](); + $hook = $this->pluginDefinition['hook_info']['balance']; + if (is_callable($hook)) { + return $hook(); } return 0; } @@ -56,47 +44,19 @@ class HookGateway extends GatewayBase { * {@inheritdoc} */ public function deliveryReport(Request $request) { - if (is_callable($this->pluginDefinition['delivery report'])) { - return $this->pluginDefinition['delivery report'](); + $hook = $this->pluginDefinition['hook_info']['delivery report']; + if (is_callable($hook)) { + return $hook(); } } /** - * Gets the machine name of the gateway. - * - * @return string - */ - public function getIdentifier() { - return $this->pluginDefinition['identifier']; - } - - /** - * {@inheritdoc} - */ - public function getName() { - return $this->pluginDefinition['identifier']; - } - - /** - * {@inheritdoc} - */ - public function getLabel() { - return $this->pluginDefinition['name']; - } - - /** - * {@inheritdoc} - */ - public function isConfigurable() { - return isset($this->pluginDefinition['configure form']) && is_callable($this->pluginDefinition['configure form']); - } - - /** * {@inheritdoc} */ 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())); + $hook = $this->pluginDefinition['hook_info']['configure form']; + if (is_callable($hook)) { + return array_merge($form, $hook($this->getCustomConfiguration())); } else { throw new \RuntimeException($this->t('Configuration form callback not available.')); @@ -107,18 +67,20 @@ class HookGateway extends GatewayBase { * {@inheritdoc} */ public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { - $function = $this->pluginDefinition['configure form'] . '_validate'; - if (is_callable($function)) $function($form, $form_state); + $hook = $this->pluginDefinition['hook_info']['configure form'] . '_validate'; + if (is_callable($hook)) { + $hook($form, $form_state); + } } /** * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { - $function = $this->pluginDefinition['configure form'] . '_submit'; - if (is_callable($function)) { + $hook = $this->pluginDefinition['hook_info']['configure form'] . '_submit'; + if (is_callable($hook)) { // Call the hook gateway submit callback. - $function($form, $form_state); + $hook($form, $form_state); } // Update the configuration. $this->setCustomConfiguration($form_state->getValues()); @@ -128,8 +90,9 @@ class HookGateway extends GatewayBase { * {@inheritdoc} */ public function sendForm(array &$form, FormStateInterface $form_state) { - if (is_callable($this->pluginDefinition['send form'])) { - return $this->pluginDefinition['send form']($form, $form_state); + $hook = $this->pluginDefinition['hook_info']['send form']; + if (is_callable($hook)) { + return $hook($form, $form_state); } else { return array(); @@ -140,8 +103,9 @@ class HookGateway extends GatewayBase { * {@inheritdoc} */ public function validateNumbers(array $numbers, array $options = array()) { - if (is_callable($this->pluginDefinition['validate number'])) { - return $this->pluginDefinition['validate number']($numbers, $options); + $hook = $this->pluginDefinition['hook_info']['validate number']; + if (is_callable($hook)) { + return $hook($numbers, $options); } else { return array(); diff --git a/src/Plugin/Gateway/LogGateway.php b/src/Plugin/Gateway/LogGateway.php index 59e0a2a..f7cabfe 100644 --- a/src/Plugin/Gateway/LogGateway.php +++ b/src/Plugin/Gateway/LogGateway.php @@ -14,7 +14,7 @@ use Drupal\sms\Message\SmsMessageResult; * @SmsGateway( * id = "log", * label = @Translation("Log only"), - * configurable = FALSE, + * configurable = false, * ) */ class LogGateway extends GatewayBase { diff --git a/src/Tests/SmsFrameworkWebTest.php b/src/Tests/SmsFrameworkWebTest.php index 4092b15..5467c75 100644 --- a/src/Tests/SmsFrameworkWebTest.php +++ b/src/Tests/SmsFrameworkWebTest.php @@ -7,16 +7,32 @@ namespace Drupal\sms\Tests; -use \Drupal\simpletest\WebTestBase; - /** * Integration tests for the SMS Framework. * * @group SMS Framework */ -class SmsFrameworkWebTest extends WebTestBase { - - public static $modules = ['sms', 'sms_test_gateway']; +class SmsFrameworkWebTest extends SmsFrameworkWebTestBase { + + /** + * Tests the HookGateway implementation. + */ + public function testHookGatewayIntegration() { + // Test that hook gateway plugins are correctly discovered. + $gateway_plugins = $this->gatewayManager->getGatewayPlugins(); + $this->assertEqual(array_keys($gateway_plugins), ['log', 'test'], 'Hook-based gateway discovered.'); + $this->assertEqual($gateway_plugins['test']['hook_info'], sms_test_gateway_gateway_info()['test'], 'sms_test_gateway hooks correct.'); + + // Confirm the existence of the test gateway. + $test_gateway = $this->gatewayManager->getGateway('test'); + $this->assertNotNull($test_gateway, 'Test gateway not null'); + + // Add an instance and confirm that it exists + $this->gatewayManager->addGateway('test', ['name' => 'test_instance', 'label' => 'Test gateway instance']); + $test_gateway = $this->gatewayManager->getGateway('test_instance'); + $this->assertEqual(get_class($test_gateway), 'Drupal\sms\Gateway\HookGateway'); + $this->assertEqual($test_gateway->getLabel(), 'Test gateway instance'); + } /** * Tests that the correct gateways list is obtained. @@ -30,6 +46,20 @@ class SmsFrameworkWebTest extends WebTestBase { } /** + * Tests the add gateway functionality. + */ + public function testAddGateways() { + $gateways = ['log', 'test']; + $this->assertEqual($gateways, array_keys($this->gatewayManager->getAvailableGateways())); + for ($i = 0; $i < 3; $i++) { + $name = $this->randomMachineName(); + $this->gatewayManager->addGateway('test', ['name' => $name]); + $gateways[] = $name; + $this->assertEqual($gateways, array_keys($this->gatewayManager->getAvailableGateways())); + } + } + + /** * Tests setting up the default gateway. */ public function testDefaultGateway() { @@ -47,7 +77,7 @@ class SmsFrameworkWebTest extends WebTestBase { // Set up default test gateway. $this->drupalPostForm('admin/config/smsframework/gateways', ['default' => 'test'], 'Save settings'); $this->assertResponse(200); - $this->rebuildContainer(); + $this->resetAll(); $gw = sms_default_gateway(); $this->assertEqual($gw->getIdentifier(), 'test', 'Default gateway set to test.'); } @@ -66,8 +96,7 @@ class SmsFrameworkWebTest extends WebTestBase { ); $this->drupalPostForm('admin/config/smsframework/gateways/test', $edit, 'Save configuration'); $this->assertResponse(200); - $this->rebuildContainer(); - $gateway = sms_gateways('gateway', 'test'); + $gateway = $this->gatewayManager->getGateway('test'); $this->assertEqual($edit, $gateway->getCustomConfiguration(), 'SMS Test gateway successfully configured.'); } diff --git a/src/Tests/SmsFrameworkWebTestBase.php b/src/Tests/SmsFrameworkWebTestBase.php new file mode 100644 index 0000000..fafc8a8 --- /dev/null +++ b/src/Tests/SmsFrameworkWebTestBase.php @@ -0,0 +1,39 @@ +gatewayManager = $this->container->get('plugin.manager.sms_gateway'); + // Add an instance of test gateway and set it as default. + $this->gatewayManager->addGateway('test', ['name' => 'test']); + } + + public function setDefaultGateway($gateway_id) { + // Ensure gateway is enabled first. + $this->gatewayManager->setEnabledGateways([$gateway_id]); + $this->gatewayManager->setDefaultGateway($gateway_id); + } + +}