View source
<?php
function apachesolr_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/apachesolr',
'title' => t('Apache Solr'),
'description' => t('Administer Apache Solr.'),
'callback' => 'drupal_get_form',
'callback arguments' => 'apachesolr_settings',
'access' => user_access('administer search'),
);
$items[] = array(
'path' => 'admin/settings/apachesolr/settings',
'title' => t('Settings'),
'weight' => -10,
'callback' => 'drupal_get_form',
'callback arguments' => 'apachesolr_settings',
'access' => user_access('administer search'),
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/settings/apachesolr/index',
'title' => t('Search index'),
'weight' => -8,
'callback' => 'apachesolr_index_page',
'access' => user_access('administer search'),
'type' => MENU_LOCAL_TASK,
);
}
return $items;
}
function apachesolr_help($section) {
switch ($section) {
case 'admin/settings/apachesolr/index':
$remaining = 0;
$total = 0;
foreach (module_list() as $module) {
if (module_hook($module, 'search')) {
$status = module_invoke($module, 'search', 'status');
$remaining += $status['remaining'];
$total += $status['total'];
}
}
return t('Apache Solr search index is generated by !cron. %percentage of the site has been indexed. There @items left to index.', array(
'!cron' => l(t('running cron'), 'admin/logs/status/run-cron', array(), 'destination=admin/settings/apachesolr/index'),
'%percentage' => (int) min(100, 100 * ($total - $remaining) / max(1, $total)) . '%',
'@items' => format_plural($remaining, t('is 1 item'), t('are @count items')),
));
}
}
function apachesolr_settings() {
$form = array();
$requirements = apachesolr_requirements('runtime');
$status = $requirements['apachesolr']['severity'] == 2 ? 'error' : 'status';
drupal_set_message($requirements['apachesolr']['value'], $status);
$form['apachesolr_host'] = array(
'#type' => 'textfield',
'#title' => t('Solr host name'),
'#default_value' => variable_get('apachesolr_host', 'localhost'),
'#description' => t('Host name of your Solr server, e.g. <code>localhost</code> or <code>example.com</code>.'),
);
$form['apachesolr_port'] = array(
'#type' => 'textfield',
'#title' => t('Solr port'),
'#default_value' => variable_get('apachesolr_port', '8983'),
'#description' => t('Port on which the Solr server listens. Tomcat is 8080 by default.'),
);
$form['apachesolr_path'] = array(
'#type' => 'textfield',
'#title' => t('Solr path'),
'#default_value' => variable_get('apachesolr_path', '/solr'),
'#description' => t('Path that identifies the Solr request handler to be used. Leave this as /solr for now.'),
);
$options = array();
foreach (array(
5,
10,
15,
20,
25,
30,
40,
50,
60,
70,
80,
90,
100,
) as $option) {
$options[$option] = $option;
}
$form['apachesolr_rows'] = array(
'#type' => 'select',
'#title' => t('Results per page'),
'#default_value' => variable_get('apachesolr_rows', 10),
'#options' => $options,
'#description' => t('The number of results that will be shown per page.'),
);
$form['apachesolr_failure'] = array(
'#type' => 'select',
'#title' => t('On failure'),
'#options' => array(
'show_error' => t('Show error'),
'show_drupal_results' => t('Show core Drupal results'),
'show_no_results' => t('Show no results'),
),
'#default_value' => variable_get('apachesolr_failure', 'show_error'),
'#description' => t('What to display if ApacheSolr search is not available.'),
);
return system_settings_form($form);
}
function apachesolr_failure($search_name, $querystring) {
$fail_rule = variable_get('apachesolr_failure', 'show_error');
switch ($fail_rule) {
case 'show_error':
drupal_set_message(t('The Apache Solr search engine is not available. Please contact your site administrator.'), 'error');
break;
case 'show_drupal_results':
drupal_set_message(t("%search_name is not available. Your search is being redirected.", array(
'%search_name' => $search_name,
)));
drupal_goto('search/node/' . drupal_urlencode($querystring));
break;
case 'show_no_results':
return;
}
}
function apachesolr_requirements($phase) {
$t = get_t();
if ($phase == 'runtime') {
$host = variable_get('apachesolr_host', 'localhost');
$port = variable_get('apachesolr_port', 8983);
$path = variable_get('apachesolr_path', '/solr');
$ping = FALSE;
try {
$solr =& apachesolr_get_solr($host, $port, $path);
$ping = @$solr
->ping();
if (!$ping) {
throw new Exception(t('No Solr instance available during indexing'));
}
} catch (Exception $e) {
watchdog('Apache Solr', $e
->getMessage(), WATCHDOG_ERROR);
}
$value = $ping ? $t('Solr can be pinged.') : $t('No Solr instance is available.');
$severity = $ping ? 0 : 2;
$description = theme('item_list', array(
$t('Host: %host', array(
'%host' => $host,
)),
$t('Port: %port', array(
'%port' => $port,
)),
$t('Path: %path', array(
'%path' => $path,
)),
));
$requirements['apachesolr'] = array(
'title' => $t('ApacheSolr'),
'value' => $value,
'description' => $description,
'severity' => $severity,
);
return $requirements;
}
}
function apachesolr_index_page() {
include_once drupal_get_path('module', 'apachesolr') . '/Solr_Base_Query.php';
$fields = Solr_Base_Query::get_fields_in_index();
$rows = array();
foreach ($fields as $name => $field) {
$rows[] = array(
$name,
$field->type,
);
}
$output = '';
$output .= theme('table', array(
t('Field name'),
t('Field index type'),
), $rows);
$output .= drupal_get_form('apachesolr_delete_index_form');
return $output;
}
function apachesolr_delete_index_form() {
$form = array();
$form['markup'] = array(
'#type' => 'markup',
'#value' => '<h3>Solr Index</h3>',
);
$form['delete_index'] = array(
'#type' => 'checkbox',
'#title' => t('Delete all documents'),
'#description' => t('This option deletes all of the documents in the Solr index. You would do this if the index contains wrong content that you need to purge. This action shouldn\'t be necessary in normal cases. After deleting you will need to rebuild the index by running cron.'),
'#default_value' => NULL,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Delete the index'),
);
return $form;
}
function apachesolr_delete_index_form_validate($form, $form_values) {
if (!$form_values['delete_index']) {
form_set_error('delete_index', t('If you want to delete the Solr index, you must check the confirmation box.'));
}
if (!user_access('administer site configuration')) {
drupal_access_denied();
}
}
function apachesolr_delete_index_form_submit($form, $form_values) {
if ($form_values['delete_index']) {
try {
$solr =& apachesolr_get_solr(variable_get('apachesolr_host', 'localhost'), variable_get('apachesolr_port', 8983), variable_get('apachesolr_path', '/solr'));
$solr
->deleteByQuery('*:*');
$solr
->commit();
variable_del('apachesolr_last_change');
variable_del('apachesolr_last_id');
drupal_set_message(t('The Solr content index has been erased. You must now !run_cron until your entire site has been re-indexed.', array(
'!run_cron' => l(t('run cron'), 'admin/logs/status/run-cron', array(
'fragment' => 'module-user',
)),
)));
} catch (Exception $e) {
watchdog('Apache Solr', $e
->getMessage(), WATCHDOG_ERROR);
}
}
}
class ApacheSolrUpdate {
public static $_namespaces = array();
static function reset($namespace) {
variable_del($namespace . '_last_change');
variable_del($namespace . '_last_id');
}
static function get_change($namespace) {
$var = variable_get($namespace . '_last_change', 0);
return $var;
}
static function get_last($namespace) {
$var = variable_get($namespace . '_last_id', 0);
return $var;
}
static function getNodesToIndex($namespace) {
register_shutdown_function('apachesolr_shutdown');
$cron_change = self::get_change($namespace);
$cron_last = self::get_last($namespace);
$cron_limit = variable_get('search_cron_limit', 100);
$result = db_query_range('SELECT GREATEST(IF(c.last_comment_timestamp IS NULL, 0, c.last_comment_timestamp), n.changed) as last_change, n.nid ' . 'FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid ' . 'WHERE n.status = 1 ' . 'AND ((GREATEST(IF(c.last_comment_timestamp IS NULL , 0, c.last_comment_timestamp ), n.changed) = %d AND n.nid > %d) OR n.changed > %d OR c.last_comment_timestamp > %d) ' . 'ORDER BY last_change ASC, n.nid ASC', $cron_change, $cron_last, $cron_change, $cron_change, 0, $cron_limit);
return $result;
}
static function success($namespace, $last_change, $last_id) {
self::$_namespaces[$namespace] = array(
'last_change' => $last_change,
'last_id' => $last_id,
);
}
static function update_index($namespace) {
$solr = FALSE;
try {
$solr =& apachesolr_get_solr(variable_get('apachesolr_host', 'localhost'), variable_get('apachesolr_port', 8983), variable_get('apachesolr_path', '/solr'));
if (!$solr
->ping()) {
throw new Exception(t('No Solr instance available during indexing'));
}
} catch (Exception $e) {
watchdog('Apache Solr', $e
->getMessage(), WATCHDOG_ERROR);
return;
}
$cck_fields = apachesolr_cck_fields();
$result = self::getNodesToIndex($namespace);
$count = 0;
$documents = array();
while ($row = db_fetch_object($result)) {
$solr_last_change = $row->last_change;
$solr_last_id = $row->nid;
$node = node_load($row->nid, NULL, TRUE);
if ($node->nid) {
$node = node_build_content($node, FALSE, FALSE);
$node->body = drupal_render($node->content);
$text = check_plain($node->title) . ' ' . $node->body;
$extra = node_invoke_nodeapi($node, 'update index');
foreach ($extra as $t) {
$text .= $t;
}
try {
$document = new Apache_Solr_Document();
$site = url(NULL, NULL, NULL, TRUE);
$hash = md5($site);
$document->site = $site;
$document->hash = $hash;
$document->url = url('node/' . $node->nid, NULL, NULL, TRUE);
$document->nid = $node->nid;
$document->uid = $node->uid;
$document->title = $node->title;
$document->body = $node->body;
$document->type = $node->type;
$document->changed = $node->changed;
$document->comment_count = $node->comment_count;
$document->name = $node->name;
$document->language = $node->language;
if (function_exists('drupal_get_path_alias')) {
$language = empty($node->language) ? '' : $node->language;
$path = 'node/' . $node->nid;
$output = drupal_get_path_alias($path, $language);
if ($output && $output != $path) {
$document->path = $output;
$text .= $output;
}
}
foreach ($cck_fields as $key => $cck_info) {
if (isset($node->{$key})) {
$function = $cck_info['callback'];
if ($cck_info['callback'] && function_exists($function)) {
$field = call_user_func_array($function, array(
$node,
$key,
));
}
else {
$field = $node->{$key};
}
$index_key = apachesolr_index_key($cck_info);
foreach ($field as $value) {
if (isset($value['view']) && strlen($value['view'])) {
if ($cck_info['multiple']) {
$document
->setMultiValue($index_key, $value['view']);
}
else {
$document->{$index_key} = $value['view'];
}
}
}
}
}
$document->stitle = $node->title;
if (is_array($node->taxonomy)) {
foreach ($node->taxonomy as $term) {
$ancestors = taxonomy_get_parents_all($term->tid);
foreach ($ancestors as $ancestor) {
$document
->setMultiValue('tid', $ancestor->tid);
$document
->setMultiValue('imfield_vid' . $ancestor->vid, $ancestor->tid);
$document
->setMultiValue('vid', $ancestor->vid);
$document
->setMultiValue('taxonomy_name', $ancestor->name);
$text .= ' ' . $ancestor->name;
}
}
}
$document->text = $text;
foreach (module_implements('apachesolr_update_index') as $module) {
$function = $module . '_apachesolr_update_index';
$function($document, $node);
}
$documents[] = $document;
} catch (Exception $e) {
watchdog('Apache Solr', $e
->getMessage(), WATCHDOG_ERROR);
}
}
self::success('apachesolr', $solr_last_change, $solr_last_id);
}
if (is_object($solr) && count($documents) > 0) {
try {
watchdog('Apache Solr', t('Adding @count documents.', array(
'@count' => count($documents),
)));
$docs_chunk = array_chunk($documents, 20);
foreach ($docs_chunk as $docs) {
$solr
->addDocuments($docs);
}
$solr
->commit();
$solr
->optimize(FALSE, FALSE);
} catch (Exception $e) {
watchdog('Apache Solr', $e
->getMessage(), WATCHDOG_ERROR);
}
}
}
}
function apachesolr_apachesolr_facets() {
return array(
'type',
);
}
function apachesolr_shutdown() {
foreach (ApacheSolrUpdate::$_namespaces as $namespace => $vars) {
extract($vars);
if ($last_change && $last_id) {
variable_set("{$namespace}_last_change", $last_change);
variable_set("{$namespace}_last_id", $last_id);
}
}
}
function apachesolr_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
switch ($op) {
case 'delete':
try {
$solr =& apachesolr_get_solr(variable_get('apachesolr_host', 'localhost'), variable_get('apachesolr_port', 8983), variable_get('apachesolr_path', '/solr'));
$solr
->deleteById(url('node/' . $node->nid, NULL, NULL, TRUE));
$solr
->commit();
} catch (Exception $e) {
watchdog('Apache Solr', $e
->getMessage(), WATCHDOG_ERROR);
}
break;
}
}
function apachesolr_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks['sort'] = array(
'info' => t('ApacheSolr Core: Sorting'),
);
$blocks['type'] = array(
'info' => t('ApacheSolr Core: Filter by type'),
);
return $blocks;
case 'view':
if (apachesolr_has_searched()) {
$response =& apachesolr_static_response_cache();
if (empty($response)) {
return;
}
$query =& apachesolr_drupal_query();
$facet_display_limits = variable_get('apachesolr_facet_query_limits', array());
switch ($delta) {
case 'sort':
$sorts = array(
'relevancy' => array(
'name' => t('Relevancy'),
'default' => 'asc',
),
'stitle' => array(
'name' => t('Title'),
'default' => 'asc',
),
'type' => array(
'name' => t('Type'),
'default' => 'asc',
),
'name' => array(
'name' => t('Author'),
'default' => 'asc',
),
'changed' => array(
'name' => t('Date'),
'default' => 'desc',
),
);
$solrsorts = array();
$sort_parameter = isset($_GET['solrsort']) ? check_plain($_GET['solrsort']) : FALSE;
foreach (explode(',', $sort_parameter) as $solrsort) {
$parts = explode(' ', $solrsort);
if (!empty($parts[0]) && !empty($parts[1])) {
$solrsorts[$parts[0]] = $parts[1];
}
}
$sort_links = array();
$path = 'search/' . arg(1) . '/' . $query
->get_query();
foreach ($sorts as $type => $sort) {
$new_sort = isset($solrsorts[$type]) ? $solrsorts[$type] == 'asc' ? 'desc' : 'asc' : $sort['default'];
$sort_links[] = theme('apachesolr_sort_link', $sort['name'], $path, $type == "relevancy" ? '' : "solrsort={$type} {$new_sort}", isset($solrsorts[$type]) ? $solrsorts[$type] : '');
}
return array(
'subject' => t('Sort by'),
'content' => theme('apachesolr_sort_list', $sort_links),
);
case 'type':
$filter_by = t('Filter by type');
return apachesolr_facet_block($response, $query, $delta, $filter_by, 'apachesolr_get_type');
default:
break;
}
}
break;
case 'configure':
return apachesolr_facetcount_form($delta);
break;
case 'save':
apachesolr_facetcount_save($delta, $edit);
break;
}
}
function apachesolr_facet_block($response, $query, $delta, $filter_by, $facet_callback = FALSE) {
if (!empty($response->facet_counts->facet_fields->{$delta})) {
$contains_active = FALSE;
$items = array();
foreach ($response->facet_counts->facet_fields->{$delta} as $facet => $count) {
$unclick_link = '';
unset($active);
if ($facet_callback && function_exists($facet_callback)) {
$facet_text = $facet_callback($facet);
}
else {
$facet_text = $facet;
}
$new_query = clone $query;
if ($active = $query
->has_field($delta, $facet)) {
$contains_active = TRUE;
$new_query
->remove_field($delta, $facet);
$path = 'search/' . arg(1) . '/' . $new_query
->get_query();
$unclick_link = theme('apachesolr_unclick_link', $path);
}
else {
$new_query
->add_field($delta, $facet);
$path = 'search/' . arg(1) . '/' . $new_query
->get_query();
}
$countsort = $count == 0 ? '' : 1 / $count;
if ($response->numFound == 1 && !$active) {
}
else {
$items[$active ? $countsort . $facet : 1 + $countsort . $facet] = theme('apachesolr_facet_item', $facet_text, $count, $path, $active, $unclick_link, $response->numFound);
}
}
if (count($items) > 0) {
ksort($items);
$facet_display_limit = isset($facet_display_limits[$delta]) ? $facet_display_limits[$delta] : 10;
$items = array_slice($items, 0, $facet_display_limit == -1 ? NULL : $facet_display_limit);
$output = theme('apachesolr_facet_list', $items);
return array(
'subject' => t('@filter_by', array(
'@filter_by' => $filter_by,
)),
'content' => $output,
);
}
}
return NULL;
}
function apachesolr_get_type($facet) {
return node_get_types('name', $facet);
}
function apachesolr_form_alter($form_id, &$form) {
$arg = arg(1);
$alias = drupal_lookup_path('alias', "search/{$arg}");
if ($alias && $arg != 'node' && $arg != 'user' && (preg_match("&/search/{$arg}&", $form['#action']) || preg_match("&/{$alias}&", $form['#action']) || strpos($form['#action'], $alias))) {
if (!isset($_POST['form_id'])) {
$form['#validate']['apachesolr_search_validate'] = array();
if (empty($form['basic']['inline']['keys']['#default_value'])) {
return;
}
if ($query =& apachesolr_drupal_query()) {
$form['basic']['inline']['keys']['#default_value'] = $query
->get_query_basic();
}
}
}
}
function apachesolr_facetcount_form($delta) {
$facet_query_limits = variable_get('apachesolr_facet_query_limits', array());
$form['apachesolr_facet_query_limit'] = array(
'#type' => 'textfield',
'#title' => t('Facet Query Limit'),
'#required' => TRUE,
'#description' => t('The number of facet links to show in this block. Set to -1 for unlimited. Default is 10.'),
'#default_value' => isset($facet_query_limits[$delta]) ? $facet_query_limits[$delta] : 10,
);
return $form;
}
function apachesolr_facetcount_save($delta, $edit) {
$facet_query_limits = variable_get('apachesolr_facet_query_limits', array());
$facet_query_limits[$delta] = intval($edit['apachesolr_facet_query_limit']);
variable_set('apachesolr_facet_query_limits', $facet_query_limits);
}
function apachesolr_has_searched($searched = NULL) {
static $_searched = FALSE;
if (is_bool($searched)) {
$_searched = $searched;
}
return $_searched;
}
function &apachesolr_get_solr($host = 'localhost', $port = 8983, $path = '/solr') {
static $solr_cache;
if (empty($solr_cache[$host][$port][$path])) {
$include_path = get_include_path();
set_include_path('./' . drupal_get_path('module', 'apachesolr') . '/SolrPhpClient/');
include_once 'Apache/Solr/Service.php';
set_include_path($include_path);
$solr_cache[$host][$port][$path] = new Apache_Solr_Service($host, $port, $path);
}
return $solr_cache[$host][$port][$path];
}
function &apachesolr_static_response_cache($response = NULL) {
static $_response;
if (!empty($response)) {
$_response = drupal_clone($response);
}
return $_response;
}
function &apachesolr_drupal_query($keys = NULL, $reset = FALSE) {
static $_queries;
if ($reset) {
unset($_queries);
}
if (empty($keys)) {
$keys = search_get_keys();
}
if (empty($_queries) || empty($_queries[$keys])) {
include_once drupal_get_path('module', 'apachesolr') . '/Solr_Base_Query.php';
$_queries[$keys] = new Solr_Base_Query($keys);
}
return $_queries[$keys];
}
function apachesolr_base_url() {
return "http://" . variable_get('apachesolr_host', 'localhost') . ':' . variable_get('apachesolr_port', '8983') . variable_get('apachesolr_path', '/solr');
}
function apachesolr_index_key($field) {
switch ($field['index_type']) {
case 'text':
$type_prefix = 't';
break;
case 'string':
$type_prefix = 's';
break;
case 'integer':
$type_prefix = 'i';
break;
case 'double':
$type_prefix = 'p';
break;
case 'boolean':
$type_prefix = 'b';
break;
case 'date':
$type_prefix = 'd';
break;
case 'float':
$type_prefix = 'f';
break;
default:
$type_prefix = 's';
}
$sm = $field['multiple'] ? 'm' : 's';
return $type_prefix . $sm . $field['name'];
}
function apachesolr_cck_fields() {
static $_fields;
if (module_exists('content')) {
$mappings = module_invoke_all('apachesolr_cck_field_mappings');
if (is_null($_fields)) {
$_fields = array();
$result = db_query("SELECT i.field_name, f.multiple, f.type, i.widget_type FROM {node_field_instance} i INNER JOIN {node_field} f ON i.field_name = f.field_name;");
while ($row = db_fetch_object($result)) {
if ($row->type == 'text' && in_array($row->widget_type, array(
'options_select',
'options_buttons',
)) || in_array($row->type, array_keys($mappings))) {
$_fields[$row->field_name] = array(
'name' => $row->field_name,
'multiple' => $row->multiple ? TRUE : FALSE,
'field_type' => $row->type,
'index_type' => empty($mappings) ? 'string' : $mappings[$row->type]['index_type'],
'callback' => empty($mappings[$row->type]['callback']) ? NULL : $mappings[$row->type]['callback'],
);
}
}
}
return $_fields;
}
else {
return array();
}
}
function apachesolr_simpletest() {
$dir = drupal_get_path('module', 'apachesolr') . '/tests';
$tests = file_scan_directory($dir, '\\.test$');
return array_keys($tests);
}
function theme_apachesolr_facet_item($name, $count, $path, $active = FALSE, $unclick_link = NULL, $num_found = NULL) {
$attributes = array();
if ($active) {
$attributes['class'] = 'active';
}
if ($unclick_link) {
return $unclick_link . ' ' . check_plain($name);
}
else {
return l($name . " ({$count})", $path, $attributes, isset($_GET['solrsort']) ? "solrsort=" . check_plain($_GET['solrsort']) : FALSE);
}
}
function theme_apachesolr_unclick_link($path) {
return l("(-)", $path, NULL, isset($_GET['solrsort']) ? "solrsort=" . check_plain($_GET['solrsort']) : FALSE);
}
function theme_apachesolr_sort_link($text, $path, $query, $direction = NULL) {
$icon = '';
if ($direction) {
$icon = theme('tablesort_indicator', $direction);
}
return $icon . ' ' . l($text, $path, NULL, $query);
}
function theme_apachesolr_facet_list($items) {
return theme('item_list', $items);
}
function theme_apachesolr_sort_list($items) {
return theme('item_list', $items);
}
function theme_apachesolr_breadcrumb_type($type) {
return node_get_types('name', $type);
}