apachesolr_reference.module in Apachesolr Reference 7
functionality for creating reference fields to apache solr objects.
File
apachesolr_reference.moduleView source
<?php
/**
* @file
* functionality for creating reference fields to apache solr objects.
*/
/**
* Implements hook_menu().
*/
function apachesolr_reference_menu() {
$items = array();
$items['apachesolr-reference/autocomplete/%/%/%/%'] = array(
'title' => 'Entity Reference Autocomplete',
'page callback' => 'apachesolr_reference_autocomplete_callback',
'page arguments' => array(
2,
3,
4,
5,
),
'access callback' => 'apachesolr_reference_autocomplete_access_callback',
'access arguments' => array(
2,
3,
4,
),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Menu Access callback for the autocomplete widget.
*
* @param string $field_name
* The name of the entity-reference field.
* @param string $entity_type
* The entity type.
* @param string $bundle_name
* The bundle name.
*
* @return bool
* True if user can access this menu item.
*/
function apachesolr_reference_autocomplete_access_callback($field_name, $entity_type, $bundle_name) {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle_name);
$type_check = $field['type'] == 'apachesolr_reference';
$access_check = field_access('edit', $field, $entity_type);
if (!$field || !$instance || !$type_check || !$access_check) {
return FALSE;
}
return TRUE;
}
/**
* Menu callback: autocomplete the label of a SOLR object.
*
* @param string $field_name
* The name of the entity-reference field.
* @param string $entity_type
* The entity type.
* @param string $bundle_name
* The bundle name.
* @param string $entity_id
* Optional; The entity ID the entity-reference field is attached to.
* Defaults to ''.
* @param string $langcode
* the language to use.
* @param string $string
* The label of the SOLR object to query by.
*
* @return json
* A json array of matching results from SOLR.
*/
function apachesolr_reference_autocomplete_callback($field_name, $entity_type, $bundle_name, $entity_id = '', $langcode = LANGUAGE_NONE, $string = '') {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle_name);
return apachesolr_reference_autocomplete_callback_get_matches($field, $instance, $entity_type, $entity_id, $langcode, $string);
}
/**
* Return JSON based on given field, instance and string.
*
* @param array $field
* The field array defintion.
* @param array $instance
* The instance array defintion.
* @param string $entity_type
* The entity type.
* @param int|string $entity_id
* Optional; The entity ID the entity-reference field is attached to.
* @param string $langcode
* Optional: The language to use.
* @param string $string
* The label of the entity to query by.
*
* @return json
* A json array of results.
*/
function apachesolr_reference_autocomplete_callback_get_matches($field, $instance, $entity_type, $entity_id = '', $langcode = LANGUAGE_NONE, $string = '') {
$matches = array();
$entity = NULL;
if ($entity_id !== 'NULL') {
$entity = entity_load_single($entity_type, $entity_id);
if (!$entity || !entity_access('view', $entity_type, $entity)) {
return MENU_ACCESS_DENIED;
}
}
// Was a value passed to lookup?
if (isset($string)) {
$string = trim($string);
$string = str_replace('"', '', $string);
// Retrieve the values we need to know which solr instance to use.
$solr_env = _apachesolr_reference_default_solr_environment($field['settings']);
$search_fields = $field['settings']['search_fields'];
// Build the SOLR query.
$field_query = array(
$field['settings']['field_query'],
);
$field_query[] = $search_fields['label'] . ':("' . $string . '" OR ' . $string . '*)';
// If Entity and language is not und, add language filter.
if ($langcode && $langcode != LANGUAGE_NONE) {
$field_query[] = 'ss_language: ' . $langcode;
}
// List of SOLR fields to return.
$field_list = $search_fields;
// Sort SOLR results by the label value.
$sort = array(
$search_fields['label'],
'asc',
);
// Query solr to retrieve data for objects.
if ($solr_objects = _apachesolr_reference_solr_query($solr_env, $field_query, $field_list, $sort)) {
// Foreach returned SOLR object add to matches array.
foreach ($solr_objects as $solr_object) {
$id = $solr_object->{$search_fields}['id'];
$label = $solr_object->{$search_fields}['label'];
$key = $label . '(' . $id . ')';
$matches[$key] = '<div class="reference-autocomplete">' . $label . '</div>';
}
}
}
drupal_json_output($matches);
}
/**
* Implements hook_field_info().
*/
function apachesolr_reference_field_info() {
return array(
'apachesolr_reference' => array(
'label' => t('Apache SOLR Reference'),
'description' => t('Provides a reference field to remote SOLR content'),
'settings' => array(
'solr_env' => apachesolr_default_environment(),
'field_query' => '',
'search_fields' => array(
'id' => 'entity_id',
'label' => 'label',
),
'field_list' => '',
),
'instance_settings' => array(),
'default_widget' => 'apachesolr_reference_autocomplete',
'default_formatter' => 'apachesolr_reference_content_formatter_label',
),
);
}
/**
* Implements hook_formatter_info().
*/
function apachesolr_reference_field_formatter_info() {
return array(
'apachesolr_reference_formatter_ids' => array(
'label' => t('SOLR IDs'),
'field types' => array(
'apachesolr_reference',
),
),
'apachesolr_reference_formatter_custom' => array(
'label' => t('SOLR Reference Custom Content'),
'field types' => array(
'apachesolr_reference',
),
'settings' => array(
'custom_display' => '',
),
),
'apachesolr_reference_formatter_label' => array(
'label' => t('SOLR Reference Label'),
'field types' => array(
'apachesolr_reference',
),
'settings' => array(
'label' => 'label',
),
),
'apachesolr_reference_formatter_label_link' => array(
'label' => t('SOLR Reference Label (linked)'),
'field types' => array(
'apachesolr_reference',
),
'settings' => array(
'label' => 'label',
'url' => 'url',
),
),
);
}
/**
* Implements hook_field_formatter_settings_form().
*/
function apachesolr_reference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$element = array();
$solr_fields = array();
foreach ($field['settings']['search_fields'] as $solr_field) {
$solr_fields[$solr_field] = $solr_field;
}
// If extra solr fields to retrieve add them to fields array.
if (strlen($field['settings']['field_list']) > 0) {
$add_fields = explode(',', $field['settings']['field_list']);
foreach ($add_fields as $solr_field) {
$solr_fields[$solr_field] = $solr_field;
}
}
// Change settings form based on output.
switch ($display['type']) {
case 'apachesolr_reference_formatter_custom':
$element['custom_display'] = array(
'#type' => 'textarea',
'#title' => t('Custom Display'),
'#description' => t('Use the provided tokens to display the SOLR field values. Field tokens will be replaced with their actual value.'),
'#default_value' => $settings['custom_display'],
);
$element['custom_display']['#description'] .= '<br />' . t('The following SOLR Field tokens are available.') . ":<br />";
foreach ($solr_fields as $solr_field) {
$element['custom_display']['#description'] .= '[' . $solr_field . ']' . '<br />';
}
$element['custom_display']['#description'] .= '<br /> Multi value fields may have deltas added to the token, ":0",":1", etc ...' . 'Example: [label:1]';
break;
case 'apachesolr_reference_formatter_label':
$element['label'] = array(
'#type' => 'select',
'#title' => t('Label Field'),
'#description' => t('Which SOLR field to use to display the label value.'),
'#default_value' => $settings['label'],
'#options' => $solr_fields,
);
break;
case 'apachesolr_reference_formatter_label_link':
$element['label'] = array(
'#type' => 'select',
'#title' => t('Label Field'),
'#description' => t('Which SOLR field to use to display the label value.'),
'#default_value' => $settings['label'],
'#options' => $solr_fields,
);
$element['url'] = array(
'#type' => 'select',
'#title' => t('URL Field'),
'#description' => t('Which SOLR field to use for the url value.'),
'#default_value' => $settings['url'],
'#options' => $solr_fields,
);
break;
}
return $element;
}
/**
* Implements hook_field_formatter_settings_summary().
*/
function apachesolr_reference_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$summary = '';
switch ($display['type']) {
case 'apachesolr_reference_formatter_custom':
$summary = t('Use a custom output for rendering of SOLR data.');
break;
case 'apachesolr_reference_formatter_label':
$summary = t("Use the SOLR field '@label' as the label value", array(
'@label' => $settings['label'],
));
break;
case 'apachesolr_reference_formatter_label_link':
$summary = t("Use the SOLR field '@label' as the label, the '@url' SOLR field as the url.", array(
'@label' => $settings['label'],
'@url' => $settings['url'],
));
break;
}
return $summary;
}
/**
* Implements hook_formatter_view().
*/
function apachesolr_reference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
$settings = $display['settings'];
$field_settings = $field['settings'];
// Add entity language if set.
if ($langcode != LANGUAGE_NONE) {
$field_settings['language'] = $langcode;
}
// Check to see if output is for just ids. No need to query solr for that.
// Since ID's are stored in the db.
if ($display['type'] == 'apachesolr_reference_formatter_ids') {
foreach ($items as $delta => $item) {
$element[$delta] = array(
'#markup' => $item['target_id'],
);
}
}
else {
$items = _apachesolr_reference_fetch_field_items($items, $field_settings);
if (count($items) > 0) {
switch ($display['type']) {
// Display a custom output, replacing tokens with actual field values.
case 'apachesolr_reference_formatter_custom':
foreach ($items as $delta => $item) {
$tokens = array();
$token_values = array();
foreach ($field_settings['search_fields'] as $search_field) {
if (isset($item->{$search_field})) {
// Multi value? add deltas.
if (is_array($item->{$search_field})) {
foreach ($item->search_field as $sdelta => $value) {
$tokens[] = '[' . $search_field . ':' . $sdelta . ']';
$token_values[] = $value;
}
$value = array_shift($item->search_field);
}
else {
$value = $item->{$search_field};
}
$tokens[] = '[' . $search_field . ']';
$token_values[] = $value;
}
}
// If additional SOLR values to retrieve, add them to array.
if (strlen($field_settings['field_list']) > 0) {
$add_fields = explode(',', $field_settings['field_list']);
foreach ($add_fields as $solr_field) {
if (isset($item->{$solr_field})) {
// Multi value? add deltas.
if (is_array($item->{$solr_field})) {
foreach ($item->{$solr_field} as $sdelta => $value) {
$tokens[] = '[' . $solr_field . ':' . $sdelta . ']';
$token_values[] = $value;
}
$value = array_shift($item->{$solr_field});
}
else {
$value = $item->{$solr_field};
}
$tokens[] = '[' . $solr_field . ']';
$token_values[] = $value;
}
}
}
$element[$delta] = array(
'#markup' => str_replace($tokens, $token_values, $settings['custom_display']),
);
}
break;
// Just display the SOLR label value.
case 'apachesolr_reference_formatter_label':
foreach ($items as $delta => $item) {
$label = $item->{$settings}['label'];
$element[$delta] = array(
'#markup' => $label,
);
}
break;
// Display linked labels.
case 'apachesolr_reference_formatter_label_link':
foreach ($items as $delta => $item) {
$label = $item->{$settings}['label'];
$url = $item->{$settings}['url'];
$element[$delta] = array(
'#markup' => l($label, $url, array(
'html' => TRUE,
)),
);
}
break;
}
}
}
return $element;
}
/**
* Implements hook_field_widget_info().
*/
function apachesolr_reference_field_widget_info() {
return array(
'apachesolr_reference_autocomplete' => array(
'label' => t('Autocomplete'),
'description' => t('Provides a reference to remote SOLR objects.'),
'field types' => array(
'apachesolr_reference',
),
'behaviors' => array(
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
),
),
);
}
/**
* Implements hook_field_widget_form().
*/
function apachesolr_reference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$entity_type = $instance['entity_type'];
$entity = isset($element['#entity']) ? $element['#entity'] : NULL;
$field_settings = $field['settings'];
if ($instance['widget']['type'] == 'apachesolr_reference_autocomplete') {
// Prepare the autocomplete path.
if (!empty($instance['widget']['settings']['path'])) {
$autocomplete_path = $instance['widget']['settings']['path'];
}
else {
$autocomplete_path = 'apachesolr-reference/autocomplete';
}
$autocomplete_path .= '/' . $field['field_name'] . '/' . $instance['entity_type'] . '/' . $instance['bundle'] . '/';
// Use <NULL> as a placeholder in the URL when we don't have an entity.
// Most webservers collapse two consecutive slashes.
$id = 'NULL';
if ($entity) {
list($eid) = entity_extract_ids($entity_type, $entity);
if ($eid) {
$id = $eid;
}
$entity_language = _apachesolr_reference_entity_language($entity_type, $entity);
// Add entity language if not 'und'.
if ($entity_language != LANGUAGE_NONE) {
$field_settings['language'] = $entity_language;
}
}
// Add entity ID and language.
$autocomplete_path .= $id . '/' . $entity_language;
$items = _apachesolr_reference_fetch_field_items($items, $field_settings);
if (isset($items[$delta])) {
$previous_val = $items[$delta];
$previous_val = $previous_val->{$field}['settings']['search_fields']['label'] . '(' . $previous_val->{$field}['settings']['search_fields']['id'] . ')';
}
else {
$previous_val = '';
}
// Build auto complete field.
$element += array(
'#type' => 'textfield',
'#maxlength' => 1024,
'#autocomplete_path' => $autocomplete_path,
'#element_validate' => array(
'_apachesolr_reference_autocomplete_validate',
),
'#default_value' => $previous_val,
);
return array(
'target_id' => $element,
);
}
}
/**
* Validates the value of an autocomplete field.
*
* @param array $element
* the element field object
* @param array $form_state
* the form state array
* @param array $form
* the form array
*/
function _apachesolr_reference_autocomplete_validate($element, &$form_state, $form) {
// If a value was entered into the autocomplete...
$value = '';
// And the value is not empty.
if (!empty($element['#value'])) {
// Take "label (id)', match the id from parenthesis.
if (preg_match("/.+\\(([^)]+)\\)/", $element['#value'], $matches)) {
$value = $matches[1];
}
}
// Update the value of this element so the field can validate the product IDs.
form_set_value($element, $value, $form_state);
}
/**
* Implements hook_field_settings_form().
*/
function apachesolr_reference_field_settings_form($field, $instance, $has_data) {
$settings = $field['settings'];
$solr_environments = _apachesolr_reference_solr_environments();
$options = array();
foreach ($solr_environments as $env_id => $env) {
$options[$env_id] = $env['name'];
}
$form['use_default'] = array(
'#type' => 'checkbox',
'#title' => t('Use default Environment'),
'#default_value' => $settings['use_default'],
);
$form['solr_env'] = array(
'#type' => 'select',
'#title' => t('SOLR Environment'),
'#description' => t('Select the SOLR Environment to query.'),
'#options' => $options,
'#default_value' => $settings['solr_env'],
'#required' => TRUE,
'#states' => array(
// Disable the settings when the default environment is used.
'disabled' => array(
':input[name="field[settings][use_default]"]' => array(
'checked' => TRUE,
),
),
),
);
$form['field_query'] = array(
'#type' => 'textfield',
'#title' => t('Base Field Query'),
'#description' => t('The Base SOLR Query to use when searching for objects. ex. bundle:(article)'),
'#default_value' => $settings['field_query'],
'#required' => FALSE,
);
// TODO: make this dynamic so that additional fields can be added.
$form['search_fields'] = array(
'#type' => 'fieldset',
'#title' => t('SOLR Search Feilds'),
'#description' => t('Map which fields from SOLR mare used for searching.'),
);
$form['search_fields']['id'] = array(
'#type' => 'textfield',
'#title' => t('ID'),
'#description' => t('The field that hold the unique id value.'),
'#default_value' => $settings['search_fields']['id'],
'#required' => TRUE,
);
$form['search_fields']['label'] = array(
'#type' => 'textfield',
'#title' => t('Label'),
'#description' => t('The field that holds the Display label value.'),
'#default_value' => $settings['search_fields']['label'],
'#required' => TRUE,
);
// TODO: make this checkboxes? powered by the SOLR module perhaps?
$form['field_list'] = array(
'#type' => 'textarea',
'#title' => t('Field List'),
'#description' => t('A comma seperated list of additional SOLR fields to return, to be used by field formatter.'),
'#default_value' => $settings['field_list'],
);
module_load_include('module', 'apachesolr');
$solr_env = _apachesolr_reference_default_solr_environment($settings);
if ($solr_env && ($solr = apachesolr_get_solr($solr_env))) {
$solr_fields = $solr
->getFields(0);
$rows = array();
foreach ($solr_fields as $solr_field_name => $solr_field) {
// We do not allow to display 'sort_*' fields.
if (strpos($solr_field_name, 'sort_') === 0) {
continue;
}
$rows[] = array(
$solr_field_name,
);
}
$headers = array(
'Field Name',
);
$form['available_fields'] = array(
'#type' => 'fieldset',
'#title' => t('Available Fields'),
'#description' => t('Fields that are selectable from the SOLR environment.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['available_fields']['field_table'] = array(
'#markup' => theme('table', array(
'header' => $headers,
'rows' => $rows,
)),
);
}
return $form;
}
/**
* Implements hook_field_is_empty().
*/
function apachesolr_reference_field_is_empty($item, $field) {
$empty = !isset($item['target_id']) || strlen($item['target_id']) < 1;
return $empty;
}
/**
* Returns a list of the available SOLR environments.
*
* @return array
* Array of SOLR environments.
*/
function _apachesolr_reference_solr_environments() {
$environments =& drupal_static(__FUNCTION__);
if (isset($environments)) {
return $environments;
}
module_load_include('module', 'apachesolr');
$environments = apachesolr_load_all_environments();
return $environments;
}
/**
* Retrieve the default solr environment.
*
* @param array $settings
* Array of SOLR settings.
*
* @return array
* Solr environment.
*/
function _apachesolr_reference_default_solr_environment($settings) {
if ($settings['use_default'] == TRUE) {
$solr_env = apachesolr_default_environment();
}
else {
$solr_env = $settings['solr_env'];
}
return $solr_env;
}
/**
* Fetch SOLR objects for a field from a list of SOLR ids.
*
* @param array $items
* the items stored for a field
* @param array $settings
* field setting array
*
* @return array
* An array of solr objects keyed on item delta.
*/
function _apachesolr_reference_fetch_field_items($items, $settings) {
$solr_env = _apachesolr_reference_default_solr_environment($settings);
$field_query = array(
$settings['field_query'],
);
$fields = array();
foreach ($settings['search_fields'] as $field) {
$fields[$field] = $field;
}
// If additional SOLR values to retrieve, add them to array.
if (strlen($settings['field_list']) > 0) {
$add_fields = explode(',', $settings['field_list']);
foreach ($add_fields as $field) {
$fields[$field] = $field;
}
}
// Build an array of specific id's to retrieve.
$ids = array();
foreach ($items as $delta => $item) {
$ids[$delta] = '"' . $item['target_id'] . '"';
}
if (count($ids) > 0) {
$field_query[] = $settings['search_fields']['id'] . ': (' . implode(' OR ', $ids) . ')';
// If the language is set add it as a query param.
if (isset($settings['language'])) {
$field_query[] = 'ss_language: ' . $settings['language'];
}
$solr_items = array();
if ($results = _apachesolr_reference_solr_query($solr_env, $field_query, $fields)) {
foreach ($results as $result) {
$test_id = '"' . $result->{$settings}['search_fields']['id'] . '"';
if (in_array($test_id, $ids)) {
$delta = array_search($test_id, $ids);
$solr_items[$delta] = $result;
}
}
}
$items = $solr_items;
}
// Call ksort here to restore delta order specified by Drupal.
ksort($items);
return $items;
}
/**
* Queries a SOLR environment for results.
*
* @param string $solr_env
* Solr environment id.
* @param array $field_query
* solr query arguments
* @param array $field_list
* list of solr fields to return
* @param array|bool $sort
* sort method
* @param int $rows
* number of rows to return
* @param int $start
* number to start at
*
* @return array
* Array of solr results
*/
function _apachesolr_reference_solr_query($solr_env, $field_query, $field_list, $sort = FALSE, $rows = 10000, $start = 0) {
$results = FALSE;
module_load_include('module', 'apachesolr');
$query_cache =& drupal_static(__FUNCTION__, array());
// Try to load the solr environment.
try {
$solr = apachesolr_get_solr($solr_env);
} catch (Exception $e) {
watchdog('Apache Solr', nl2br(check_plain($e
->getMessage())), NULL, WATCHDOG_ERROR);
}
// Build the SOLR query.
$params = array();
$params['fq'] = $field_query;
$params['fl'] = is_array($field_list) ? implode(',', $field_list) : $field_list;
$params['rows'] = $rows;
$params['start'] = $start;
// Use params as key value to check static cache.
// To prevent running the same search on a single page load.
$cache_key = serialize($params);
if (isset($query_cache[$cache_key])) {
return $query_cache[$cache_key];
}
// Load the solr environment.
$query = new SolrBaseQuery('apachesolr', $solr, $params, '', current_path());
// Add sorting.
if (isset($params['sort'])) {
$query
->setAvailableSort($this->params['sort'][0], $this->params['sort'][1]);
$query
->setSolrsort($this->params['sort'][0], $this->params['sort'][1]);
}
$query->page = 0;
try {
// Boost parameters if apachesolr_search module is available.
apachesolr_search_add_boost_params($query);
// The way that the apachesolr module works, it static caches queries per
// environment on all page loads. This is preventing multiple queries from
// being run, since the static cache key is the searcher value.
// The searcher value is only unique per solr env, not per query.
// Because of this, need to clear apachesolrs static cache for this env
// before running query, not ideal but cannot figure out any other work arounds.
// This may have unknown effects but shouldnt be that big of a deal.
$searcher = $query
->getSearcher();
$solr_cache =& drupal_static('apachesolr_static_response_cache', array());
$solr_cache[$searcher] = NULL;
// Execute search.
list($final_query, $response) = apachesolr_do_query($query);
apachesolr_has_searched($solr
->getId(), TRUE);
} catch (Exception $e) {
watchdog('Apache Solr', nl2br(check_plain($e
->getMessage())), NULL, WATCHDOG_ERROR);
}
// Results found?
if (isset($response->response->numFound) && $response->response->numFound > 0) {
$results = $response->response->docs;
$query_cache[$cache_key] = $results;
}
return $results;
}
/**
* Function to determine which language is being used for an entity.
*
* @param string $entity_type
* The entity type.
* @param array $entity
* The entity object
*
* @return string
* The language to be used.
*/
function _apachesolr_reference_entity_language($entity_type, $entity = NULL) {
global $language;
$lang = LANGUAGE_NONE;
// First, check if Entity Translation is in play.
if ($entity && module_exists('entity_translation')) {
// Get the languages that entity translations tells us.
module_load_include('module', 'entity_translation');
$lang = entity_translation_language($entity_type, $entity);
}
elseif ($entity && isset($entity->language) && $entity->language != LANGUAGE_NONE) {
$lang = $entity->language;
}
elseif ($language->language != LANGUAGE_NONE) {
$lang = $language->language;
}
return $lang;
}