vertex_ai_search-1.0.0-beta4/src/Plugin/Search/VertexAISearch.php
src/Plugin/Search/VertexAISearch.php
<?php
namespace Drupal\vertex_ai_search\Plugin\Search;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Pager\PagerManagerInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\search\Plugin\ConfigurableSearchPluginBase;
use Drupal\vertex_ai_search\VertexAutocompletePluginManager;
use Drupal\vertex_ai_search\VertexConfigurableJavaScriptPluginManager;
use Drupal\vertex_ai_search\VertexSearchFilterPluginManager;
use Drupal\vertex_ai_search\VertexSearchResultsPluginManager;
use Drupal\vertex_ai_search\Service\VertexSearchManagerInterface;
use Google\ApiCore\ApiException;
use Google\Cloud\DiscoveryEngine\V1\Client\DataStoreServiceClient;
use Google\Cloud\DiscoveryEngine\V1\ListDataStoresRequest;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Handles searching for node entities using the Search module index.
*
* @SearchPlugin(
* id = "vertex_ai_search",
* title = @Translation("Vertex AI Search")
* )
*/
class VertexAISearch extends ConfigurableSearchPluginBase implements AccessibleInterface {
/**
* The Vertex AI Search Manager.
*
* @var \Drupal\vertex_ai_search\VertexSearchManagerInterface
*/
protected $searchManager;
/**
* Vertex Autocomplete Plugin Manager.
*
* @var \Drupal\vertex_ai_search\VertexAutocompletePluginManager
*/
protected $autoPluginManager;
/**
* Vertex Search Results Plugin Manager.
*
* @var \Drupal\vertex_ai_search\VertexSearchResultsPluginManager
*/
protected $resultsPluginManager;
/**
* Vertex Search Configurable JavaScript Plugin Manager.
*
* @var \Drupal\vertex_ai_search\VertexConfigurableJavaScriptPluginManager
*/
protected $jsPluginManager;
/**
* Vertex Search Filter Plugin Manager.
*
* @var \Drupal\vertex_ai_search\VertexSearchFilterPluginManager
*/
protected $filterPluginManager;
/**
* PagerManager service object.
*
* @var \Drupal\Core\Pager\PagerManagerInterface
*/
protected $pagerManager;
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* Logger service object.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructor.
*
* @param array $configuration
* Configuration array containing information about search page.
* @param string $plugin_id
* Identifier of custom plugin.
* @param array $plugin_definition
* Provides definition of search plugin.
* @param \Drupal\vertex_ai_search\Service\VertexSearchManagerInterface $search_manager
* The vertex search_manager service.
* @param \Drupal\vertex_ai_search\VertexAutocompletePluginManager $autoPluginManager
* Vertex Autocomplete Plugin Manager.
* @param \Drupal\vertex_ai_search\VertexConfigurableJavaScriptPluginManager $jsPluginManager
* Vertex Search Configurable JavaScript Plugin Manager.
* @param \Drupal\vertex_ai_search\VertexSearchResultsPluginManager $resultsPluginManager
* Vertex Search Results Plugin Manager.
* @param \Drupal\vertex_ai_search\VertexSearchFilterPluginManager $filterPluginManager
* Vertex Search Filter Plugin Manager.
* @param \Drupal\Core\Pager\PagerManagerInterface $pagerManager
* Pager Manager.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
* @param Psr\Log\LoggerInterface $logger
* This is the Logger.
*/
public function __construct(
array $configuration,
$plugin_id,
array $plugin_definition,
VertexSearchManagerInterface $search_manager,
VertexAutocompletePluginManager $autoPluginManager,
VertexConfigurableJavaScriptPluginManager $jsPluginManager,
VertexSearchResultsPluginManager $resultsPluginManager,
VertexSearchFilterPluginManager $filterPluginManager,
PagerManagerInterface $pagerManager,
RendererInterface $renderer,
LoggerInterface $logger,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->searchManager = $search_manager;
$this->autoPluginManager = $autoPluginManager;
$this->jsPluginManager = $jsPluginManager;
$this->resultsPluginManager = $resultsPluginManager;
$this->filterPluginManager = $filterPluginManager;
$this->pagerManager = $pagerManager;
$this->renderer = $renderer;
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('vertex_ai_search.search_manager'),
$container->get('plugin.manager.vertex_autocomplete'),
$container->get('plugin.manager.vertex_configurable_js'),
$container->get('plugin.manager.vertex_search_results'),
$container->get('plugin.manager.vertex_search_filter'),
$container->get('pager.manager'),
$container->get('renderer'),
$container->get('logger.channel.vertex_ai_search'),
);
}
/**
* {@inheritdoc}
*/
public function access($operation = 'view', ?AccountInterface $account = NULL, $return_as_object = FALSE) {
$searchPageId = $this->configuration['id'];
$permission = "use $searchPageId custom search page";
$result = AccessResult::allowedIf($account->hasPermission($permission))->cachePerPermissions();
return $return_as_object ? $result : $result->isAllowed();
}
/**
* {@inheritdoc}
*/
public function execute() {
$resource = $this->searchManager->executeSearch(
$this->configuration,
$this->getParameters()
);
return $resource;
}
/**
* {@inheritdoc}
*/
public function buildResults() {
// Perform search and retrieve the results.
$results = $this->execute();
if (!empty($results['error'])) {
if (!empty($results['error'])) {
$output[] = [
'#theme' => 'vertex_ai_search_error_message',
'#message' => $results['error'],
'#plugin_id' => $this->getPluginId(),
'#attached' => [
'library' => [
'vertex_ai_search/vertexAiSearchResults',
],
],
];
}
return $output;
}
// Begin building the search results page.
$output = [];
// See if the query was corrected.
if (!empty($results['messages']['correction']) || $results['correctedQuery']) {
$output[] = [
'#theme' => 'vertex_ai_search_spelling_correction',
'#spelling' => $results['correctedQuery'],
'#corrected' => $results['queryCorrected'],
'#url' => $results['tokens']['vertex_ai_search_corrected_keyword_url'],
'#message' => $results['messages']['correction'],
'#corrected_url' => $results['tokens']['vertex_ai_search_corrected_keyword_url'],
'#original_url' => $results['tokens']['vertex_ai_search_original_keyword_url'],
'#corrected_keyword' => $results['tokens']['vertex_ai_search_corrected_keyword'],
'#original_keyword' => $results['tokens']['vertex_ai_search_original_keyword'],
'#plugin_id' => $this->getPluginId(),
'#attached' => [
'library' => [
'vertex_ai_search/vertexAiSearchResults',
],
],
];
// Modify search keywords if they were automatically corrected.
if ($results['queryCorrected']) {
$this->setSearch($results['correctedQuery'], $this->getParameters(), $this->getAttributes());
}
}
// Display results message.
if (!empty($results['messages']['results'])) {
$output[] = [
'#theme' => 'vertex_ai_search_' . $results['messages']['results_type'],
'#message' => $results['messages']['results'],
'#term' => $results['tokens']['vertex_ai_search_keywords'],
'#plugin_id' => $this->getPluginId(),
'#attached' => [
'library' => [
'vertex_ai_search/vertexAiSearchResults',
],
],
];
}
foreach ($results['results'] as $result) {
$derivedStructData = $result['document']['derivedStructData'];
$parsedResult['htmlTitle'] = $derivedStructData['htmlTitle'];
$parsedResult['link'] = $derivedStructData['link'];
$parsedResult['snippet'] = NULL;
if ($this->configuration['result_parts'] === 'SNIPPETS') {
$parsedResult['snippet'] = $derivedStructData['snippets'][0]['htmlSnippet'] ?? NULL;
}
$output[] = [
'#theme' => 'vertex_ai_search_result',
'#result' => $parsedResult,
'#term' => $this->getKeywords(),
'#plugin_id' => $this->getPluginId(),
'#attached' => [
'library' => [
'vertex_ai_search/vertexAiSearchResults',
],
],
];
}
// Retrieve any VertexSearchResultPlugins and modified page results.
$resultsPluginDefinitions = $this->resultsPluginManager->getDefinitions();
foreach ($resultsPluginDefinitions as $pluginKey => $pluginDefinition) {
$resultsPlugin = $this->resultsPluginManager->createInstance(
$pluginKey
);
$output = $resultsPlugin->manipulatePageResults($this->configuration, $this->getParameters(), $output, $results['curated']);
}
return $output;
}
/**
* {@inheritdoc}
*/
public function searchFormAlter(array &$form, FormStateInterface $form_state) {
unset($form['basic']);
if (!$this->configuration['displaySearchForm']) {
return;
}
$form['#attributes']['class'][] = 'vertex-ai-search-search-box-form';
$form['#attributes']['id'] = 'vertex-ai-search-search-box-form';
$form['#theme'] = 'vertex_ai_search_search_page_form';
// Search term element.
$form['keys'] = [
'#type' => 'search',
'#title' => $this->t('Search'),
'#title_display' => 'none',
'#default_value' => $this->getKeywords(),
];
// Determine if Autocomplete should be enabled or not.
if (!empty($this->configuration['autocomplete_enable']) &&
!empty($this->configuration['autocomplete_source'])) {
$form['keys']['#autocomplete_route_name'] = 'vertex_ai_search.autocomplete';
$form['keys']['#autocomplete_route_parameters'] = ['search_page_id' => $this->configuration['id']];
}
// Add class attributes to search keys input element.
if (!empty($this->configuration['classSearchKeys'])) {
$form['keys']['#attributes']['class'][] = $this->configuration['classSearchKeys'];
}
// Add aria label attribute to search keys input element.
if (!empty($this->configuration['searchInputAriaLabel'])) {
$form['keys']['#attributes']['aria-label'] = $this->configuration['searchInputAriaLabel'];
}
// Search submit element.
$form['submit'] = [
'#type' => 'submit',
'#value' => 'Search',
];
// Add class attributes to search submit button.
if (!empty($this->configuration['classSearchSubmit'])) {
$form['submit']['#attributes']['class'][] = $this->configuration['classSearchSubmit'];
}
// Add aria label to search submit button.
if (!empty($this->configuration['searchSubmitAriaLabel'])) {
$form['submit']['#attributes']['aria-label'] = $this->configuration['searchSubmitAriaLabel'];
}
// Add js plugin libraries.
if (!empty($this->configuration['js_enable'])) {
foreach ($this->configuration['js_plugin'] as $plugin_id) {
$jsPlugin = $this->jsPluginManager->createInstance(
$plugin_id,
$this->configuration
);
if ($jsPlugin instanceof PluginFormInterface) {
$jsPlugin->alterSearchForm($form, $form_state);
}
}
}
}
/**
* {@inheritdoc}
*/
public function buildSearchUrlQuery(FormStateInterface $form_state) {
$keys = $form_state->getValue('keys');
return [
'keys' => $keys,
'page' => $form_state->getValue('page'),
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form += $form['vertex_ai_search_config_info'] = [
'#title' => $this->t('Search Page Configuration Documentation'),
'#type' => 'details',
'#description' => $this->t('Refer to the <a href=":configPage">Search
Page Configuration Options</a> documentation for more detailed
information on configuring a custom Vertex AI Search page.',
[
':configPage' => 'https://www.drupal.org/docs/extending-drupal/contributed-modules/contributed-module-documentation/vertex-ai-search/search-page-configuration-options',
]),
'#open' => TRUE,
];
$form += $this->retrieveVertexAuthenticationElements($form, $form_state);
$form += $this->retrieveVertexServingConfigElements($form, $form_state);
$form += $this->retrieveVertexAutocompleteElements($form, $form_state);
$form += $this->retrieveVertexSearchOptionElements($form, $form_state);
$form += $this->retrieveVertexConfigurableJsElements($form, $form_state);
$form += $this->retrieveVertexSearchFilterElements($form, $form_state);
$form += $this->retrieveVertexExclusionListElements($form, $form_state);
$this->groupFilteringElements($form, $form_state);
$form += $this->retrieveVertexFloodConfigElements($form, $form_state);
$form += $this->retrieveVertexSearchMessageElements($form, $form_state);
$form += $this->retrieveVertexAdvancedOptionElements($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
/** @var \Drupal\search\Form\SearchPageEditForm $formObject */
$formObject = $form_state->getFormObject();
/** @var \Drupal\search\Entity\SearchPage $searchPageEntity */
$searchPageEntity = $formObject->getEntity();
/** @var \Drupal\vertex_ai_search\Plugin\Search\VertexAISearch $plugin */
$plugin = $searchPageEntity->getPlugin();
// Update the SearchEntity plugin configuration.
$plugin->setConfiguration($form_state->getValues());
if (!empty($this->configuration['autocomplete_source'])) {
$autoPlugin = $this->autoPluginManager->createInstance(
$this->configuration['autocomplete_source'],
$this->configuration
);
// Call the submit function on the autocomplete plugin.
if ($autoPlugin instanceof PluginFormInterface) {
$autoPlugin->submitConfigurationForm($form, $form_state);
}
}
$enabled = (array) $form_state->getValue('js_plugin');
$this->configuration['js_plugin'] = array_filter($enabled);
$js_config = [];
foreach ($enabled as $plugin_id) {
if ($this->jsPluginManager->hasDefinition($plugin_id)) {
$instance = $this->jsPluginManager->createInstance($plugin_id, $this->configuration);
$instance->submitConfigurationForm($form, $form_state);
$js_config[$plugin_id] = $instance->getConfiguration();
}
}
$this->configuration['js_config'] = $js_config;
}
/**
* Get the project from the credentials.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* Updating a text container to display project ID.
*/
public function lookupProject(array &$form, FormStateInterface $form_state) {
$ajaxResponse = new AjaxResponse();
$values = $form_state->getValues();
$credPath = $values['service_account_credentials_file'] ?? '';
if ($credPath) {
$credentials = json_decode(file_get_contents($credPath), TRUE);
$output = $this->t('Project ID: %project_id', [
'%project_id' => $credentials['project_id'] ?? 'Project ID not found',
]);
$ajaxResponse->addCommand(new ReplaceCommand('#list-project-container', '<div id="list-project-container">' . $output . '</div>'));
}
return $ajaxResponse;
}
/**
* Get the list of data stores from the Google cloud project.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* Updating a text container to display data stores.
*/
public function lookupDatastores(array &$form, FormStateInterface $form_state) {
$ajaxResponse = new AjaxResponse();
$output = '';
$values = $form_state->getValues();
$credPath = $values['service_account_credentials_file'] ?? '';
$project_id = $values['google_cloud_project_id'] ?? '';
$location = $values['google_cloud_location'] ?? 'global';
if ($credPath && $project_id && $location) {
$transport = $values['transport_method'] ?? NULL;
// Get client parameters and create datastore Service Client.
$clientParameters['credentials'] = json_decode(
file_get_contents($credPath),
TRUE
);
if (!empty($transport)) {
$clientParameters['transport'] = $transport;
}
$dataStoreListItems = [];
try {
$dataStoreServiceClient = new DataStoreServiceClient($clientParameters);
$request = (new ListDataStoresRequest())
->setParent('projects/' . $project_id . '/locations/' . $location);
$response = $dataStoreServiceClient->listDataStores($request);
foreach ($response as $element) {
$dataStoreJson = $element->serializeToJsonString();
$dataStore = json_decode($dataStoreJson);
$dataStoreURI = explode('/', $dataStore->name);
$dataStoreListItems[] = $this->t(':displayName: %id', [
':displayName' => $dataStore->displayName,
'%id' => $dataStoreURI[count($dataStoreURI) - 1],
]);
}
}
catch (ApiException $ex) {
$dataStoreListItems[] = $this->t('Error retrieving data stores: @message', [
'@message' => $ex->getMessage(),
]);
$this->logger->error(
'Vertex AI Search APIException error occurred: @error.
Message: @message.',
[
'@error' => $ex->getStatus(),
'@message' => $ex->getMessage(),
],
);
}
catch (\Exception $ex) {
$dataStoreListItems[] = $this->t('Error retrieving data stores: @message', [
'@message' => $ex->getMessage(),
]);
$this->logger->critical('Vertex AI Search Exception error occurred.
Message: @message. Details: @details.',
[
'@message' => $ex->getMessage(),
'@details' => $ex->__toString(),
],
);
}
$dataStoreList = [
'#theme' => 'item_list',
'#title' => $this->t('List of Data Stores (copy the ID to the Data Store ID field)'),
'#type' => 'ul',
'#items' => $dataStoreListItems,
'#empty' => $this->t('No data store found.'),
];
$output = $this->renderer->render($dataStoreList);
$ajaxResponse->addCommand(new ReplaceCommand('#list-datastores-container', '<div id="list-datastores-container">' . $output . '</div>'));
}
return $ajaxResponse;
}
/**
* Get the value from example select field and fill.
*
* @return array
* New autocomplete option elements.
*/
public function retrieveAutocompleteOptions(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
if (empty($values['autocomplete_source'])) {
$form['vertex_ai_search_autocomplete']['auto_options'] =
array_diff_key(
$form['vertex_ai_search_autocomplete']['auto_options'],
array_flip($values['pluginElements'])
);
$form['vertex_ai_search_autocomplete']['auto_options']['pluginElements']['#value'] = [];
}
// Return the autocomplete options.
return $form['vertex_ai_search_autocomplete']['auto_options'];
}
/**
* Get the elements from the selected plugin's config form.
*
* @param array $form
* The configuration form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current state of the configuration form.
*
* @return array
* New search filter option elements.
*/
public function retrieveFilterOptions(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
if (empty($values['filter_plugin'])) {
$form['vertex_ai_search_filter']['filter_options'] =
array_diff_key(
$form['vertex_ai_search_filter']['filter_options'],
array_flip($values['pluginElements'])
);
$form['vertex_ai_search_filter']['filter_options']['pluginElements']['#value'] = [];
}
return $form['vertex_ai_search_filter']['filter_options'];
}
/**
* Helper to add vertex authentication elements to config form.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexAuthenticationElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_authentication'] = [
'#title' => $this->t('Vertex AI Agent Builder Authentication'),
'#type' => 'details',
'#open' => TRUE,
];
// Path and name of Service Account credentials file.
$form['vertex_ai_authentication']['service_account_credentials_file'] = [
'#title' => $this->t('Service Account Credentials'),
'#type' => 'textfield',
'#default_value' => $this->configuration['service_account_credentials_file'] ?? '',
'#required' => TRUE,
];
return $form;
}
/**
* Helper to add vertex app configuration elements to config form.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexServingConfigElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_config_info'] = [
'#title' => $this->t('Vertex AI Agent Builder Serving Config'),
'#type' => 'details',
'#open' => TRUE,
];
$form['vertex_ai_config_info']['lookup_project'] = [
'#type' => 'button',
'#value' => $this->t('Lookup Project'),
'#ajax' => [
'callback' => [$this, 'lookupProject'],
'wrapper' => 'list-project-container',
],
'#limit_validation_errors' => [],
];
$form['vertex_ai_config_info']['lookup_project_container'] = [
'#type' => 'container',
'#attributes' => ['id' => 'list-project-container'],
];
// Vertex AI Search Google Cloud Platform project id.
$form['vertex_ai_config_info']['google_cloud_project_id'] = [
'#title' => $this->t('Google Cloud Project ID'),
'#type' => 'textfield',
'#default_value' => $this->configuration['google_cloud_project_id'] ?? '',
'#required' => TRUE,
];
// Vertex AI Search Google Cloud Platform application cloud location.
$form['vertex_ai_config_info']['google_cloud_location'] = [
'#title' => $this->t('Google Cloud Location'),
'#type' => 'textfield',
'#default_value' => $this->configuration['google_cloud_location'] ?? 'global',
'#required' => TRUE,
];
$form['vertex_ai_config_info']['lookup_datastores'] = [
'#type' => 'button',
'#value' => $this->t('Lookup Data Stores'),
'#ajax' => [
'callback' => [$this, 'lookupDatastores'],
'wrapper' => 'list-datastores-container',
],
'#limit_validation_errors' => [],
];
$form['vertex_ai_config_info']['list_of_datastores_container'] = [
'#type' => 'container',
'#attributes' => ['id' => 'list-datastores-container'],
];
// Vertex AI Search Google Cloud Platform data store.
$form['vertex_ai_config_info']['vertex_ai_data_store_id'] = [
'#title' => $this->t('Vertex AI Data Store ID'),
'#type' => 'textfield',
'#default_value' => $this->configuration['vertex_ai_data_store_id'] ?? '',
'#required' => TRUE,
'#description' => $this->t('Use the lookup button to find data stores using the project ID and location.'),
];
// Vertex AI Search Google Cloud Platform Serving configuration.
$form['vertex_ai_config_info']['vertex_ai_serving_config'] = [
'#title' => $this->t('Vertex AI Serving Configuration'),
'#type' => 'textfield',
'#default_value' => $this->configuration['vertex_ai_serving_config'] ?? 'default_search',
'#required' => TRUE,
];
return $form;
}
/**
* Helper to add vertex autocomplete elements to config form.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexAutocompleteElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_search_autocomplete'] = [
'#title' => $this->t('Search Autocomplete'),
'#type' => 'details',
'#description' => $this->t('Refer to the Developer Documentation for more
information on <a href=":autoPlugin">developing a custom Autocomplete
Plugin</a>.', [
':autoPlugin' => 'https://www.drupal.org/docs/extending-drupal/contributed-modules/contributed-module-documentation/vertex-ai-search/developer-documentation#s-autocomplete-plugin',
]),
'#open' => TRUE,
];
// Enable Autocomplete (or not).
$form['vertex_ai_search_autocomplete']['autocomplete_enable'] = [
'#title' => $this->t('Enable Autocomplete'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['autocomplete_enable'] ?? '',
'#description' => $this->t('Enable an autocomplete mechanism.'),
];
// Enable Autocomplete (or not) on core search block form.
$form['vertex_ai_search_autocomplete']['autocomplete_enable_block'] = [
'#title' => $this->t('Enable Autocomplete on Core Search Block Form'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['autocomplete_enable_block'] ?? '',
'#description' => $this->t(
'Enable autocomplete on search block form as well as main search form.'
),
'#states' => [
'visible' => [
':input[name="autocomplete_enable"]' => ['checked' => TRUE],
],
],
];
// Get Available Vertex Autocomplete Plugins.
$autoDefinitions = $this->autoPluginManager->getDefinitions();
$autoOptions[NULL] = $this->t('-- Select an Autocomplete Plugin --');
foreach ($autoDefinitions as $key => $definition) {
$autoOptions[$key] = $definition['title'];
}
// Trigger Length.
$form['vertex_ai_search_autocomplete']['autocomplete_trigger_length'] = [
'#title' => $this->t('Autocomplete Trigger Length'),
'#type' => 'number',
'#min' => 1,
'#step' => 1,
'#default_value' => $this->configuration['autocomplete_trigger_length'] ?? 4,
'#states' => [
'visible' => [
':input[name="autocomplete_enable"]' => ['checked' => TRUE],
],
],
];
// Max Autocomplete Suggestions.
$form['vertex_ai_search_autocomplete']['autocomplete_max_suggestions'] = [
'#title' => $this->t('Max Suggestions'),
'#type' => 'number',
'#min' => 1,
'#step' => 1,
'#default_value' => $this->configuration['autocomplete_max_suggestions'] ?? 10,
'#states' => [
'visible' => [
':input[name="autocomplete_enable"]' => ['checked' => TRUE],
],
],
];
// Autocomplete Source (plugin).
$form['vertex_ai_search_autocomplete']['autocomplete_source'] = [
'#title' => $this->t('Autocomplete Source'),
'#type' => 'select',
'#options' => $autoOptions,
'#default_value' => $this->configuration['autocomplete_source'] ?? NULL,
'#states' => [
'visible' => [
':input[name="autocomplete_enable"]' => ['checked' => TRUE],
],
'required' => [
':input[name="autocomplete_enable"]' => ['checked' => TRUE],
],
],
'#ajax' => [
'callback' => [$this, 'retrieveAutocompleteOptions'],
'disable-refocus' => FALSE,
'event' => 'change',
'wrapper' => 'auto-options-wrapper',
'progress' => [
'type' => 'throbber',
'message' => $this->t('Retrieving Options...'),
],
],
];
// Autocomplete Plugin-Specific Options.
$form['vertex_ai_search_autocomplete']['auto_options'] = [
'#title' => $this->t('Autocomplete Options'),
'#type' => 'details',
'#open' => TRUE,
'#prefix' => '<div id="auto-options-wrapper">',
'#suffix' => '</div>',
'#states' => [
'visible' => [
':input[name="autocomplete_enable"]' => ['checked' => TRUE],
':input[name="autocomplete_source"]' => ['!value' => ''],
],
],
'#cache' => [
'max-age' => 0,
],
];
$autoPlugin = NULL;
if (!empty($this->configuration['autocomplete_source'])) {
$autoPlugin = $this->autoPluginManager->createInstance(
$this->configuration['autocomplete_source'],
$this->configuration
);
}
$currentForm = $form;
if ($autoPlugin instanceof PluginFormInterface) {
$form += $autoPlugin->buildConfigurationForm($form, $form_state);
}
$autocompleteElements = array_diff_key($form, $currentForm);
foreach ($autocompleteElements as $key => $element) {
$form['vertex_ai_search_autocomplete']['auto_options'][$key] = $element;
unset($form[$key]);
}
$form['vertex_ai_search_autocomplete']['auto_options']['pluginElements'] = [
'#type' => 'hidden',
'#value' => array_keys($autocompleteElements),
];
// Manipulate form to display appropriate autocomplete plug options.
if ($newSource = $form_state->getValue('autocomplete_source')) {
// Remove unneeded elements from previous autocomplete_source.
$remove = array_keys($autocompleteElements);
$form['vertex_ai_search_autocomplete']['auto_options'] = array_diff_key(
$form['vertex_ai_search_autocomplete']['auto_options'],
array_flip($remove)
);
$this->SetConfiguration(array_diff_key($this->configuration, array_flip($remove)));
// Set autocomplete source based on form_state value ($newSource).
$this->configuration['autocomplete_source'] = $newSource;
$autoPlugin = $this->autoPluginManager->createInstance($newSource, $this->configuration);
$currentForm = $form;
if ($autoPlugin instanceof PluginFormInterface) {
$form += $autoPlugin->buildConfigurationForm($form, $form_state);
}
// Get an array of form elements specific to the autocomplete plugin.
$autocompleteElements = array_diff_key($form, $currentForm);
$form['vertex_ai_search_autocomplete']['autocomplete_source']['#default_value'] = $newSource;
// Make sure autocomplete plugin-specific elements in auto_options group.
foreach ($autocompleteElements as $key => $element) {
$form['vertex_ai_search_autocomplete']['auto_options'][$key] = $element;
$form['vertex_ai_search_autocomplete']['auto_options'][$key]['#default_value'] = $this->configuration[$key] ?? NULL;
}
// Keep a record of autocomplete-plugin specific elements.
$form['vertex_ai_search_autocomplete']['auto_options']['pluginElements'] = [
'#type' => 'hidden',
'#value' => array_keys($autocompleteElements),
];
}
return $form;
}
/**
* Helper to add vertex search option elements to config form.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexSearchOptionElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_search_options'] = [
'#title' => $this->t('Search Results Page Display Options'),
'#type' => 'details',
'#open' => TRUE,
];
// Results per page.
$form['vertex_ai_search_options']['resultsPerPage'] = [
'#title' => $this->t('Number of results per page'),
'#type' => 'select',
'#options' => [
10 => 10,
20 => 20,
30 => 30,
40 => 40,
50 => 50,
100 => 100,
],
'#default_value' => $this->configuration['resultsPerPage'] ?? '',
'#description' => $this->t(
'Note: The true limit of results returned per request depends upon the
<a href=":pageSizeDocUrl">datastore type and the indexing used.</a>',
[
':pageSizeDocUrl' => 'https://cloud.google.com/dotnet/docs/reference/Google.Cloud.DiscoveryEngine.V1Beta/latest/Google.Cloud.DiscoveryEngine.V1Beta.SearchRequest#Google_Cloud_DiscoveryEngine_V1Beta_SearchRequest_PageSize',
],
),
];
// Max number of results to retrieve.
$form['vertex_ai_search_options']['totalResultsLimit'] = [
'#title' => $this->t('Total Results Limit'),
'#type' => 'number',
'#min' => 1,
'#step' => 1,
'#default_value' => $this->configuration['totalResultsLimit'] ?? 100,
];
// Pager type.
$form['vertex_ai_search_options']['pagerType'] = [
'#title' => $this->t('Pager Type'),
'#type' => 'select',
'#options' => [
'STANDARD' => $this->t('Standard Pager'),
'VERTEX' => $this->t('Vertex'),
],
'#default_value' => $this->configuration['pagerType'] ?? 'STANDARD',
'#description' => $this->t(
"The <strong>'Standard Pager'</strong> displays multiple pages at once
and is susceptible to <a href=':estimatedUrl'>empty pages based on the
available results count being a rough estimate</a>.
If the total available results estimate is greater than the Total
Results Limit, then the Total Results Limit is used to determine the
number of pages in the pager. The <strong>'Vertex Pager'</strong>
determines if the 'next' pager element should be displayed and
incrementally shows page links as the 'next' element is clicked.",
[':estimatedUrl' => 'https://support.google.com/programmable-search/thread/220139517?hl=en&msgid=220196146'],
),
];
// Vertex Spelling-correction mode.
$form['vertex_ai_search_options']['spelling_correction_mode'] = [
'#title' => $this->t('Spelling Correction Mode'),
'#type' => 'select',
'#options' => [
'AUTO' => 'AUTO',
'SUGGESTION_ONLY' => $this->t('SUGGESTION ONLY'),
'MODE_UNSPECIFIED' => $this->t('MODE UNSPECIFIED (Defaults to AUTO)'),
],
'#default_value' => $this->configuration['spelling_correction_mode'] ?? '',
];
// Result display options.
$form['vertex_ai_search_options']['result_parts'] = [
'#title' => $this->t('Result Output'),
'#type' => 'select',
'#options' => [
'TITLE' => $this->t('Title Only'),
'SNIPPETS' => $this->t('Title and Snippet'),
],
'#default_value' => $this->configuration['result_parts'] ?? 'SNIPPETS',
];
// Vertex AI Search SafeSearch option.
$form['vertex_ai_search_options']['safeSearch'] = [
'#title' => $this->t('Use SafeSearch'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['safeSearch'] ?? '',
'#description' => $this->t('SafeSearch helps filter out explicit content in search results.'),
];
// Remove domain from results - good for development environments.
$form['vertex_ai_search_options']['removeDomain'] = [
'#title' => $this->t('Remove Domain from Result Link URLs'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['removeDomain'] ?? '',
'#description' => $this->t('Removing the domain makes all search results relative links, instead of pointing to the production domain. Useful when working with non-production domains.'),
];
// Should search form be on SERP.
$form['vertex_ai_search_options']['displaySearchForm'] = [
'#title' => $this->t('Display Search Form on results page'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['displaySearchForm'] ?? '',
];
// Class to associate with search key input element.
$form['vertex_ai_search_options']['classSearchKeys'] = [
'#title' => $this->t('Keywords Input Class'),
'#type' => 'textfield',
'#default_value' => $this->configuration['classSearchKeys'] ?? '',
'#description' => $this->t('Add optional class to the keywords input element.'),
'#states' => [
'visible' => [
':input[name="displaySearchForm"]' => ['checked' => TRUE],
],
],
];
// Aria Label to associate with search key input element.
$form['vertex_ai_search_options']['searchInputAriaLabel'] = [
'#title' => $this->t('Keywords Input Aria Label'),
'#type' => 'textfield',
'#default_value' => $this->configuration['searchInputAriaLabel'] ?? '',
'#description' => $this->t('Add optional aria-label attribute the keywords input element.'),
'#states' => [
'visible' => [
':input[name="displaySearchForm"]' => ['checked' => TRUE],
],
],
];
// Class to associate with search submit button.
$form['vertex_ai_search_options']['classSearchSubmit'] = [
'#title' => $this->t('Submit Input Class'),
'#type' => 'textfield',
'#default_value' => $this->configuration['classSearchSubmit'] ?? '',
'#description' => $this->t('Add optional class to the submit input element.'),
'#states' => [
'visible' => [
':input[name="displaySearchForm"]' => ['checked' => TRUE],
],
],
];
// Aria label to associate with search submit button.
$form['vertex_ai_search_options']['searchSubmitAriaLabel'] = [
'#title' => $this->t('Submit Input Aria Label'),
'#type' => 'textfield',
'#default_value' => $this->configuration['searchSubmitAriaLabel'] ?? '',
'#description' => $this->t('Add optional aria-label attribute the submit input element.'),
'#states' => [
'visible' => [
':input[name="displaySearchForm"]' => ['checked' => TRUE],
],
],
];
return $form;
}
/**
* Helper to add vertex configurable JavaScript elements to config form.
*
* @param array $form
* The configuration form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current state of the configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexConfigurableJsElements(array $form, FormStateInterface $form_state): array {
$form['vertex_ai_search_js'] = [
'#title' => $this->t('Search Configurable JavaScript'),
'#type' => 'details',
'#description' => $this->t('These are plugins that attach
custom JavaScript to the search page. The JavaScript can
use configuration captured here to alter search functionality
and search requests on the client side. Refer to the Developer
Documentation for more information on <a href=":jsPlugin">developing a
custom Configurable JavaScript Plugin</a>.',
[':jsPlugin' => 'https://www.drupal.org/docs/extending-drupal/contributed-modules/contributed-module-documentation/vertex-ai-search/developer-documentation#s-configurable-javascript-plugin'],
),
'#open' => FALSE,
];
// Enable JavaScript plugins.
$form['vertex_ai_search_js']['js_enable'] = [
'#title' => $this->t('Enable Configurable JavaScript'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['js_enable'] ?? 0,
];
// Get available JavaScript plugins.
$pluginDefinitions = $this->jsPluginManager->getDefinitions();
$jsOptions = [];
foreach ($pluginDefinitions as $key => $definition) {
$jsOptions[$key] = $definition['title'];
}
$form['vertex_ai_search_js']['js_plugin'] = [
'#title' => $this->t('Configurable JavaScript Plugins'),
'#type' => 'checkboxes',
'#options' => $jsOptions,
'#default_value' => $form_state->getValue('js_plugin') ?? ($this->configuration['js_plugin'] ?? []),
'#states' => [
'visible' => [
':input[name="js_enable"]' => ['checked' => TRUE],
],
'required' => [
':input[name="js_enable"]' => ['checked' => TRUE],
],
],
];
foreach ($jsOptions as $plugin_id => $plugin_label) {
if ($this->jsPluginManager->hasDefinition($plugin_id)) {
$plugin = $this->jsPluginManager->createInstance($plugin_id, $this->configuration);
$plugin_form = $plugin->buildConfigurationForm([], $form_state);
// JavaScript plugin-specific options.
$form['vertex_ai_search_js'][$plugin_id] = [
'#title' => $this->t('@plugin configuration', ['@plugin' => $plugin_label]),
'#type' => 'details',
'#open' => TRUE,
'#prefix' => '<div id="' . $plugin_id . '-wrapper">',
'#suffix' => '</div>',
'#states' => [
'visible' => [
':input[name="js_enable"]' => ['checked' => TRUE],
':input[name="js_plugin[' . $plugin_id . ']"]' => ['checked' => TRUE],
],
],
'#cache' => [
'max-age' => 0,
],
] + $plugin_form;
}
}
return $form;
}
/**
* Helper to add vertex exclusion list elements.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexExclusionListElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_search_exclusion'] = [
'#title' => $this->t('Exclusion List'),
'#type' => 'details',
'#description' => $this->t('Using the exclusion list, words,
phrases, and regex patterns can be stripped from the search
keywords. The search request will proceed after all
exclusion list matches have been removed.'),
'#open' => FALSE,
];
// Enable Exclusion List (or not).
$form['vertex_ai_search_exclusion']['exclusion_list_enable'] = [
'#title' => $this->t('Enable Exclusion List'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['exclusion_list_enable'] ?? '',
'#description' => $this->t('Enable Exclusion List.'),
];
// Exclusion List - words to strip from searches.
$form['vertex_ai_search_exclusion']['exclusion_list'] = [
'#title' => $this->t('Exclusion List'),
'#type' => 'textarea',
'#default_value' => $this->configuration['exclusion_list'] ?? NULL,
'#description' => $this->t('Words or phrases to strip from a search request.
One word/phrase per line.'),
'#states' => [
'visible' => [
':input[name="exclusion_list_enable"]' => ['checked' => TRUE],
],
'required' => [
':input[name="exclusion_list_enable"]' => ['checked' => TRUE],
],
],
];
return $form;
}
/**
* Helper to add vertex search filter elements to config form.
*
* @param array $form
* The configuration form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current state of the configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexSearchFilterElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_search_filter'] = [
'#title' => $this->t('Filter Expressions'),
'#type' => 'details',
'#description' => $this->t('Using custom search filter plugins,
<a href=":filters">filter expressions</a> can be applied to search
requests. To define a custom filter expression to be applied to all
search requests, refer to the Developer Documentation for more
information on <a href=":filterPlugin">developing a custom Search Filter
Plugin</a>.', [
':filters' => 'https://cloud.google.com/generative-ai-app-builder/docs/filter-website-search#filter-expressions-basic-indexing',
':filterPlugin' => 'https://www.drupal.org/docs/extending-drupal/contributed-modules/contributed-module-documentation/vertex-ai-search/developer-documentation#s-search-filter-plugin',
]),
'#open' => FALSE,
];
// Enable filtering.
$form['vertex_ai_search_filter']['filter_enable'] = [
'#title' => $this->t('Enable Filter'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['filter_enable'] ?? '',
'#description' => $this->t('Enable search filter.'),
];
// Get available filter plugins.
$filterDefinitions = $this->filterPluginManager->getDefinitions();
$filterOptions[NULL] = $this->t('-- Select a Filter Plugin --');
foreach ($filterDefinitions as $pluginKey => $pluginDefinition) {
$filterOptions[$pluginKey] = $pluginDefinition['title'];
}
$form['vertex_ai_search_filter']['filter_plugin'] = [
'#title' => $this->t('Filter Plugin'),
'#type' => 'select',
'#options' => $filterOptions,
'#default_value' => $this->configuration['filter_plugin'] ?? NULL,
'#states' => [
'visible' => [
':input[name="filter_enable"]' => ['checked' => TRUE],
],
'required' => [
':input[name="filter_enable"]' => ['checked' => TRUE],
],
],
'#ajax' => [
'callback' => [$this, 'retrieveFilterOptions'],
'disable-refocus' => FALSE,
'event' => 'change',
'wrapper' => 'filter-options-wrapper',
'progress' => [
'type' => 'throbber',
'message' => $this->t('Retrieving Options...'),
],
],
];
// Filter plugin-specific options.
$form['vertex_ai_search_filter']['filter_options'] = [
'#title' => $this->t('Filter Options'),
'#type' => 'details',
'#open' => TRUE,
'#prefix' => '<div id="filter-options-wrapper">',
'#suffix' => '</div>',
'#states' => [
'visible' => [
':input[name="filter_enable"]' => ['checked' => TRUE],
':input[name="filter_plugin"]' => ['!value' => ''],
],
],
'#cache' => [
'max-age' => 0,
],
];
$filterPlugin = NULL;
if (!empty($this->configuration['filter_plugin'])) {
$filterPlugin = $this->filterPluginManager->createInstance(
$this->configuration['filter_plugin'],
$this->configuration
);
}
$currentForm = $form;
if ($filterPlugin instanceof PluginFormInterface) {
$form += $filterPlugin->buildConfigurationForm($form, $form_state);
}
$filterElements = array_diff_key($form, $currentForm);
foreach ($filterElements as $filterKey => $filterElement) {
$form['vertex_ai_search_filter']['filter_options'][$filterKey] = $filterElement;
unset($form[$filterKey]);
}
$form['vertex_ai_search_filter']['filter_options']['pluginElements'] = [
'#type' => 'hidden',
'#value' => array_keys($filterElements),
];
// Manipulate form to display appropriate filter plugin options.
if ($newFilter = $form_state->getValue('filter_plugin')) {
// Remove unneeded elements from previous filter_plugin.
$remove = array_keys($filterElements);
$form['vertex_ai_search_filter']['filter_options'] = array_diff_key($form['vertex_ai_search_filter']['filter_options'], array_flip($remove));
$this->SetConfiguration(array_diff_key($this->configuration, array_flip($remove)));
// Set filter plugin based on form_state value ($newFilter).
$this->configuration['filter_plugin'] = $newFilter;
$filterPlugin = $this->filterPluginManager->createInstance($newFilter, $this->configuration);
$currentForm = $form;
if ($filterPlugin instanceof PluginFormInterface) {
$form += $filterPlugin->buildConfigurationForm($form, $form_state);
}
// Get an array of form elements specific to the filter plugin.
$filterElements = array_diff_key($form, $currentForm);
$form['vertex_ai_search_filter']['filter_plugin']['#default_value'] = $newFilter;
// Make sure filter plugin-specific elements in filter_options group.
foreach ($filterElements as $filterKey => $filterElement) {
$form['vertex_ai_search_filter']['filter_options'][$filterKey] = $filterElement;
$form['vertex_ai_search_filter']['filter_options'][$filterKey]['#default_value'] = $this->configuration[$filterKey] ?? NULL;
}
// Keep a record of filter plugin-specific elements.
$form['vertex_ai_search_filter']['filter_options']['pluginElements'] = [
'#type' => 'hidden',
'#value' => array_keys($filterElements),
];
}
return $form;
}
/**
* Helper to group all of the filter-related form elements together.
*
* @param array &$form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*/
private function groupFilteringElements(array &$form, FormStateInterface $form_state) {
$form['filters'] = [
'#title' => $this->t('Filters'),
'#type' => 'details',
'#description' => $this->t('The search request can be modified using
JavaScript Plugins, an Exclusion List, or Vertex Filter Expressions.'
),
'#open' => TRUE,
];
// Add filter details elements to a common details element.
$form['filters']['vertex_ai_search_js'] =
$form['vertex_ai_search_js'];
$form['filters']['vertex_ai_search_exclusion'] =
$form['vertex_ai_search_exclusion'];
$form['filters']['vertex_ai_search_filter'] =
$form['vertex_ai_search_filter'];
// Unset original form structure since these have moved.
unset($form['vertex_ai_search_js']);
unset($form['vertex_ai_search_exclusion']);
unset($form['vertex_ai_search_filter']);
}
/**
* Helper to add vertex flood configuration elements.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexFloodConfigElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_search_flood'] = [
'#title' => $this->t('Flood Settings'),
'#type' => 'details',
'#open' => TRUE,
];
// Enable Flood Protection (or not).
$form['vertex_ai_search_flood']['flood_enable'] = [
'#title' => $this->t('Enable Flood Control'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['flood_enable'] ?? '',
'#description' => $this->t('Enable flood control.'),
];
// Flood Threshold - number of requests allowed within a time window.
$form['vertex_ai_search_flood']['flood_threshold'] = [
'#title' => $this->t('Flood Threshold'),
'#type' => 'number',
'#min' => 1,
'#step' => 1,
'#default_value' => $this->configuration['flood_threshold'] ?? 100,
'#description' => $this->t('Max number of requests allowed within
a time window before flood control engages.'),
'#states' => [
'visible' => [
':input[name="flood_enable"]' => ['checked' => TRUE],
],
'required' => [
':input[name="flood_enable"]' => ['checked' => TRUE],
],
],
];
// Flood Window - time window within which requests are metered.
$form['vertex_ai_search_flood']['flood_window'] = [
'#title' => $this->t('Flood Window'),
'#type' => 'number',
'#min' => 1,
'#step' => 1,
'#default_value' => $this->configuration['flood_window'] ?? 3600,
'#description' =>
$this->t('Time window (in seconds) within which requests are metered.'),
'#states' => [
'visible' => [
':input[name="flood_enable"]' => ['checked' => TRUE],
],
'required' => [
':input[name="flood_enable"]' => ['checked' => TRUE],
],
],
];
// Message to display on SERP when flood control intervenes.
$form['vertex_ai_search_flood']['flood_message'] = [
'#title' => $this->t('Flood Message'),
'#type' => 'textarea',
'#default_value' => $this->configuration['flood_message'] ??
$this->t('Request threshold has been reached. Try again at a later time.'),
'#description' =>
$this->t('Message to display when the number of requests exceeds the threshold.'),
'#states' => [
'visible' => [
':input[name="flood_enable"]' => ['checked' => TRUE],
],
'required' => [
':input[name="flood_enable"]' => ['checked' => TRUE],
],
],
];
return $form;
}
/**
* Helper to add vertex search message elements to config form.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexSearchMessageElements(array $form, FormStateInterface $form_state) {
$form['serp_messages'] = [
'#title' => $this->t('Search Results Page Messages'),
'#type' => 'details',
'#open' => TRUE,
'#description' => $this->t('Tokens may be used in any of the results-related messages.'),
];
// Add token browser to search page configuration form.
$form['serp_messages']['token_browser'] = [
'#theme' => 'token_tree_link',
'#token_types' => ['vertex_ai_search', 'vertex_ai_search_keywords'],
'#global_types' => FALSE,
];
// Message to display on SERP when results are returned.
$form['serp_messages']['results_message'] = [
'#title' => $this->t('Results message'),
'#type' => 'textarea',
'#default_value' => $this->configuration['results_message'] ?? '',
'#description' => $this->t('Message to display when results are returned.'),
];
// Corner-case message if only one result is returned.
$form['serp_messages']['results_message_singular'] = [
'#title' => $this->t('Results message (singular)'),
'#type' => 'textarea',
'#default_value' => $this->configuration['results_message_singular'] ?? '',
'#description' => $this->t('Message to display when only a single result is returned. Normal results message will be default.'),
];
// Message to display if search query has no results returned.
$form['serp_messages']['no_results_message'] = [
'#title' => $this->t('No results message'),
'#type' => 'textarea',
'#default_value' => $this->configuration['no_results_message'] ?? '',
'#description' => $this->t('Message to display when there are no search results returned.'),
];
// Message to display when trying to search without specifying keywords.
$form['serp_messages']['no_keywords_message'] = [
'#title' => $this->t('No keywords specified message'),
'#type' => 'textarea',
'#default_value' => $this->configuration['no_keywords_message'] ?? '',
'#description' => $this->t('Message to display when arriving at search page without specifying keywords on which to search.'),
];
// Message for keyword correction when correction mode is 'AUTO'.
$form['serp_messages']['correction_made_message'] = [
'#title' => $this->t('Keyword Correction Made'),
'#type' => 'textarea',
'#default_value' => $this->configuration['correction_made_message'] ?? '',
'#description' => $this->t(
'Message to display when keyword correction is made.'
),
];
// Message for keyword correction when correction mode is 'SUGGESTION'.
$form['serp_messages']['correction_suggestion_message'] = [
'#title' => $this->t('Keyword Correction Suggested'),
'#type' => 'textarea',
'#default_value' => $this->configuration['correction_suggestion_message'] ?? '',
'#description' => $this->t(
'Message to display when keyword correction is suggested.'
),
];
return $form;
}
/**
* Helper to add vertex advanced options elements to config form.
*
* @param array $form
* The configuration Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state of configuration form.
*
* @return array
* The elements to be added.
*/
private function retrieveVertexAdvancedOptionElements(array $form, FormStateInterface $form_state) {
$form['vertex_ai_search_advanced'] = [
'#title' => $this->t('Advanced Settings'),
'#type' => 'details',
'#open' => TRUE,
];
// Transport Method options.
$form['vertex_ai_search_advanced']['transport_method'] = [
'#title' => $this->t('Transport Options'),
'#type' => 'select',
'#options' => [
NULL => $this->t('Auto'),
'grpc' => $this->t('gRPC'),
'rest' => $this->t('REST'),
],
'#default_value' => $this->configuration['transport_method'] ?? NULL,
'#description' => $this->t(
"Specify the transport option to be used by Service Clients.
The 'Auto' option will use gRPC first if support exists, and then
fallback to REST."
),
];
// Disable querying the Google API during searches.
$form['vertex_ai_search_advanced']['disable_google_api_queries'] = [
'#title' => $this->t('Disable queries to the Google API'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['disable_google_api_queries'] ?? FALSE,
'#description' => $this->t(
'Disable querying the Google API during searches. This is useful
for testing purposes, but should not be used in production.
'),
];
// Default Search Path for REST Clients.
$form['vertex_ai_search_advanced']['default_client_search_path'] = [
'#title' => $this->t('REST Resource Client Default Search Path (search-path)'),
'#type' => 'textfield',
'#default_value' => $this->configuration['default_client_search_path'] ?? '',
'#description' => $this->t(
'REST Resource Clients can pass the "search-path" query parameter to
specify the path to the client-side search page. If not specified,
this default path will be used to populate tokens that can be used
in the construction of search messages configured on this page. The
path to this custom search page will be used if neither "search-path"
or a default is specified.',
),
];
return $form;
}
}
