acquia_search.module in Acquia Connector 7.2
Same filename and directory in other branches
Integration between Acquia Drupal and Acquia's hosted solr search service.
File
acquia_search/acquia_search.moduleView source
<?php
/**
* @file
* Integration between Acquia Drupal and Acquia's hosted solr search service.
*/
use Drupal\acquia_search\PreferredSearchCoreService;
define('ACQUIA_SEARCH_ENVIRONMENT_ID', 'acquia_search_server_1');
define('ACQUIA_SEARCH_OVERRIDE_AUTO_SET', 1);
define('ACQUIA_SEARCH_AUTO_OVERRIDE_READ_ONLY', 2);
define('ACQUIA_SEARCH_EXISTING_OVERRIDE', 3);
/**
* Implements hook_init().
*/
function acquia_search_init() {
// Add apachesolr module connection overrides if applicable.
$auto_switch_disabled = variable_get('acquia_search_disable_auto_switch', 0);
$sub_active = acquia_agent_subscription_is_active();
if (!$auto_switch_disabled && $sub_active && !module_exists('acquia_search_multi_subs')) {
module_load_include('php', 'acquia_search', 'src/PreferredSearchCoreService');
global $conf;
$acquia_identifier = acquia_agent_settings('acquia_identifier');
$ah_env = isset($_SERVER['AH_SITE_ENVIRONMENT']) ? $_SERVER['AH_SITE_ENVIRONMENT'] : '';
$sites_foldername = substr(conf_path(), strrpos(conf_path(), '/') + 1);
$ah_db_name = isset($conf['acquia_hosting_site_info']['db']['name']) ? $conf['acquia_hosting_site_info']['db']['name'] : '';
$subscription = acquia_agent_settings('acquia_subscription_data');
$available_cores = acquia_search_available_cores($subscription);
$environments = apachesolr_load_all_environments();
$core_service = new PreferredSearchCoreService($acquia_identifier, $ah_env, $sites_foldername, $ah_db_name, $available_cores);
$overrode = acquia_search_add_apachesolr_overrides($core_service, $environments);
if ($overrode) {
// If an override was applied, then clear the corresponding cache item.
cache_clear_all('apachesolr:environments', 'cache_apachesolr');
}
}
}
/**
* Overrides apachesolr configs to talk to the proper Acquia search core.
*
* It determines proper (preferred) Acquia search core to which the site should
* be connected and overrides the apachesolr config accordingly. If a proper
* search core is not found, it enables the read-only mode so that the site
* does not unintentionally connect to the wrong core and corrupts its index.
*
* @param Drupal\acquia_search\PreferredSearchCoreService $core_service
* @param array $environments
* The apachesolr environments.
*
* @return bool|NULL
* True if preferred core has been found in the list of available cores and
* it was used to override the search settings.
*/
function acquia_search_add_apachesolr_overrides($core_service, $environments) {
global $conf;
$overrode = FALSE;
foreach ($environments as $acquia_env_id => $environment) {
// Override Acquia search environments only.
if ($environment['service_class'] != 'AcquiaSearchService') {
continue;
}
// Skip if the Acquia search environment has already been overridden.
if (!empty($conf['apachesolr_environments'][$acquia_env_id])) {
continue;
}
$conf['apachesolr_environments'][$acquia_env_id]['acquia_search_possible_cores'] = $core_service
->getListOfPossibleCores();
if ($core_service
->isPreferredCoreAvailable()) {
$derived_key = acquia_search_get_derived_key_for_core($core_service
->getPreferredCoreId());
$conf['apachesolr_environments'][$acquia_env_id]['url'] = $core_service
->getPreferredCoreUrl();
$conf['apachesolr_environments'][$acquia_env_id]['conf'] = array(
'acquia_search_key' => $derived_key,
);
$conf['apachesolr_environments'][$acquia_env_id]['overridden_by_acquia_search'] = ACQUIA_SEARCH_OVERRIDE_AUTO_SET;
drupal_static_reset('apachesolr_load_all_environments');
$overrode = TRUE;
continue;
}
// At this point none of the available search cores match to what is
// expected. Switch the search into read-only mode.
if (!variable_get('acquia_search_disable_auto_read_only', 0)) {
$conf['apachesolr_environments'][$acquia_env_id]['conf'] = array(
'apachesolr_read_only' => 1,
);
$conf['apachesolr_environments'][$acquia_env_id]['overridden_by_acquia_search'] = ACQUIA_SEARCH_AUTO_OVERRIDE_READ_ONLY;
drupal_static_reset('apachesolr_load_all_environments');
$overrode = TRUE;
}
}
return $overrode;
}
/**
* Returns derived key for the given core ID.
*
* @param $core_id
*
* @return string
*/
function acquia_search_get_derived_key_for_core($core_id) {
$salt = acquia_search_derived_key_salt();
$key = acquia_agent_settings('acquia_key');
return _acquia_search_create_derived_key($salt, $core_id, $key);
}
/**
* Retrieves all available search cores as set in the subscription.
*
* @param $subscription
* Acquia Subscription array.
*
* @return array
*/
function acquia_search_available_cores($subscription) {
if (!empty($subscription['heartbeat_data']['search_cores'])) {
return $subscription['heartbeat_data']['search_cores'];
}
}
/**
* Pings the search core.
*
* @param string $env_id
* @param string $op
*
* @return bool
*/
function acquia_search_ping($env_id, $op = 'ping') {
try {
return (bool) apachesolr_get_solr($env_id)
->{$op}();
} catch (Exception $e) {
watchdog_exception('acquia_search', $e, 'Exception thrown when calling @op on Apachesolr. %type: !message in %function (line %line of %file).', array(
'@op' => $op,
));
}
}
/**
* Adds Acquia search connection details to the given form.
*
* @param array $form
* @param array $form_state
* @param array $environment
* An Apache Solr module environment.
*/
function acquia_search_add_form_status_message(&$form, &$form_state, $environment) {
$form['acquia_search_message'] = array(
'#type' => 'fieldset',
'#title' => t('Acquia Search status for this connection'),
'#collapsible' => FALSE,
'#weight' => -10,
);
$form['acquia_search_message']['message'] = array(
'#markup' => acquia_search_get_search_status_message($environment),
);
}
/**
* Returns formatted message about read-only mode.
*
* @param array $environment
* An Apache Solr module environment.
* @param string $t
*
* @return string
*/
function acquia_search_get_read_only_mode_warning($environment, $t = 't') {
$msg = $t('To protect your data, Acquia Search module is enforcing
read-only mode, because it could not figure out what Acquia-hosted Solr
index to connect to. This helps you avoid writing to a production index
if you copy your site to a development or other environment(s).');
if (!empty($environment['acquia_search_possible_cores'])) {
$list = theme('item_list', array(
'items' => $environment['acquia_search_possible_cores'],
));
$msg .= '<p>';
$msg .= $t('These index IDs would have worked, but could not be found on
your Acquia subscription: !list', array(
'!list' => $list,
));
$msg .= '</p>';
}
$msg .= $t('To fix this problem, please read <a href="@url">our documentation</a>.', array(
'@url' => 'https://docs.acquia.com/acquia-search/multiple-cores',
));
return $msg;
}
/**
* Returns formatted message about Acquia Search connection details.
*
* @param array $environment
* An Apache Solr module environment.
* @return string
*/
function acquia_search_get_search_status_message($environment) {
$items = array(
t('apachesolr.module environment ID: @env', array(
'@env' => $environment['env_id'],
)),
t('URL: @url', array(
'@url' => $environment['url'],
)),
);
if (acquia_search_ping($environment['env_id'])) {
$items[] = t('Solr index is currently reachable and up.');
}
else {
$items[] = t('Solr index is currently unreachable.');
}
// Deep-ping the Solr index to ensure authentication is working.
if (acquia_search_ping($environment['env_id'], 'getFields')) {
$items[] = t('Requests to Solr index are passing authentication checks.');
}
else {
$items[] = t('Solr core authentication check fails.');
}
return t('Connection managed by Acquia Search module.') . theme('item_list', array(
'items' => $items,
));
}
/**
* Predefined Acquia Search network environment
*/
function acquia_search_get_environment($conf = array()) {
if (!empty($conf['acquia_subscription_id']) && !empty($conf['acquia_subscription_key'])) {
$identifier = $conf['acquia_subscription_id'];
$key = $conf['acquia_subscription_key'];
$subscription = acquia_agent_get_subscription($params = array(), $identifier, $key);
}
else {
$identifier = acquia_agent_settings('acquia_identifier');
$subscription = acquia_agent_settings('acquia_subscription_data');
}
$search_host = variable_get('acquia_search_host', 'search.acquia.com');
// @todo rework this logic once we have AH_SERVER_REGION
// legacy_internal will be true if 'internal-' is the start of the host
// name that's set in via Acquia Cloud platform or vset.
$legacy_internal = preg_match('/internal[-.]/', $search_host);
// Adding the subscription specific colony to the heartbeat data
if (!empty($subscription['heartbeat_data']['search_service_colony'])) {
$search_host = $subscription['heartbeat_data']['search_service_colony'];
if ($legacy_internal) {
// If we want to be using an internal host name, fix the one
// from the hearbeat data here.
$search_host = 'internal-' . $search_host;
}
}
// Check if we are on Acquia Cloud hosting. @see NN-2503
if (!empty($_ENV['AH_SITE_ENVIRONMENT']) && !empty($_ENV['AH_SERVER_REGION'])) {
if ($_ENV['AH_SERVER_REGION'] == 'us-east-1' && $search_host == 'search.acquia.com') {
$search_host = 'internal-search.acquia.com';
}
elseif (strpos($search_host, 'search-' . $_ENV['AH_SERVER_REGION']) === 0) {
$search_host = 'internal-' . $search_host;
}
}
$environment = array(
'url' => 'http://' . $search_host . variable_get('acquia_search_path', '/solr/' . $identifier),
'service_class' => 'AcquiaSearchService',
);
return $environment;
}
/**
* Implementation of hook_enable().
*/
function acquia_search_enable() {
// Send a heartbeat so the Acquia Subscription knows the module is enabled.
// This causes an invocation of hook_acquia_subscription_status() which is
// implemented in this module to set up the environment.
_acquia_search_set_version();
acquia_agent_check_subscription();
}
/**
* Implementation of hook_help().
*/
function acquia_search_help($path, $arg) {
switch ($path) {
case 'admin/config/search/apachesolr':
$env_id = isset($arg[5]) ? $arg[5] : apachesolr_default_environment();
$environment = apachesolr_environment_load($env_id);
if (acquia_search_environment_connected($environment) && acquia_agent_subscription_is_active()) {
$as_link = l(t('Acquia Search'), 'http://www.acquia.com/products-services/acquia-search');
return t("Search is being provided by the !as network service.", array(
'!as' => $as_link,
));
}
break;
}
}
/**
* Create a new record pointing to the Acquia apachesolr search server and set it as the default
*/
function acquia_search_enable_acquia_solr_environment() {
// Creates the new environment
$environment = apachesolr_environment_load(ACQUIA_SEARCH_ENVIRONMENT_ID);
if (!$environment) {
$environment['conf'] = array();
// Copy the bundles from the previous default environment
$orig_env_id = apachesolr_default_environment();
$orig_env = apachesolr_environment_load($orig_env_id);
$environment['index_bundles'] = $orig_env['index_bundles'];
// Also make sure that the default search page has Acquia Search as its
// default environment
$default_search_page_id = apachesolr_search_default_search_page();
$default_search_page = apachesolr_search_page_load($default_search_page_id);
if (!empty($default_search_page) && $default_search_page['env_id'] != ACQUIA_SEARCH_ENVIRONMENT_ID) {
$default_search_page['env_id'] = ACQUIA_SEARCH_ENVIRONMENT_ID;
apachesolr_search_page_save($default_search_page);
}
// Only set the default if we just created the environment.
// This will almost always happen, unless the module was disabled via SQL.
variable_set('apachesolr_default_environment', ACQUIA_SEARCH_ENVIRONMENT_ID);
// Make sure apachesolr search is the default search module.
variable_set('search_default_module', 'apachesolr_search');
}
$acquia_environment = acquia_search_get_environment();
// Override default values
foreach ($acquia_environment as $key => $value) {
$environment[$key] = $value;
}
$environment['env_id'] = ACQUIA_SEARCH_ENVIRONMENT_ID;
$environment['name'] = t('Acquia Search');
// allow other modules to override this
drupal_alter('acquia_search_enable', $environment);
apachesolr_environment_save($environment);
}
/**
* Implementation of hook_disable().
*
* Helper function to clear variables we may have set.
*/
function acquia_search_disable() {
// Revert the default search environment
if (apachesolr_default_environment() == ACQUIA_SEARCH_ENVIRONMENT_ID) {
acquia_search_disable_revert_defaults();
}
// Remove the base Acquia Search environment we added.
apachesolr_environment_delete(ACQUIA_SEARCH_ENVIRONMENT_ID);
// Unset all other acquia search environments
$environments = apachesolr_load_all_environments();
foreach ($environments as $environment) {
if (acquia_search_environment_connected($environment)) {
// remove traces of acquia_search
// unset our acquia url and set it back to default
$environment['url'] = 'http://localhost:8983/solr';
// Emptying the service class, unsetting it would not work, since it would
// not overwrite the old value
$environment['service_class'] = '';
apachesolr_environment_save($environment);
}
}
variable_del('acquia_search_derived_key_salt');
}
/**
* Helper function to revert the default environment after disable
*/
function acquia_search_disable_revert_defaults() {
// if the environment does not exists, it will return FALSE
// Clear all caches to be sure.
cache_clear_all('apachesolr:environments', 'cache_apachesolr');
drupal_static_reset('apachesolr_load_all_environments');
drupal_static_reset('apachesolr_get_solr');
if (module_exists('ctools')) {
ctools_include('export');
ctools_export_load_object_reset('apachesolr_environment');
}
if (!apachesolr_environment_load('solr')) {
module_load_include('inc', 'apachesolr', 'apachesolr.index');
// Same statements as during the apachesolr install process
// @todo make this less duplicate
db_insert('apachesolr_environment')
->fields(array(
'env_id' => 'solr',
'name' => 'localhost server',
'url' => 'http://localhost:8983/solr',
))
->execute();
// Initialize the entities to index. We enable all node types by default
$info = entity_get_info('node');
$bundles = array_keys($info['bundles']);
apachesolr_index_set_bundles('solr', 'node', $bundles);
}
// Go back to the default variable which is 'solr'.
variable_del('apachesolr_default_environment');
}
/**
* Implementation of hook_menu_alter().
*/
function acquia_search_menu_alter(&$menu) {
$delete_page = 'admin/config/search/apachesolr/settings/%apachesolr_environment/delete';
if (isset($menu[$delete_page])) {
$menu[$delete_page]['access callback'] = 'acquia_search_environment_delete_access';
$menu[$delete_page]['access arguments'] = array(
5,
);
}
}
/**
* Helper function to cache the Acquia Search version.
*/
function _acquia_search_set_version() {
// Cache the version in a variable so we can send it at not extra cost.
$version = variable_get('acquia_search_version', '7.x');
$info = system_get_info('module', 'acquia_search');
// Send the version, or at least the core compatibility as a fallback.
$new_version = isset($info['version']) ? (string) $info['version'] : (string) $info['core'];
if ($version != $new_version) {
variable_set('acquia_search_version', $new_version);
}
}
/**
* Tests whether the environment is connected to Acquia Search.
*/
function acquia_search_environment_connected($environment) {
if ($environment['service_class'] == 'AcquiaSearchService') {
return TRUE;
}
$acquia_search_key = apachesolr_environment_variable_get($environment['env_id'], 'acquia_search_key');
if (!empty($acquia_search_key)) {
return TRUE;
}
return FALSE;
}
/**
* Delete environment page access.
*/
function acquia_search_environment_delete_access($environment) {
if ($environment['env_id'] == ACQUIA_SEARCH_ENVIRONMENT_ID) {
return FALSE;
}
// Fall back to the original check.
return user_access('administer search');
}
/**
* Implementation of hook_form_[form_id]_alter().
*/
function acquia_search_form_apachesolr_settings_alter(&$form, $form_state) {
// Don't alter the form if there is no subscription.
if (acquia_agent_subscription_is_active()) {
// Don't show delete operation for the Default AS environment. This means
// that cloned acquia search environments can be deleted
foreach ($form['apachesolr_host_settings']['table']['#rows'] as &$row) {
if (isset($row['data']['delete']['data']) && strpos($row['data']['delete']['data'], ACQUIA_SEARCH_ENVIRONMENT_ID . '/delete') !== FALSE) {
$row['data']['delete']['data'] = '';
break;
}
}
$form['advanced']['acquia_search_edismax_default'] = array(
'#type' => 'radios',
'#title' => t('Always allow advanced syntax for Acquia Search'),
'#default_value' => variable_get('acquia_search_edismax_default', 0),
'#options' => array(
0 => t('Disabled'),
1 => t('Enabled'),
),
'#description' => t('If enabled, all Acquia Search keyword searches may use advanced <a href="@url">Lucene syntax</a> such as wildcard searches, fuzzy searches, proximity searches, boolean operators and more via the Extended Dismax parser. If not enabled, this syntax wll only be used when needed to enable wildcard searches.', array(
'@url' => 'http://lucene.apache.org/java/2_9_3/queryparsersyntax.html',
)),
'#weight' => 10,
);
}
}
/**
* Implementation of hook_form_[form_id]_alter().
*/
function acquia_search_form_apachesolr_environment_edit_form_alter(&$form, $form_state) {
// Gets environment from form, gets connection status to Acquia Search.
$env_id = isset($form['env_id']['#default_value']) ? $form['env_id']['#default_value'] : '';
$environment = $env_id ? apachesolr_environment_load($env_id) : FALSE;
// Add message to Acquia-connected indexes.
if ($environment && acquia_search_environment_connected($environment)) {
acquia_search_add_form_status_message($form, $form_state, $environment);
if (acquia_search_is_environment_overridden_to_read_only($environment)) {
$dsm = acquia_search_get_read_only_mode_warning($environment);
drupal_set_message($dsm, 'warning', FALSE);
}
$form['conf']['apachesolr_read_only']['#disabled'] = TRUE;
$form['url']['#disabled'] = TRUE;
$form['url']['#type'] = 'hidden';
$form['env_id']['#disabled'] = TRUE;
}
// Don't let the user delete the initial environment.
if ($env_id == ACQUIA_SEARCH_ENVIRONMENT_ID) {
$form['name']['#disabled'] = TRUE;
$form['actions']['delete']['#access'] = FALSE;
}
$form['actions']['save']['#validate'][] = 'acquia_search_environment_edit_form_validate';
}
/**
* Implementation of hook_form_[form_id]_alter().
*/
function acquia_search_form_apachesolr_index_action_form_alter(&$form, $form_state) {
$env_id = isset($form['action']['env_id']['#value']) ? $form['action']['env_id']['#value'] : '';
$environment = $env_id ? apachesolr_environment_load($env_id) : FALSE;
// Add message to Acquia-connected indexes.
if ($environment && acquia_search_environment_connected($environment)) {
acquia_search_add_form_status_message($form, $form_state, $environment);
if (acquia_search_is_environment_overridden_to_read_only($environment)) {
$dsm = acquia_search_get_read_only_mode_warning($environment);
drupal_set_message($dsm, 'warning', FALSE);
}
}
}
/**
* Determines whether the given environment is in the read-only mode.
*
* @param $environment
* Apachesolr environment.
*
* @return bool
* True if the environment has been switched to read-only mode by Acquia
* Search.
*/
function acquia_search_is_environment_overridden_to_read_only($environment) {
return isset($environment['overridden_by_acquia_search']) && $environment['overridden_by_acquia_search'] == ACQUIA_SEARCH_AUTO_OVERRIDE_READ_ONLY;
}
function acquia_search_environment_edit_form_validate($form, &$form_state) {
if ($form_state['values']['env_id'] == ACQUIA_SEARCH_ENVIRONMENT_ID) {
// make sure that the environment parameters have not been changed
$form_state['values'] = array_merge($form_state['values'], acquia_search_get_environment());
}
}
/**
* Implementation of hook_acquia_subscription_status().
*/
function acquia_search_acquia_subscription_status($active, $subscription = FALSE) {
if ($active) {
acquia_search_enable_acquia_solr_environment();
// Refresh the salt with the subscription data returned by the heartbeat
// since it can change periodically.
$salt = variable_get('acquia_search_derived_key_salt', '');
if (isset($subscription['derived_key_salt']) && $salt != $subscription['derived_key_salt']) {
variable_set('acquia_search_derived_key_salt', $subscription['derived_key_salt']);
}
}
else {
if (is_int($subscription)) {
switch ($subscription) {
case SUBSCRIPTION_NOT_FOUND:
case SUBSCRIPTION_EXPIRED:
acquia_search_disable();
break;
}
}
}
}
/**
* Modify a solr base url and construct a hmac authenticator cookie.
*
* @param $url
* The solr url beng requested - passed by reference and may be altered.
* @param $string
* A string - the data to be authenticated, or empty to just use the path
* and query from the url to build the authenticator.
* @param $derived_key
* Optional string to supply the derived key.
*
* @return
* An array containing the string to be added as the content of the
* Cookie header to the request and the nonce.
*/
function acquia_search_auth_cookie(&$url, $string = '', $derived_key = NULL, $env_id = NULL) {
$uri = parse_url($url);
// Add a scheme - should always be https if available.
if (in_array('ssl', stream_get_transports(), TRUE) && !defined('ACQUIA_DEVELOPMENT_NOSSL')) {
$scheme = 'https://';
$port = '';
}
else {
$scheme = 'http://';
$port = isset($uri['port']) && $uri['port'] != 80 ? ':' . $uri['port'] : '';
}
$path = isset($uri['path']) ? $uri['path'] : '/';
$query = isset($uri['query']) ? '?' . $uri['query'] : '';
$url = $scheme . $uri['host'] . $port . $path . $query;
// 32 character nonce.
$nonce = base64_encode(drupal_random_bytes(24));
if ($string) {
$auth_header = acquia_search_authenticator($string, $nonce, $derived_key, $env_id);
}
else {
$auth_header = acquia_search_authenticator($path . $query, $nonce, $derived_key, $env_id);
}
return array(
$auth_header,
$nonce,
);
}
/**
* Returns the subscription's salt used to generate the derived key.
*
* The salt is stored in a system variable so that this module can continue
* connecting to Acquia Search even when the subscription data is not available.
* The most common reason for subscription data being unavailable is a failed
* heartbeat connection to rpc.acquia.com.
*
* Acquia Connector versions <= 7.x-2.7 pulled the derived key salt directly
* from the subscription data. In order to allow for seamless upgrades, this
* function checks whether the system variable exists and sets it with the data
* in the subscription if it doesn't.
*
* @return string
* The derived key salt.
*
* @see http://drupal.org/node/1784114
*/
function acquia_search_derived_key_salt() {
$salt = variable_get('acquia_search_derived_key_salt', '');
if (!$salt) {
// If the variable doesn't exist, set it using the subscription data.
$subscription = acquia_agent_settings('acquia_subscription_data');
if (isset($subscription['derived_key_salt'])) {
variable_set('acquia_search_derived_key_salt', $subscription['derived_key_salt']);
$salt = $subscription['derived_key_salt'];
}
}
return $salt;
}
/**
* Get the derived key for the solr hmac using the information shared with acquia.com.
*/
function _acquia_search_derived_key($env_id = NULL) {
static $derived_key = array();
if (empty($env_id)) {
$env_id = 0;
}
if (!isset($derived_key[$env_id])) {
// If we set an explicit environment, check if this needs to overridden
// Use the default
$identifier = acquia_agent_settings('acquia_identifier');
$key = acquia_agent_settings('acquia_key');
// See if we need to overwrite these values
if ($env_id) {
// Load the explicit environment and a manually set search key.
if ($search_key = apachesolr_environment_variable_get($env_id, 'acquia_search_key')) {
$derived_key[$env_id] = $search_key;
}
}
// In any case, this is equal for all subscriptions. Also
// even if the search sub is different, the main subscription should be
// active
$derived_key_salt = acquia_search_derived_key_salt();
// We use a salt from acquia.com in key derivation since this is a shared
// value that we could change on the AN side if needed to force any
// or all clients to use a new derived key. We also use a string
// ('solr') specific to the service, since we want each service using a
// derived key to have a separate one.
if (empty($derived_key_salt) || empty($key) || empty($identifier)) {
// Expired or invalid subscription - don't continue.
$derived_key[$env_id] = '';
}
elseif (!isset($derived_key[$env_id])) {
// Get the Solr core identifier from the URL, to build the correct derived key.
$environment = apachesolr_environment_load($env_id);
if (isset($environment['url'])) {
$core_id = acquia_search_extract_core_id_from_environment_url($environment['url']);
$derived_key[$env_id] = _acquia_search_create_derived_key($derived_key_salt, $core_id, $key);
}
}
}
return $derived_key[$env_id];
}
/**
* Extracts and return a core ID from given URL.
*
* Returns the last part of URL within given environment URL which represents
* the core ID. E.g. for 'http://useast1-c1.acquia-search.com/solr/GHTV-36910'
* it returns 'GHTV-36910'.
* For 'http://useast1-c1.acquia-search.com/solr/GHTV-36910.dev.mysitedev'
* it returns 'GHTV-36910.dev.mysitedev'.
*
* @param string $url
*
* @return string
*/
function acquia_search_extract_core_id_from_environment_url($url) {
return substr($url, strrpos($url, '/') + 1);
}
/**
* Derive a key for the solr hmac using a salt, id and key.
*/
function _acquia_search_create_derived_key($salt, $id, $key) {
$derivation_string = $id . 'solr' . $salt;
return hash_hmac('sha1', str_pad($derivation_string, 80, $derivation_string), $key);
}
/**
* Creates an authenticator based on a data string and HMAC-SHA1.
*/
function acquia_search_authenticator($string, $nonce, $derived_key = NULL, $env_id = NULL, $time = NULL) {
if (empty($derived_key)) {
$derived_key = _acquia_search_derived_key($env_id);
}
if (empty($derived_key)) {
// Expired or invalid subscription - don't continue.
return '';
}
else {
// @see http://stackoverflow.com/questions/2524680/check-whether-the-string-is-a-unix-timestamp
if (!(is_numeric($time) && (int) $time == $time)) {
// Use time() instead of REQUEST_TIME so that long-running operations like
// `drush solr-index` continually have fresh request times. Use of
// REQUEST_TIME will cause Acquia Search to respond with a 403 Forbidden
// after the acquia_solr_time value is older than 15 minutes.
$time = time();
}
return 'acquia_solr_time=' . $time . '; acquia_solr_nonce=' . $nonce . '; acquia_solr_hmac=' . hash_hmac('sha1', $time . $nonce . $string, $derived_key) . ';';
}
}
/**
* Validate the authenticity of returned data using a nonce and HMAC-SHA1.
*
* @return
* TRUE or FALSE.
*/
function acquia_search_valid_response($hmac, $nonce, $string, $derived_key = NULL, $env_id = NULL) {
if (empty($derived_key)) {
$derived_key = _acquia_search_derived_key($env_id);
}
return $hmac == hash_hmac('sha1', $nonce . $string, $derived_key);
}
/**
* Look in the headers and get the hmac_digest out
* @return string hmac_digest
*
*/
function acquia_search_extract_hmac($headers) {
$reg = array();
if (is_array($headers)) {
foreach ($headers as $name => $value) {
if (strtolower($name) == 'pragma' && preg_match("/hmac_digest=([^;]+);/i", $value, $reg)) {
return trim($reg[1]);
}
}
}
return '';
}
/**
* Implementation of hook_apachesolr_modify_query().
*
* Possibly alters the query type ('defType') param to edismax.
*/
function acquia_search_apachesolr_query_alter($query) {
$environment = apachesolr_environment_load($query
->solr('getId'));
// @todo - does it make sense to check $caller too?
if (!acquia_search_environment_connected($environment) || $query
->getParam('qt') || $query
->getParam('defType')) {
// This is a 'mlt' query or something else custom.
return;
}
// Set the qt to edismax if we have keywords, and we always use it, or are
// using a wildcard (* or ?).
$keys = $query
->getParam('q');
if ($keys && (($wildcard = preg_match('/\\S+[*?]/', $keys)) || variable_get('acquia_search_edismax_default', 0))) {
$query
->addParam('defType', 'edismax');
if ($wildcard) {
$keys = preg_replace_callback('/(\\S+[*?]\\S*)/', '_acquia_search_lower', $keys);
$query
->replaceParam('q', $keys);
}
}
}
/**
* Convert to lower-case any keywords containing a wildcard.
*/
function _acquia_search_lower($matches) {
return drupal_strtolower($matches[1]);
}
Functions
Name | Description |
---|---|
acquia_search_acquia_subscription_status | Implementation of hook_acquia_subscription_status(). |
acquia_search_add_apachesolr_overrides | Overrides apachesolr configs to talk to the proper Acquia search core. |
acquia_search_add_form_status_message | Adds Acquia search connection details to the given form. |
acquia_search_apachesolr_query_alter | Implementation of hook_apachesolr_modify_query(). |
acquia_search_authenticator | Creates an authenticator based on a data string and HMAC-SHA1. |
acquia_search_auth_cookie | Modify a solr base url and construct a hmac authenticator cookie. |
acquia_search_available_cores | Retrieves all available search cores as set in the subscription. |
acquia_search_derived_key_salt | Returns the subscription's salt used to generate the derived key. |
acquia_search_disable | Implementation of hook_disable(). |
acquia_search_disable_revert_defaults | Helper function to revert the default environment after disable |
acquia_search_enable | Implementation of hook_enable(). |
acquia_search_enable_acquia_solr_environment | Create a new record pointing to the Acquia apachesolr search server and set it as the default |
acquia_search_environment_connected | Tests whether the environment is connected to Acquia Search. |
acquia_search_environment_delete_access | Delete environment page access. |
acquia_search_environment_edit_form_validate | |
acquia_search_extract_core_id_from_environment_url | Extracts and return a core ID from given URL. |
acquia_search_extract_hmac | Look in the headers and get the hmac_digest out |
acquia_search_form_apachesolr_environment_edit_form_alter | Implementation of hook_form_[form_id]_alter(). |
acquia_search_form_apachesolr_index_action_form_alter | Implementation of hook_form_[form_id]_alter(). |
acquia_search_form_apachesolr_settings_alter | Implementation of hook_form_[form_id]_alter(). |
acquia_search_get_derived_key_for_core | Returns derived key for the given core ID. |
acquia_search_get_environment | Predefined Acquia Search network environment |
acquia_search_get_read_only_mode_warning | Returns formatted message about read-only mode. |
acquia_search_get_search_status_message | Returns formatted message about Acquia Search connection details. |
acquia_search_help | Implementation of hook_help(). |
acquia_search_init | Implements hook_init(). |
acquia_search_is_environment_overridden_to_read_only | Determines whether the given environment is in the read-only mode. |
acquia_search_menu_alter | Implementation of hook_menu_alter(). |
acquia_search_ping | Pings the search core. |
acquia_search_valid_response | Validate the authenticity of returned data using a nonce and HMAC-SHA1. |
_acquia_search_create_derived_key | Derive a key for the solr hmac using a salt, id and key. |
_acquia_search_derived_key | Get the derived key for the solr hmac using the information shared with acquia.com. |
_acquia_search_lower | Convert to lower-case any keywords containing a wildcard. |
_acquia_search_set_version | Helper function to cache the Acquia Search version. |