toolshed-8.x-1.x-dev/modules/toolshed_search/src/Plugin/SolrConnector/SettingsSolrConnector.php
modules/toolshed_search/src/Plugin/SolrConnector/SettingsSolrConnector.php
<?php
namespace Drupal\toolshed_search\Plugin\SolrConnector;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Site\Settings;
use Drupal\search_api_solr\Plugin\SolrConnector\StandardSolrConnector;
use Drupal\search_api_solr\SearchApiSolrException;
use Solarium\QueryType\Update\Query\Query;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Solr connector where the connection settings are from the settings file.
*
* @SolrConnector(
* id = "toolshed_settings",
* label = @Translation("Settings file values"),
* description = @Translation("A connector that uses host settings from the site's settings.php file.")
* )
*/
class SettingsSolrConnector extends StandardSolrConnector implements ContainerFactoryPluginInterface {
/**
* The site settings for the currently active site.
*
* @var \Drupal\Core\Site\Settings
*/
protected Settings $settings;
/**
* Create a new instance of the SettingsSolrConnector connector plugin.
*
* @param array $configuration
* Plugin configuration array.
* @param string $plugin_id
* The plugin unique identifier.
* @param mixed $plugin_definition
* Plugin definition from the plugin manager.
* @param \Drupal\Core\Site\Settings $settings
* Setting from the site settings file.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, Settings $settings) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->settings = $settings;
try {
$this->configuration = $this->generateConfiguration($this->configuration);
}
catch (SearchApiSolrException $e) {
// If there was a settings error, log it and prevent use of these options.
$this
->getLogger()
->error($e->getMessage());
}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
$plugin = new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('settings')
);
$plugin->eventDispatcher = $container->get('event_dispatcher');
return $plugin;
}
/**
* Resolves the 'solr_instance' configuration value into Solr host settings.
*
* @param array $configuration
* Configuration to use in as defaults and to determine which set of
* settings to use from the Drupal settings.
*
* @return array
* The a configuration with the settings applied if the appropriate settings
* could be found with defaults applied. If no settings were found, it
* will just return the original settings back.
*/
protected function generateConfiguration(array $configuration): array {
$instances = $this->settings->get('solr_core_connector.hosts');
$instance = $configuration['solr_instance'] ?? '';
$settings = [];
if (!empty($instances[$instance])) {
$solrConfig = $instances[$instance] + parent::defaultConfiguration();
$violations = [];
// Validate the protocol scheme type.
if (!preg_match('#https?#', $solrConfig['scheme'])) {
$violations[] = $this->t('Only "http" and "https" are supported as scheme types.');
}
else {
$settings['scheme'] = $solrConfig['scheme'];
}
// Validate the host name.
if (preg_match('#[^\w\-_.]#', $solrConfig['host'])) {
$violations[] = $this->t('Provider host should only contain letters, numbers, periods, hyphens or underscores.');
}
else {
$settings['host'] = $solrConfig['host'];
}
// Validate the Solr path setting.
if (strpos($solrConfig['path'], '/') !== 0) {
$violations[] = $this->t('Provider path should start with a "%i"', ['%i' => '/']);
}
else {
$settings['path'] = $solrConfig['path'];
}
// Validate a Solr core name.
if (preg_match('#^/|[^\w\-_]#', $solrConfig['core'], $matches)) {
$violations[] = (strpos($matches[0], '/') === 0)
? $this->t('Provider core should not start with "%i".', ['%i' => '/'])
: $this->t('Provider core should only contain letters, numbers, hyphens or underscores.');
}
else {
$settings['core'] = $solrConfig['core'];
}
$port = intval($solrConfig['port'] ?? 8983);
if ($port > 0 && $port < 65535) {
$settings['port'] = $port;
}
else {
$violations[] = $this->t('Invalid port specified.');
}
// Apply basic auth if the credentials are provided, through settings.
if (!empty($solrConfig['user']) && !empty($solrConfig['pass'])) {
$settings['username'] = $solrConfig['user'];
$settings['password'] = $solrConfig['pass'];
}
if (!empty($violations)) {
throw new SearchApiSolrException(sprintf(
"Format validation for the following Solr hosting settings: %s",
'<ul><li>' . implode("</li><li> - ", $violations) . '</li></ul>'
));
}
}
return $settings + $configuration;
}
/**
* {@inheritdoc}
*
* @throws \Exception
* If this index in read-only mode.
*/
public function getUpdateQuery(): Query {
if (!empty($this->configuration['read_only'])) {
$message = 'The Search API Server serving this index is currently in read-only mode.';
$this->getLogger()->error($message);
throw new \Exception($message);
}
return parent::getUpdateQuery();
}
/**
* Get a list of keys to exclude from the Solr configurations.
*
* @return string[]
* List of keys which to exclude from the connector configuration.
*/
protected function getExcludeConfigKeys(): array {
return [
'scheme' => 'scheme',
'host' => 'host',
'path' => 'path',
'core' => 'core',
'port' => 'port',
];
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration(): array {
// Allow all the defaults except for the ones provided by the settings.php
// configuration arrays (@see generateConfiguration()).
$defaults = array_diff_key(parent::defaultConfiguration(), $this->getExcludeConfigKeys());
return $defaults + [
'solr_instance' => '',
];
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration): void {
// Set this to silence some notice warnings in the parent class.
$configuration['port'] = 8983;
parent::setConfiguration($configuration);
// Ensure configurations set by the settings file should be filtered out
// of the plugin configurations, and not saved.
$this->configuration = array_diff_key($this->configuration, $this->getExcludeConfigKeys());
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
$form = parent::buildConfigurationForm($form, $form_state);
$instances = $this->settings->get('solr_core_connector.hosts', []);
$solrOpts = [];
foreach ($instances as $key => $solrInstance) {
$solrOpts[$key] = $solrInstance['label'] ?? $key;
}
if ($solrOpts) {
$form['solr_instance'] = [
'#type' => 'select',
'#title' => $this->t('Solr instance settings'),
'#required' => TRUE,
'#weight' => -5,
'#options' => $solrOpts,
'#default_value' => $this->configuration['solr_instance'],
];
}
else {
$form['solr_instance'] = [
'#theme' => 'status_messages',
'#status_headings' => [
'error' => $this->t('No Solr servers available'),
],
'#message_list' => [
'error' => [
$this->t('Unable to find any Solr configurations in the settings.php file. Add your Solr server settings to $settings["solr_core_connector.hosts"], see README.md for a full description of usage.'),
],
],
'#weight' => -5,
];
}
// These are replaces by their respective @settings values.
foreach ($this->getExcludeConfigKeys() as $configKey) {
unset($form[$configKey]);
}
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void {
if (!$form_state->hasAnyErrors()) {
try {
$values = $form_state->getValues();
if (empty($values['solr_instance'])) {
throw new \InvalidArgumentException();
}
$values = $this->generateConfiguration($values);
// Try to orchestrate a server link from form values.
$solr = $this->createClient($values);
$solr->createEndpoint($values + ['key' => 'search_api_solr'], TRUE);
$this->getServerLink();
}
catch (SearchApiSolrException $e) {
$form_state->setError($form['solr_instance'], Markup::create($e->getMessage()));
}
catch (\InvalidArgumentException $e) {
$form_state->setError($form['solr_instance'], $this->t('The server link generated from the form values is illegal.'));
}
}
}
}
