diff --git a/config/install/inmail.fetcher.imap.yml b/config/install/inmail.fetcher.imap.yml new file mode 100644 index 0000000..a460cff --- /dev/null +++ b/config/install/inmail.fetcher.imap.yml @@ -0,0 +1,9 @@ +id: imap +label: 'IMAP Sample' +plugin: imap +configuration: + host: + ssl: FALSE + port: 143 + username: + password: diff --git a/config/schema/inmail.schema.yml b/config/schema/inmail.schema.yml index 5758826..c6cb02a 100644 --- a/config/schema/inmail.schema.yml +++ b/config/schema/inmail.schema.yml @@ -45,6 +45,23 @@ inmail.inmail_handler.*: label: 'Configuration' type: inmail.handler.[%parent.plugin] +inmail.fetcher.*: + label: 'Mail fetcher' + type: config_entity + mapping: + id: + label: 'Machine-name' + type: string + label: + label: 'Label' + type: label + plugin: + label: 'Plugin' + type: string + configuration: + label: 'Configuration' + type: inmail.plugin.fetcher.[%parent.plugin] + # Empty default configuration schema inmail.analyzer.*: label: 'Analyzer configuration' @@ -54,7 +71,7 @@ inmail.handler.*: label: 'Handler configuration' type: mapping -# Specific analyzer/handler configuration +# Specific plugin configuration inmail.handler.moderator_forward: label: 'Moderator Forward handler configuration' type: mapping @@ -62,3 +79,26 @@ inmail.handler.moderator_forward: moderator: label: 'Moderator address' type: email + +inmail.plugin.fetcher.imap: + label: 'IMAP Fetcher' + type: mapping + mapping: + host: + label: 'Host' + type: string + ssl: + label: 'SSL' + type: boolean + port: + label: 'Port' + type: integer + username: + label: 'Username' + type: string + password: + label: 'Password' + type: string + batch_size: + label: 'Batch size' + type: integer diff --git a/inmail.links.task.yml b/inmail.links.task.yml index 0a8f7a5..d835d19 100644 --- a/inmail.links.task.yml +++ b/inmail.links.task.yml @@ -4,14 +4,20 @@ inmail.settings: base_route: inmail.settings weight: 0 +inmail.fetcher_list: + route_name: inmail.fetcher_list + title: 'Mail fetchers' + base_route: inmail.settings + weight: 1 + inmail.analyzer_list: route_name: inmail.analyzer_list title: 'Message analyzers' base_route: inmail.settings - weight: 1 + weight: 2 inmail.handler_list: route_name: inmail.handler_list title: 'Message handlers' base_route: inmail.settings - weight: 2 + weight: 3 diff --git a/inmail.module b/inmail.module index 59ab750..862a8b5 100644 --- a/inmail.module +++ b/inmail.module @@ -270,12 +270,18 @@ function inmail_mail($key, &$message, $params) { * Implements hook_cron(). */ function inmail_cron() { - // Fetch new mail by adding each fetcher to the queue. The fetching queue - // worker in turn adds new messages to another queue for processing. - /** @var \Drupal\inmail\FetcherManager $fetcher_manager */ + /** @var \Drupal\inmail\Entity\FetcherConfig[] $fetchers */ + $fetchers = \Drupal::entityManager()->getStorage('inmail_fetcher')->loadMultiple(); + /** @var \Drupal\Component\Plugin\PluginManagerInterface $fetcher_manager */ $fetcher_manager = \Drupal::service('plugin.manager.inmail.fetcher'); - foreach ($fetcher_manager->getInstances() as $fetcher) { - // @todo Is it iportant to avoid duplicates? - \Drupal::queue('inmail_fetch')->createItem($fetcher); + /** @var \Drupal\inmail\MessageProcessorInterface $processor */ + $processor = \Drupal::service('inmail.processor'); + + // Fetch and process new mail. + foreach ($fetchers as $fetcher) { + /** @var \Drupal\inmail\Plugin\inmail\Fetcher\FetcherInterface $plugin */ + $plugin = $fetcher_manager->createInstance($fetcher->id(), $fetcher->getConfiguration()); + $raws = $plugin->fetch(); + $processor->processMultiple($raws); } } diff --git a/inmail.routing.yml b/inmail.routing.yml index 0a9a46b..d7d0ab8 100644 --- a/inmail.routing.yml +++ b/inmail.routing.yml @@ -65,3 +65,33 @@ entity.inmail_handler.disable: _controller: \Drupal\inmail\Controller\HandlerController::disable requirements: _permission: 'administer inmail' + +inmail.fetcher_list: + path: admin/config/system/inmail/fetchers + defaults: + _entity_list: 'inmail_fetcher' + _title: 'Mail fetchers' + requirements: + _permission: 'administer inmail' + +entity.inmail_fetcher.edit_form: + path: admin/config/system/inmail/fetchers/{inmail_fetcher} + defaults: + _entity_form: 'inmail_fetcher.default' + _title_callback: \Drupal\inmail\Controller\FetcherController::titleEdit + requirements: + _permission: 'administer inmail' + +entity.inmail_fetcher.enable: + path: admin/config/system/inmail/fetchers/{inmail_fetcher}/enable + defaults: + _controller: \Drupal\inmail\Controller\FetcherController::enable + requirements: + _permission: 'administer inmail' + +entity.inmail_fetcher.disable: + path: admin/config/system/inmail/fetchers/{inmail_fetcher}/disable + defaults: + _controller: \Drupal\inmail\Controller\FetcherController::disable + requirements: + _permission: 'administer inmail' diff --git a/inmail.services.yml b/inmail.services.yml index 7ccfb3e..260e421 100644 --- a/inmail.services.yml +++ b/inmail.services.yml @@ -3,9 +3,13 @@ services: class: Drupal\inmail\MessageProcessor arguments: ['@entity.manager', '@plugin.manager.inmail.analyzer', '@plugin.manager.inmail.handler', '@logger.channel.inmail'] plugin.manager.inmail.fetcher: - class: Drupal\inmail\FetcherManager - parent: default_plugin_manager - arguments: ['@config.factory'] + class: Drupal\Core\Plugin\DefaultPluginManager + arguments: + - 'Plugin/inmail/Fetcher' + - '@container.namespaces' + - '@module_handler' + - 'Drupal\inmail\Plugin\inmail\Fetcher\FetcherInterface' + - 'Drupal\inmail\Annotation\Fetcher' plugin.manager.inmail.analyzer: class: Drupal\inmail\AnalyzerManager parent: default_plugin_manager diff --git a/src/Controller/FetcherController.php b/src/Controller/FetcherController.php new file mode 100644 index 0000000..8018524 --- /dev/null +++ b/src/Controller/FetcherController.php @@ -0,0 +1,51 @@ +get('plugin.manager.inmail.fetcher')); + } + + /** + * Returns a title for the fetcher configuration edit page. + */ + public function titleEdit(FetcherConfig $inmail_fetcher) { + return $this->t('Configure %label fetcher', array('%label' => $inmail_fetcher->label())); + } + + /** + * Enables a mail fetcher. + */ + public function enable(FetcherConfig $inmail_fetcher) { + $inmail_fetcher->enable()->save(); + return new RedirectResponse(\Drupal::url('inmail.fetcher_list', [], ['absolute' => TRUE])); + } + + /** + * Disables a mail fetcher. + */ + public function disable(FetcherConfig $inmail_fetcher) { + $inmail_fetcher->disable()->save(); + return new RedirectResponse(\Drupal::url('inmail.fetcher_list', [], ['absolute' => TRUE])); + } + +} diff --git a/src/Entity/AnalyzerConfig.php b/src/Entity/AnalyzerConfig.php index 9bafd06..8569541 100644 --- a/src/Entity/AnalyzerConfig.php +++ b/src/Entity/AnalyzerConfig.php @@ -6,8 +6,6 @@ namespace Drupal\inmail\Entity; -use Drupal\Core\Config\Entity\ConfigEntityBase; - /** * Message analyzer configuration entity. * @@ -40,35 +38,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase; * } * ) */ -class AnalyzerConfig extends ConfigEntityBase { - - /** - * The machine name of the analyzer configuration. - * - * @var string - */ - protected $id; - - /** - * The translatable, human-readable name of the analyzer configuration. - * - * @var string - */ - protected $label; - - /** - * The ID of the analyzer plugin for this configuration. - * - * @var string - */ - protected $plugin; - - /** - * The configuration for the plugin. - * - * @var array - */ - protected $configuration = array(); +class AnalyzerConfig extends PluginConfigEntity { /** * The weight of the analyzer configuration. @@ -79,39 +49,4 @@ class AnalyzerConfig extends ConfigEntityBase { */ protected $weight; - /** - * Returns the analyzer plugin ID. - * - * @return string - * The machine name of the plugin for this analyzer. - */ - public function getPluginId() { - return $this->plugin; - } - - /** - * Returns the plugin configuration stored for this analyzer. - * - * @return array - * The plugin configuration. Its properties are defined by the associated - * plugin. - */ - public function getConfiguration() { - return $this->configuration; - } - - /** - * Replaces the configuration stored for this analyzer. - * - * @param array $configuration - * New plugin configuraion. Should match the properties defined by the - * plugin referenced by ::$plugin. - * - * @return $this - */ - public function setConfiguration(array $configuration) { - $this->configuration = $configuration; - return $this; - } - } diff --git a/src/Entity/FetcherConfig.php b/src/Entity/FetcherConfig.php new file mode 100644 index 0000000..c67d3a2 --- /dev/null +++ b/src/Entity/FetcherConfig.php @@ -0,0 +1,38 @@ +plugin; - } - - /** - * Returns the plugin configuration stored for this handler. - * - * @return array - * The plugin configuration. Its properties are defined by the associated - * plugin. - */ - public function getConfiguration() { - return $this->configuration; - } - - /** - * Replaces the configuration stored for this handler. - * - * @param array $configuration - * New plugin configuraion. Should match the properties defined by the - * plugin referenced by ::$plugin. - * - * @return $this - */ - public function setConfiguration(array $configuration) { - $this->configuration = $configuration; - return $this; - } - +class HandlerConfig extends PluginConfigEntity { // @todo Implement HandlerConfig::calculateDependencies() https://www.drupal.org/node/2379929 } diff --git a/src/Entity/PluginConfigEntity.php b/src/Entity/PluginConfigEntity.php new file mode 100644 index 0000000..d3ac97b --- /dev/null +++ b/src/Entity/PluginConfigEntity.php @@ -0,0 +1,78 @@ +plugin; + } + + /** + * Returns the configuration stored for this plugin. + * + * @return array + * The plugin configuration. Its properties are defined by the associated + * plugin. + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * Replaces the configuration stored for this plugin. + * + * @param array $configuration + * New plugin configuraion. Should match the properties defined by the + * plugin referenced by ::$plugin. + * + * @return $this + */ + public function setConfiguration(array $configuration) { + $this->configuration = $configuration; + return $this; + } +} diff --git a/src/FetcherListBuilder.php b/src/FetcherListBuilder.php new file mode 100644 index 0000000..a45a41d --- /dev/null +++ b/src/FetcherListBuilder.php @@ -0,0 +1,86 @@ +fetcherManager = $fetcher_manager; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, + $container->get('entity.manager')->getStorage($entity_type->id()), + $container->get('plugin.manager.inmail.fetcher') + ); + } + + /** + * {@inheritdoc} + */ + public function buildHeader() { + $row['label'] = $this->t('Fetcher'); + $row['plugin'] = $this->t('Plugin'); + return $row + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + /** @var \Drupal\inmail\Entity\FetcherConfig $entity */ + $plugin_id = $entity->getPluginId(); + if ($this->fetcherManager->hasDefinition($plugin_id)) { + $plugin_label = $this->fetcherManager->getDefinition($plugin_id)['label']; + } + else { + $plugin_label = $this->t('Plugin missing'); + } + + $row['label'] = $this->getLabel($entity); + $row['plugin'] = $plugin_label; + return $row + parent::buildRow($entity); + } + + /** + * {@inheritdoc} + */ + public function getDefaultOperations(EntityInterface $entity) { + $operations = parent::getDefaultOperations($entity); + $operations['edit']['title'] = $this->t('Configure'); + return $operations; + } +} diff --git a/src/FetcherManager.php b/src/FetcherManager.php deleted file mode 100644 index 345bde1..0000000 --- a/src/FetcherManager.php +++ /dev/null @@ -1,66 +0,0 @@ -setCacheBackend($cache_backend, 'inmail_fetcher'); - $this->config = $config; - } - - /** - * Creates and returns instances for all fetchers. - * - * @return \Drupal\inmail\Plugin\inmail\Fetcher\FetcherInterface[] - * Instantiated fetcher plugins. - */ - public function getInstances() { - $instances = array(); - foreach ($this->getDefinitions() as $id => $definition) { - $config_item = $this->config->get("inmail.fetcher.$id"); - $instances[$id] = $this->createInstance($id, $config_item->getRawData()); - } - return $instances; - } - - /** - * Invokes each fetcher and returns new messages. - * - * @returns array - * An associative array where keys are fetcher IDs and values are lists of - * raw messages. - */ - public function fetchAll() { - return array_map(function(FetcherInterface $fetcher) { - return $fetcher->fetch(); - }, $this->getInstances()); - } - -} diff --git a/src/Form/AnalyzerConfigurationForm.php b/src/Form/AnalyzerConfigurationForm.php index 87104f3..956f386 100644 --- a/src/Form/AnalyzerConfigurationForm.php +++ b/src/Form/AnalyzerConfigurationForm.php @@ -6,11 +6,7 @@ namespace Drupal\inmail\Form; -use Drupal\Core\Config\Entity\ConfigEntityStorageInterface; -use Drupal\Core\Entity\EntityForm; -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\inmail\AnalyzerManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -22,29 +18,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * * @ingroup analyzer */ -class AnalyzerConfigurationForm extends EntityForm { - - /** - * The message analyzer plugin manager. - * - * @var \Drupal\inmail\AnalyzerManagerInterface - */ - protected $analyzerManager; - - /** - * The entity storage for analyzer configurations. - * - * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface - */ - protected $storage; - - /** - * {@inheritdoc} - */ - public function __construct(AnalyzerManagerInterface $analyzer_manager, ConfigEntityStorageInterface $storage) { - $this->analyzerManager = $analyzer_manager; - $this->storage = $storage; - } +class AnalyzerConfigurationForm extends PluginConfigurationForm { /** * {@inheritdoc} @@ -59,83 +33,9 @@ class AnalyzerConfigurationForm extends EntityForm { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state) { - $form = parent::buildForm($form, $form_state); - /** @var \Drupal\inmail\Entity\AnalyzerConfig $entity */ - $entity = $this->getEntity(); - - // Load plugin instance. - /** @var \Drupal\inmail\Plugin\inmail\Analyzer\AnalyzerInterface $plugin */ - $plugin = $this->analyzerManager->createInstance($entity->getPluginId(), $entity->getConfiguration()); - $form_state->set('plugin', $plugin); - - $form['label'] = array( - '#title' => $this->t('Label'), - '#type' => 'textfield', - '#default_value' => $entity->label(), - ); - - $form['id'] = array( - '#type' => 'machine_name', - '#default_value' => $entity->id(), - '#disabled' => !$entity->isNew(), - '#machine_name' => array( - 'exists' => array($this, 'exists'), - ), - ); - - $form['status'] = array( - '#title' => $this->t('Enabled'), - '#type' => 'checkbox', - '#default_value' => TRUE, - ); - - $form['configuration'] = $plugin->buildConfigurationForm(array(), $form_state); - - return $form; - } - - /** - * Determines if the analyzer already exists. - * - * @param string $id - * The analyzer configuration ID. - * - * @return bool - * TRUE if the analyzer exists, FALSE otherwise. - */ - public function exists($id) { - return (!is_null($this->storage->load($id))); - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - parent::validateForm($form, $form_state); - /** @var \Drupal\inmail\Plugin\inmail\Analyzer\AnalyzerInterface $plugin */ - $plugin = $form_state->get('plugin'); - $plugin->validateConfigurationForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); - // Let the plugin update its configuration from the form. - /** @var \Drupal\inmail\Plugin\inmail\Analyzer\AnalyzerInterface $plugin */ - $plugin = $form_state->get('plugin'); - $plugin->submitConfigurationForm($form, $form_state); - - // Copy plugin configuration to the entity for persistence. The reason for - // not doing this by overriding copyFormValuesToEntity is that the plugin - // submit handler has to happen first. - /** @var \Drupal\inmail\Entity\AnalyzerConfig $entity */ - $entity = $this->getEntity(); - $entity->setConfiguration($plugin->getConfiguration()); - $form_state->setRedirect('inmail.analyzer_list'); } diff --git a/src/Form/FetcherConfigurationForm.php b/src/Form/FetcherConfigurationForm.php new file mode 100644 index 0000000..e2972ef --- /dev/null +++ b/src/Form/FetcherConfigurationForm.php @@ -0,0 +1,42 @@ +get('plugin.manager.inmail.fetcher'), + $container->get('entity.manager')->getStorage('inmail_fetcher') + ); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + parent::submitForm($form, $form_state); + + $form_state->setRedirect('inmail.fetcher_list'); + } + +} diff --git a/src/Form/HandlerConfigurationForm.php b/src/Form/HandlerConfigurationForm.php index 629108c..4818f32 100644 --- a/src/Form/HandlerConfigurationForm.php +++ b/src/Form/HandlerConfigurationForm.php @@ -6,11 +6,7 @@ namespace Drupal\inmail\Form; -use Drupal\Core\Config\Entity\ConfigEntityStorageInterface; -use Drupal\Core\Entity\EntityForm; -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\inmail\HandlerManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -22,29 +18,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * * @ingroup handler */ -class HandlerConfigurationForm extends EntityForm { - - /** - * The message handler plugin manager. - * - * @var \Drupal\inmail\HandlerManagerInterface - */ - protected $handlerManager; - - /** - * The entity storage for handler configurations. - * - * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface - */ - protected $storage; - - /** - * {@inheritdoc} - */ - public function __construct(HandlerManagerInterface $handler_manager, ConfigEntityStorageInterface $storage) { - $this->handlerManager = $handler_manager; - $this->storage = $storage; - } +class HandlerConfigurationForm extends PluginConfigurationForm { /** * {@inheritdoc} @@ -59,83 +33,9 @@ class HandlerConfigurationForm extends EntityForm { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state) { - $form = parent::buildForm($form, $form_state); - /** @var \Drupal\inmail\Entity\HandlerConfig $entity */ - $entity = $this->getEntity(); - - // Load plugin instance. - /** @var \Drupal\inmail\Plugin\inmail\Handler\HandlerInterface $plugin */ - $plugin = $this->handlerManager->createInstance($entity->getPluginId(), $entity->getConfiguration()); - $form_state->set('plugin', $plugin); - - $form['label'] = array( - '#title' => $this->t('Label'), - '#type' => 'textfield', - '#default_value' => $entity->label(), - ); - - $form['id'] = array( - '#type' => 'machine_name', - '#default_value' => $entity->id(), - '#disabled' => !$entity->isNew(), - '#machine_name' => array( - 'exists' => array($this, 'exists'), - ), - ); - - $form['status'] = array( - '#title' => $this->t('Enabled'), - '#type' => 'checkbox', - '#default_value' => TRUE, - ); - - $form['configuration'] = $plugin->buildConfigurationForm(array(), $form_state); - - return $form; - } - - /** - * Determines if the handler already exists. - * - * @param string $id - * The handler configuration ID. - * - * @return bool - * TRUE if the handler exists, FALSE otherwise. - */ - public function exists($id) { - return (!is_null($this->storage->load($id))); - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - parent::validateForm($form, $form_state); - /** @var \Drupal\inmail\Plugin\inmail\Handler\HandlerInterface $plugin */ - $plugin = $form_state->get('plugin'); - $plugin->validateConfigurationForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); - // Let the plugin update its configuration from the form. - /** @var \Drupal\inmail\Plugin\inmail\Handler\HandlerInterface $plugin */ - $plugin = $form_state->get('plugin'); - $plugin->submitConfigurationForm($form, $form_state); - - // Copy plugin configuration to the entity for persistence. The reason for - // not doing this by overriding copyFormValuesToEntity is that the plugin - // submit handler has to happen first. - /** @var \Drupal\inmail\Entity\HandlerConfig $entity */ - $entity = $this->getEntity(); - $entity->setConfiguration($plugin->getConfiguration()); - $form_state->setRedirect('inmail.handler_list'); } diff --git a/src/Form/InmailSettingsForm.php b/src/Form/InmailSettingsForm.php index 41b624d..d04b213 100644 --- a/src/Form/InmailSettingsForm.php +++ b/src/Form/InmailSettingsForm.php @@ -6,11 +6,8 @@ namespace Drupal\inmail\Form; -use Drupal\Component\Plugin\PluginManagerInterface; -use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Form for general Inmail configuration. @@ -20,31 +17,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface; class InmailSettingsForm extends ConfigFormBase { /** - * The injected Fetcher plugin manager. - * - * @var \Drupal\inmail\FetcherManager - */ - protected $fetcherManager; - - /** - * Constructs an Inmail settings form. - */ - public function __construct(ConfigFactoryInterface $config_factory, PluginManagerInterface $fetcher_manager) { - parent::__construct($config_factory); - $this->fetcherManager = $fetcher_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('config.factory'), - $container->get('plugin.manager.inmail.fetcher') - ); - } - - /** * {@inheritdoc} */ public function getFormId() { @@ -68,22 +40,6 @@ class InmailSettingsForm extends ConfigFormBase { '#default_value' => $config->get('return_path'), ); - if ($fetchers = $this->fetcherManager->getInstances()) { - $form['fetching'] = array( - '#type' => 'fieldset', - '#title' => $this->t('Fetching'), - '#description' => $this->t('By configuring a fetcher you can process all email from a mailbox of your choice.'), - '#description_display' => 'before', - ); - foreach ($fetchers as $fetcher) { - $form['fetching'][$fetcher->getPluginId()] = array( - '#type' => 'details', - '#title' => $fetcher->getLabel(), - '#open' => TRUE, - ) + $fetcher->buildConfigurationForm(array(), $form_state); - } - } - return parent::buildForm($form, $form_state); } @@ -93,30 +49,9 @@ class InmailSettingsForm extends ConfigFormBase { public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); - // Save general settings. $this->config('inmail.settings') ->set('return_path', $form_state->getValue('return_path')) ->save(); - - // Save fetcher settings. - foreach ($this->fetcherManager->getInstances() as $id => $fetcher) { - $fetcher->submitConfigurationForm($form, $form_state); - $fetcher_config = $this->config("inmail.fetcher.$id"); - foreach ($fetcher->getConfiguration() as $key => $value) { - $fetcher_config->set($key, $value); - } - $fetcher_config->save(); - } - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - parent::validateForm($form, $form_state); - foreach ($this->fetcherManager->getInstances() as $fetcher) { - $fetcher->validateConfigurationForm($form, $form_state); - } } /** @@ -130,5 +65,4 @@ class InmailSettingsForm extends ConfigFormBase { $form_state->setError($element, $this->t('The address may not contain a + character.')); } } - } diff --git a/src/Form/HandlerConfigurationForm.php b/src/Form/PluginConfigurationForm.php similarity index 57% copy from src/Form/HandlerConfigurationForm.php copy to src/Form/PluginConfigurationForm.php index 629108c..3be2bcc 100644 --- a/src/Form/HandlerConfigurationForm.php +++ b/src/Form/PluginConfigurationForm.php @@ -1,38 +1,30 @@ handlerManager = $handler_manager; + public function __construct(PluginManagerInterface $plugin_manager, ConfigEntityStorageInterface $storage) { + $this->pluginManager = $plugin_manager; $this->storage = $storage; } /** * {@inheritdoc} */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.inmail.handler'), - $container->get('entity.manager')->getStorage('inmail_handler') - ); - } - - /** - * {@inheritdoc} - */ public function buildForm(array $form, FormStateInterface $form_state) { $form = parent::buildForm($form, $form_state); - /** @var \Drupal\inmail\Entity\HandlerConfig $entity */ + /** @var \Drupal\inmail\Entity\PluginConfigEntity $entity */ $entity = $this->getEntity(); // Load plugin instance. - /** @var \Drupal\inmail\Plugin\inmail\Handler\HandlerInterface $plugin */ - $plugin = $this->handlerManager->createInstance($entity->getPluginId(), $entity->getConfiguration()); + /** @var \Drupal\Core\Plugin\PluginFormInterface $plugin */ + $plugin = $this->pluginManager->createInstance($entity->getPluginId(), $entity->getConfiguration()); $form_state->set('plugin', $plugin); $form['label'] = array( @@ -96,13 +78,13 @@ class HandlerConfigurationForm extends EntityForm { } /** - * Determines if the handler already exists. + * Determines if the plugin configuration already exists. * * @param string $id - * The handler configuration ID. + * The plugin configuration ID. * * @return bool - * TRUE if the handler exists, FALSE otherwise. + * TRUE if the plugin config exists, FALSE otherwise. */ public function exists($id) { return (!is_null($this->storage->load($id))); @@ -113,7 +95,8 @@ class HandlerConfigurationForm extends EntityForm { */ public function validateForm(array &$form, FormStateInterface $form_state) { parent::validateForm($form, $form_state); - /** @var \Drupal\inmail\Plugin\inmail\Handler\HandlerInterface $plugin */ + + /** @var \Drupal\Core\Plugin\PluginFormInterface $plugin */ $plugin = $form_state->get('plugin'); $plugin->validateConfigurationForm($form, $form_state); } @@ -125,18 +108,16 @@ class HandlerConfigurationForm extends EntityForm { parent::submitForm($form, $form_state); // Let the plugin update its configuration from the form. - /** @var \Drupal\inmail\Plugin\inmail\Handler\HandlerInterface $plugin */ + /** @var \Drupal\Core\Plugin\PluginFormInterface|\Drupal\Component\Plugin\ConfigurablePluginInterface $plugin */ $plugin = $form_state->get('plugin'); $plugin->submitConfigurationForm($form, $form_state); // Copy plugin configuration to the entity for persistence. The reason for // not doing this by overriding copyFormValuesToEntity is that the plugin // submit handler has to happen first. - /** @var \Drupal\inmail\Entity\HandlerConfig $entity */ + /** @var \Drupal\inmail\Entity\PluginConfigEntity $entity */ $entity = $this->getEntity(); $entity->setConfiguration($plugin->getConfiguration()); - - $form_state->setRedirect('inmail.handler_list'); } } diff --git a/src/Plugin/QueueWorker/Fetch.php b/src/Plugin/QueueWorker/Fetch.php deleted file mode 100644 index 11bc4a6..0000000 --- a/src/Plugin/QueueWorker/Fetch.php +++ /dev/null @@ -1,61 +0,0 @@ -processQueue = $process_queue; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static($configuration, $plugin_id, $plugin_definition, $container->get('queue')->get('inmail_process', TRUE)); - } - - /** - * {@inheritdoc} - */ - public function processItem($data) { - if ($data instanceof FetcherInterface) { - foreach ($data->fetch() as $raw) { - $this->processQueue->createItem($raw); - } - } - } - -} diff --git a/src/Plugin/QueueWorker/Process.php b/src/Plugin/QueueWorker/Process.php deleted file mode 100644 index 22a4b9f..0000000 --- a/src/Plugin/QueueWorker/Process.php +++ /dev/null @@ -1,54 +0,0 @@ -messageProcessor = $message_processor; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static($configuration, $plugin_id, $plugin_definition, $container->get('inmail.processor')); - } - - /** - * {@inheritdoc} - */ - public function processItem($data) { - $this->messageProcessor->process($data); - } - -} diff --git a/src/Plugin/inmail/Fetcher/ImapFetcher.php b/src/Plugin/inmail/Fetcher/ImapFetcher.php index 547e89f..c245e95 100644 --- a/src/Plugin/inmail/Fetcher/ImapFetcher.php +++ b/src/Plugin/inmail/Fetcher/ImapFetcher.php @@ -24,13 +24,11 @@ class ImapFetcher extends FetcherBase { */ public function fetch() { // Get details from config and connect. - // @todo Inject config. // @todo Return silently if not configured. - // @todo Return noisily if misconfigured... - $config = \Drupal::config('inmail.settings')->get('imap'); - $mailbox_flags = $config['ssl'] ? '/ssl' : ''; - $mailbox = '{' . $config['host'] . ':' . $config['port'] . $mailbox_flags . '}'; - $imap_res = imap_open($mailbox, $config['username'], $config['password']); + // @todo Return noisily if misconfigured. Possibly stop retrying. + $mailbox_flags = $this->configuration['ssl'] ? '/ssl' : ''; + $mailbox = '{' . $this->configuration['host'] . ':' . $this->configuration['port'] . $mailbox_flags . '}'; + $imap_res = imap_open($mailbox, $this->configuration['username'], $this->configuration['password']); if (!$imap_res) { // @todo Inject logger. @@ -40,15 +38,25 @@ class ImapFetcher extends FetcherBase { } // Find IDs of unread messages. - // @todo Allow choosing A) only read UNSEEN and mark unread or B) read all and delete. + // @todo Introduce options for message selection: + // - only read UNSEEN and mark unread + // - read all and delete (and optionally expunge) + // - keep track of current UID, read all with higher UID + // In the UI, warn about possible interference with other IMAP connections + // marking messages as read. $unread_ids = imap_search($imap_res, 'UNSEEN') ?: array(); + $batch_ids = array_splice($unread_ids, 0, $this->configuration['batch_size']); // Get the header + body of each message. $raws = array(); - foreach ($unread_ids as $unread_id) { + foreach ($batch_ids as $unread_id) { $raws[] = imap_fetchheader($imap_res, $unread_id) . imap_body($imap_res, $unread_id); } + // Save number of unread messages. + // @todo Create a Monitoring sensor for this state key. + \Drupal::state()->set('inmail.fetcher.imap.remaining', count($unread_ids)); + // Close resource and return messages. imap_close($imap_res); return $raws; @@ -60,11 +68,12 @@ class ImapFetcher extends FetcherBase { public function defaultConfiguration() { return array( 'host' => '', - // Standard non-SSL IMAP port as defined by 3501. + // Standard non-SSL IMAP port as defined by RFC 3501. 'port' => 143, 'ssl' => FALSE, 'username' => '', 'password' => '', + 'batch_size' => '100', ); } @@ -72,6 +81,7 @@ class ImapFetcher extends FetcherBase { * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + // @todo Add descriptions here and/or in FetcherBase. $form['host'] = array( '#type' => 'textfield', '#title' => $this->t('Host'), @@ -92,11 +102,16 @@ class ImapFetcher extends FetcherBase { '#title' => $this->t('Username'), '#default_value' => $this->configuration['username'], ); - // @todo Hide password field unless user checks to change password. + // @todo Password field cannot have #default_value. Hide field unless user checks to change password? $form['password'] = array( '#type' => 'password', '#title' => $this->t('Password'), ); + $form['batch_size'] = array( + '#type' => 'number', + '#title' => $this->t('Batch size'), + '#description' => $this->t('How many messages to fetch on each invocation.'), + ); return $form; } @@ -111,6 +126,7 @@ class ImapFetcher extends FetcherBase { 'ssl' => $form_state->getValue('ssl'), 'username' => $form_state->getValue('username'), 'password' => $form_state->getValue('password'), + 'batch_size' => $form_state->getValue('batch_size'), )); }