search_autocomplete.module in Search Autocomplete 8
Provides autocompletion in any field from GUI.
The Search Autocomplete module provides autocompletion configurations to help autocompleting any fields.
File
search_autocomplete.moduleView source
<?php
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\views\Views;
/**
* @file
* Provides autocompletion in any field from GUI.
*
* The Search Autocomplete module provides autocompletion configurations
* to help autocompleting any fields.
*/
/**
* Implements hook_element_info_alter().
*
* The purpose of this is to include our own settings in formAPI for
* autocompletion configurations.
*/
function search_autocomplete_element_info_alter(&$types) {
/*
* Iterate throught the elements to find autocompletion enabled ones.
* This methods let's all autocompletion enabled elements to be overriden,
* even if coming from other modules or if unknown yet.
*/
foreach ($types as $type => $info) {
$process_methods = isset($info['#process']) ? $info['#process'] : [];
foreach ($process_methods as $method) {
// If the element is autocompleted, overrides it.
if (in_array('processAutocomplete', (array) $method)) {
$types[$type]['#process'][] = 'process_search_autocomplete';
break;
}
}
}
return $types;
}
/**
* Adds autocomplete functionality to elements.
*
* This sets up autocomplete functionality for elements with an
* #autocomplete_configuration property.
*
* @param array $element
* The form element to process.
*
* @return array
* The form element.
*/
function process_search_autocomplete(&$element) {
if (!empty($element['#autocomplete_configuration'])) {
attach_configuration_to_element($element, $element['#autocomplete_configuration']);
$element['#attributes']['class'][] = 'form-autocomplete';
$element['#attached']['library'][] = 'core/drupal.autocomplete';
$element['#attributes']['data-key'] = $element['#autocomplete_configuration'];
}
return $element;
}
/**
* This helper method will analyse an autocomplete_configuration to build
* the element properties and add necessary libraries, configurations and
* data necessary for autocompletion to happen.
*
* @param array $element
* An element of a form.
* @param string $config_id
* The ID of the autocomplete_configuration to use.
*
* @return array
* The element once filled with data.
*/
function attach_configuration_to_element(&$element, $config_id) {
// Load the configuration.
$config = Drupal::entityTypeManager()
->getStorage('autocompletion_configuration')
->load($config_id);
// No need to continue if the $config is empty.
if ($config == NULL || !$config
->getStatus()) {
return;
}
$source = $config
->getSource();
$exposed_filters = [];
// Build the callback URL.
$input_source = explode('::', $source);
// If from View, build view URL.
if (count($input_source) == 2) {
// Load the view.
$view = Views::getView($input_source[0]);
// Load the required display.
if ($view != NULL && $view
->setDisplay($input_source[1])) {
// Retrieve the display URL.
$display = $view
->getDisplay();
$source = '/' . $display
->getPath();
// Build filters
$filters = $view
->getHandlers('filter');
foreach ($filters as $filter) {
if (isset($filter['exposed']) && $filter['exposed'] && isset($filter['expose'])) {
$exposed_filters[] = $filter['expose']['identifier'];
}
}
}
}
// In case of internal URL, convert it to absolute.
if (!UrlHelper::isExternal($source)) {
$prefix = 'internal:';
if (strpos($source, '/') !== 0) {
$prefix .= '/';
}
$source = Url::fromUri($prefix . $source, [
'absolute' => FALSE,
])
->toString();
}
$settings = [
'source' => $source,
'selector' => $config
->getSelector(),
'minChars' => $config
->getMinChar(),
'maxSuggestions' => $config
->getMaxSuggestions(),
'autoSubmit' => $config
->getAutoSubmit(),
'autoRedirect' => $config
->getAutoRedirect(),
'theme' => basename($config
->getTheme(), '.css'),
// theme without extension
'filters' => $exposed_filters,
'noResult' => [
'group' => [
'group_id' => 'no_results',
],
'label' => $config
->getNoResultLabel(),
'value' => $config
->getNoResultValue(),
'link' => $config
->getNoResultLink(),
],
'moreResults' => [
'group' => [
'group_id' => 'more_results',
],
'label' => $config
->getMoreResultsLabel(),
'value' => $config
->getMoreResultsValue(),
'link' => $config
->getMoreResultsLink(),
],
];
$element['#attached']['drupalSettings']['search_autocomplete'][$config
->id()] = $settings;
// Add the theme library if available
if (Drupal::service('library.discovery')
->getLibraryByName('search_autocomplete', 'theme.' . $config
->getTheme())) {
$element['#attached']['library'][] = 'search_autocomplete/theme.' . $config
->getTheme();
}
return $element;
}
/**
* Implements hook_install().
*
* Display a notice to user for cache rebuild after install.
*/
function search_autocomplete_install() {
drupal_set_message(t('To finish install of Search Autocomplete, you must %clear_caches.', [
'%clear_caches' => Link::fromTextAndUrl(t('Clear all caches'), Url::fromRoute('system.performance_settings'))
->toString(),
]));
}
/**
* Implements hook_library_info_build().
*
* Add dynamic library definitions.
*
* Since we can't add files through css in #attached anymore, this is a trick
* defining as many libraries as the number of found CSS in the theme folder.
*
* @return array
* An array of library definitions.
*/
function search_autocomplete_library_info_build() {
$libraries = [];
// Find all available themes.
$themes = [];
$files = file_scan_directory(drupal_get_path('module', 'search_autocomplete') . '/css/themes', '/.*\\.css\\z/', [
'recurse' => FALSE,
]);
// Create a new library for all themes.
foreach ($files as $file) {
$libraries["theme.{$file->filename}"] = [
'css' => [
'base' => [
"css/themes/{$file->filename}" => [],
],
],
];
}
return $libraries;
}
/**
* Implements hook_library_info_alter().
*/
function search_autocomplete_library_info_alter(array &$libraries, $extension) {
if ($extension == 'core' && isset($libraries['drupal.autocomplete'])) {
$libraries['drupal.autocomplete']['js'] = [
'/' . drupal_get_path('module', 'search_autocomplete') . '/js/jquery.autocomplete.js' => [],
];
}
}
/**
* Implements hook_page_attachments().
*
* Adds the settings from user defined autocomplete_configurations to the page.
* Also loads the necessary JS library if needed.
*/
function search_autocomplete_page_attachments(array &$attachments) {
// Check user permissions.
if (!Drupal::currentUser()
->hasPermission('use search autocomplete')) {
return;
}
// Load module settings.
$module_settings = Drupal::config('search_autocomplete.settings');
// Check if user have authorization to use admin tools and if enabled.
if (Drupal::currentUser()
->hasPermission('administer Search Autocomplete') && $module_settings
->get('admin_helper')) {
$attachments['#attached']['library'][] = 'search_autocomplete/search_autocomplete.admin.helper';
}
// Load all configurations with selectors
$autocomplete_configuration_entities = Drupal::entityQuery('autocompletion_configuration')
->condition('status', TRUE)
->condition('selector', '', '<>')
->execute();
// End-up here if no config to set.
if (empty($autocomplete_configuration_entities)) {
return;
}
// Add the settings to be pass to JS:
foreach ($autocomplete_configuration_entities as $config_id) {
attach_configuration_to_element($attachments, $config_id);
}
$attachments['#attached']['library'][] = 'core/drupal.autocomplete';
}
/**
* Implements hook_form_alter().
*
* Adds autocomplete_configurations to formAPI for search block and search page.
* This is an example on how to use configurations from code.
*/
function search_autocomplete_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Prevent a useless query to configurations.
if ($form_id != $form_id && $form_id != 'search_form') {
return;
}
// Load the default activated configurations.
$autocomplete_configuration_entities = Drupal::entityQuery('autocompletion_configuration')
->condition('status', TRUE)
->condition('id', [
'search_block',
'search_form_users',
'search_form_content',
], 'IN')
->execute();
// Autocomplete the default search block.
if ($form_id == 'search_block_form' && isset($autocomplete_configuration_entities['search_block'])) {
$form['keys']['#autocomplete_configuration'] = 'search_block';
}
else {
if ($form_id == 'search_form') {
if (Drupal::routeMatch()
->getRouteName() == 'search.view_user_search' && isset($autocomplete_configuration_entities['search_form_users'])) {
$form['basic']['keys']['#autocomplete_configuration'] = 'search_form_users';
}
else {
if (isset($autocomplete_configuration_entities['search_form_users'])) {
$form['basic']['keys']['#autocomplete_configuration'] = 'search_form_content';
}
}
}
}
return $form;
}
Functions
Name | Description |
---|---|
attach_configuration_to_element | This helper method will analyse an autocomplete_configuration to build the element properties and add necessary libraries, configurations and data necessary for autocompletion to happen. |
process_search_autocomplete | Adds autocomplete functionality to elements. |
search_autocomplete_element_info_alter | Implements hook_element_info_alter(). |
search_autocomplete_form_alter | Implements hook_form_alter(). |
search_autocomplete_install | Implements hook_install(). |
search_autocomplete_library_info_alter | Implements hook_library_info_alter(). |
search_autocomplete_library_info_build | Implements hook_library_info_build(). |
search_autocomplete_page_attachments | Implements hook_page_attachments(). |