'Apache Solr search', 'description' => 'Administer Apache Solr.', 'page callback' => 'drupal_get_form', 'page arguments' => array('apachesolr_settings'), 'access callback' => 'user_access', 'access arguments' => array('administer site configuration'), ); $items['admin/settings/apachesolr/index'] = array( 'title' => 'Apache Solr search index', 'page callback' => 'apachesolr_index_page', 'access callback' => 'user_access', 'access arguments' => array('administer site configuration'), ); return $items; } function apachesolr_settings() { $form = array(); $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. localhost or example.com.'), ); $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.'), ); return system_settings_form($form); } /** * Implementation of hook_requirements(). */ function apachesolr_requirements($phase) { // Ensure translations don't break at install time $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 there is no $solr object, there is no server available, so don't continue. if (!$ping) { throw new Exception(t('No Solr instance available during indexing')); } } catch (Exception $e) { watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR); } $value = $ping ? $t('Solr can be pinged.') : $t('No Solr instance is available.'); $severity = $ping ? REQUIREMENT_OK: REQUIREMENT_ERROR; $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() { //TODO: Should we be generating the dynamic blocks based on what is indexed? // Or should we be using things like node_field.type to get this list? $response = drupal_http_request(apachesolr_base_url() ."/admin/luke?numTerms=0"); if ($response->code == '200') { $xml_response = simplexml_load_string($response->data); $fields = $xml_response->xpath("//lst[@name='fields']/lst"); $rows = array(); foreach ((array)$fields as $field) { $field_name = $field['name']; $field_type = $field->xpath('./str'); $rows[] = array( $field_name, $field_type[0], ); } } return theme('table', array(t('Field name'), t('Field index type')), $rows); } /** * The point of this class is to manage the update index needs of multiple * search modules. Each one needs to track its own list of nodes that need * updating. */ 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; } /** * Function to generically handle the fetching of nodes that need indexing on a cron run. * It takes a namespace which needs to be unique to the calling module and manages * all of the global variables and the shutdown function so that every search * implementation can have its own without needing to duplicate the query. * Returns a db_query $result. * Modules need to then call apache_update_success after each node is successfully * indexed. */ 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(n.changed, c.last_comment_timestamp) = %d AND n.nid > %d) OR (n.changed > %d OR c.last_comment_timestamp > %d)) '. 'ORDER BY GREATEST(n.changed, c.last_comment_timestamp) ASC, n.nid ASC', $cron_change, $cron_last, $cron_change, $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 { // Get the $solr object $solr =& apachesolr_get_solr(variable_get('apachesolr_host', 'localhost'), variable_get('apachesolr_port', 8983), variable_get('apachesolr_path', '/solr')); // If there is no $solr object, there is no server available, so don't continue. if (!$solr->ping()) { throw new Exception(t('No Solr instance available during indexing')); } } catch (Exception $e) { watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR); return; } // Get CCK fields list $cck_fields = apachesolr_cck_fields(); $result = self::getNodesToIndex($namespace); $count = 0; $documents = array(); while ($row = db_fetch_object($result)) { // Variables to track the last item changed. $solr_last_change = $row->last_change; $solr_last_id = $row->nid; $node = node_load($row->nid); if ($node->nid) { // Build the node body. $node = node_build_content($node, FALSE, FALSE); $node->body = drupal_render($node->content); $text = check_plain($node->title) . $node->body; // Fetch extra data normally not visible $extra = node_invoke_nodeapi($node, 'update index'); foreach ($extra as $t) { $text .= $t; } // Update solr index. try { $document = new Apache_Solr_Document(); // Let modules add to the document module_invoke_all('apachesolr_update_index', &$document, $node); $fields = array('title', 'body', 'type', 'uid', 'changed', 'nid', 'comment_count', 'name', 'language'); foreach ((array)$node as $key => $value) { if (in_array($key, $fields)) { $document->$key = $value; } if ($cck_fields && strpos($key, 'field_') === 0) { // Got a CCK field. See if it is to be indexed. if (in_array($key, array_keys($cck_fields))) { $function = $cck_fields[$key]['callback']; if ($cck_fields[$key]['callback'] && function_exists($function)) { $dynamic_fields = call_user_func_array($function, array($node, $key)); } else { $dynamic_fields = $node->$key; } if (is_array($dynamic_fields) && count($dynamic_fields) > 0) { foreach ($dynamic_fields as $field) { if (!empty($field['view'])) { $index_key = apachesolr_index_key($cck_fields[$key]); if ($cck_fields[$key]['multiple']) { $document->setMultiValue($index_key, $field['view']); } else { $document->$index_key = $field['view']; } } } } } } } if (is_array($node->taxonomy)) { foreach ($node->taxonomy as $term) { $document->setMultiValue('tid', $term->tid); // Double indexing of tids lets us do effecient searches (on tid) // and do accurate per-vocabulary faceting. $document->setMultiValue('imfield_vid' . $term->vid, $term->tid); $document->setMultiValue('vid', $term->vid); $document->setMultiValue('taxonomy_name', $term->name); } } $document->text = $text; $documents[] = $document; if ($count++ % 50 == 49) { watchdog('Apache Solr', 'Adding @count documents', array('@count' => count($documents))); $solr->addDocuments($documents); $count = 0; $documents = array(); } } catch (Exception $e) { watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR); } } self::success('apachesolr', $solr_last_change, $solr_last_id); } if (is_object($solr)) { // remaining documents try { watchdog('Apache Solr', 'Adding @count documents in cleanup', array('@count' => count($documents))); $solr->addDocuments($documents); $solr->commit(); $solr->optimize(FALSE, FALSE); } catch (Exception $e) { watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR); } } } } /** * Registered shutdown function. */ 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); } } } /** * Implementation of hook_nodeapi(). */ 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($node->nid); $solr->commit(); } catch (Exception $e) { watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR); } break; } } /** * Implementation of hook_block(). */ function apachesolr_block($op = 'list', $delta = 0, $edit = array()) { $term_title = t('ApacheSolr: Filter by term'); $type_title = t('ApacheSolr: Filter by type'); $author_title = t('ApacheSolr: Filter by author'); switch ($op) { case 'list': if (module_exists('taxonomy')) { $vocabs = taxonomy_get_vocabularies(); foreach ($vocabs as $vid => $vocab) { $blocks['imfield_vid' . $vid] = array('info' => t('ApacheSolr: Filter by @name', array('@name' => $vocab->name))); } } $blocks['type'] = array('info' => $type_title); $blocks['uid'] = array('info' => $author_title); if ($fields = apachesolr_cck_fields()) { foreach ($fields as $name => $field) { $index_key = apachesolr_index_key($field); $label = db_result(db_query("SELECT label FROM {node_field_instance} WHERE field_name = '%s'", $name)); // TODO: $index_key must be wrong here. $blocks[$index_key] = array('info' => t('ApacheSolr: Filter by @field', array('@field' => $label))); } } // Sorting block $blocks['sort'] = array('info' => t('ApacheSolr: Sorting')); return $blocks; case 'view': if (apachesolr_has_searched()) { // Get the query and response. Without these no blocks make sense. $response =& apachesolr_static_response_cache(); if (empty($response)) { return; } $query =& apachesolr_drupal_query(); // Get information needed by the rest of the blocks about limits. $facet_display_limits = variable_get('apachesolr_facet_query_limits', array()); if ((strpos($delta, 'imfield_vid') === 0) && module_exists('taxonomy')) { if (is_array($response->facets->$delta)) { $contains_active = FALSE; $terms = array(); foreach ($response->facets->$delta as $tid => $count) { $unclick_link = ''; unset($active); $term = taxonomy_get_term($tid); $new_query = clone $query; if ($active = $query->has_field('tid', $tid)) { $contains_active = TRUE; $new_query->remove_field('tid', $term->tid); $path = 'search/' . arg(1) . '/' . $new_query->get_query(); $unclick_link = theme('apachesolr_unclick_link', $path); } else { $new_query->add_field('tid', $term->tid); $path = 'search/' . arg(1) . '/' . $new_query->get_query(); } $countsort = $count == 0 ? '' : 1 / $count; // if numdocs == 1 and !active, don't add. if ($response->numFound == 1 && !$active) { // skip } else { $terms[$term->vid][$active ? $countsort . $term->name : 1 + $countsort . $term->name] = theme('apachesolr_facet_item', $term->name, $count, $path, $active, $unclick_link); } } } $vid = substr($delta, 11); $vocab = taxonomy_vocabulary_load($vid); if (is_numeric($vid) && is_array($terms) && isset($terms[$vid]) && is_array($terms[$vid])) { ksort($terms[$vid]); $facet_display_limit = isset($facet_display_limits[$delta]) ? $facet_display_limits[$delta] : 10; $terms[$vid] = array_slice($terms[$vid], 0, $facet_display_limit); return array('subject' => t('Filter by @name', array('@name' => $vocab->name)), 'content' => theme('apachesolr_facet_list', $terms[$vid])); } else { return; } } switch ($delta) { case 'sort': $sorts = array( 'title' => 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, "solrsort={$type} {$new_sort}", isset($solrsorts[$type]) ? $solrsorts[$type] : ''); } return array('subject' => t('Sort by') . ':', 'content' => theme('apachesolr_sort_list', $sort_links)); case 'type': if (is_array($response->facets->type)) { $contains_active = FALSE; $types = array(); foreach ($response->facets->type as $t => $count) { $unclick_link = ''; unset($active); $type = node_get_types('name', $t); $new_query = clone $query; if ($active = $query->has_field('type', $t)) { $contains_active = TRUE; $new_query->remove_field('type', $t); $path = 'search/'. arg(1) .'/'. $new_query->get_query(); $unclick_link = theme('apachesolr_unclick_link', $path); } else { $new_query->add_field('type', $t); $path = 'search/'. arg(1) .'/'. $new_query->get_query(); } $countsort = $count == 0 ? '' : 1 / $count; // if numdocs == 1 and !active, don't add. if ($response->numFound == 1 && !$active) { // skip } else { $types[$active ? $countsort . $type : 1 + $countsort . $type] = theme('apachesolr_facet_item', $type, $count, $path, $active, $unclick_link); } } if (count($types) > 0) { ksort($types); $facet_display_limit = isset($facet_display_limits[$delta]) ? $facet_display_limits[$delta] : 10; $types = array_slice($types, 0, $facet_display_limit); $output = theme('apachesolr_facet_list', $types); return array('subject' => $type_title, 'content' => $output); } } break; case 'uid': if (is_array($response->facets->uid)) { $contains_active = FALSE; $users = array(); foreach ($response->facets->uid as $uid => $count) { $unclick_link = ''; unset($active); if ($uid == 0) { $name = variable_get('anonymous', t('Anonymous')); } else { $name = db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $uid)); } $new_query = clone $query; if ($active = $query->has_field('uid', $uid)) { $contains_active = TRUE; $new_query->remove_field('uid', $uid); $path = 'search/'. arg(1) .'/'. $new_query->get_query(); $unclick_link = theme('apachesolr_unclick_link', $path); } else { $new_query->add_field('uid', $uid); $path = 'search/'. arg(1) .'/'. $new_query->get_query(); } $countsort = $count == 0 ? '' : 1 / $count; // if numdocs == 1 and !active, don't add. if ($response->numFound == 1 && !$active) { // skip } else { $users[$active ? $countsort . $name : 1 + $countsort . $name] = theme('apachesolr_facet_item', $name, $count, $path, $active, $unclick_link); } } if (is_array($users)) { ksort($users); $facet_display_limit = isset($facet_display_limits[$delta]) ? $facet_display_limits[$delta]: 10; $users = array_slice($users, 0, $facet_display_limit); $output = theme('apachesolr_facet_list', $users); return array('subject' => $author_title, 'content' => $output); } } break; default: if ($fields = apachesolr_cck_fields()) { foreach ($fields as $name => $field) { $index_key = apachesolr_index_key($field); if ($index_key == $delta) { if (is_array($response->facets->$index_key)) { $contains_active = FALSE; foreach ($response->facets->$index_key as $facet => $count) { $unclick_link = ''; unset($active); $new_query = clone $query; if ($active = $query->has_field($index_key, $facet)) { $contains_active = TRUE; $new_query->remove_field($index_key, $facet); $path = 'search/'. arg(1) .'/'. $new_query->get_query(); $unclick_link = theme('apachesolr_unclick_link', $path); } else { $new_query->add_field($index_key, $facet); $path = 'search/'. arg(1) .'/'. $new_query->get_query(); } $countsort = $count == 0 ? '' : 1 / $count; // if numdocs == 1 and !active, don't add. if ($response->numFound == 1 && !$active) { // skip } else { $facets[$active ? $countsort . $facet : 1 + $countsort . $facet] = theme('apachesolr_facet_item', $facet, $count, $path, $active, $unclick_link); } } if (is_array($facets)) { ksort($facets); $facet_display_limit = isset($facet_display_limits[$delta]) ? $facet_display_limits[$delta] : 10; $facets = array_slice($facets, 0, $facet_display_limit); $output = theme('apachesolr_facet_list', $facets); $label = db_result(db_query("SELECT label FROM {node_field_instance} WHERE field_name = '%s'", $name)); return array('subject' => t('Filter by @field', array('@field' => $label)), 'content' => $output); } } } } } } break; } break; case 'configure': if ($delta != 'sort') { $facet_query_limits = variable_get('apachesolr_facet_query_limits', array()); // If the block is not 'sort' (and therefore is a facet block), // display facet limit option. $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; } break; case 'save': if ($delta != 'sort') { // Save query limits $query_limit = intval($edit['apachesolr_facet_query_limit']); $facet_query_limits = variable_get('apachesolr_facet_query_limits', array()); $facet_query_limits[$delta] = $query_limit; variable_set('apachesolr_facet_query_limits', $facet_query_limits); } break; } } function apachesolr_form_block_admin_configure_alter(&$form, $form_state) { if (isset($form['block_settings']) && isset($form['block_settings']['apachesolr_facet_query_limit'])) { $form['#validate'][] = 'apachesolr_facet_query_limit_validate'; } } /* * Validates a facet query limit input. Must be a positive integer or -1. */ function apachesolr_facet_query_limit_validate($form, &$form_state) { $value = intval($form_state['values']['apachesolr_facet_query_limit']); if ($value < 1 && $value != -1) { form_set_error('apachesolr_facet_limit', t('Please enter a number greater than 1 or -1 for the facet query limit.')); return FALSE; } } /** * Implementation of hook_form_alter(). */ function apachesolr_form_alter(&$form, $form_state, $form_id) { $arg = arg(1); $alias = drupal_lookup_path('alias', "search/{$arg}"); // Ok, this really sucks. I want a way to know whether the action of the form // is supposed to be handled by an ApacheSolr module or not. I manually // exclude node and user here, but there are many other hook_search implementations // in the wild and this code will potentially interfere with them. It also // creates a Solr instance wasting resources. if ($arg != 'node' && $arg != 'user' && ( preg_match("&/search/{$arg}&", $form['#action']) || preg_match("&/{$alias}&", $form['#action']) || strpos($form['#action'], $alias))) { if (!isset($_POST['form_id'])) { // Set up our validation function $form['#validate']['apachesolr_search_validate'] = array(); // if no keys, there's nothing to do. if (empty($form['basic']['inline']['keys']['#default_value'])) { return; } // The $query is the true source for search key information if ($query =& apachesolr_drupal_query()) { $form['basic']['inline']['keys']['#default_value'] = $query->get_query_basic(); } } } } /** * Semaphore that indicates whether a search has been done. Blocks use this * later to decide whether they should load or not. * * @param $searched * A boolean indicating whether a search has been executed. * * @return * TRUE if a search has been executed. * FALSE otherwise. */ function apachesolr_has_searched($searched = NULL) { static $_searched = FALSE; if (is_bool($searched)) { $_searched = $searched; } return $_searched; } /** * Factory method for solr singleton object. Structure allows for an arbitrary * number of solr objects to be used based on the host, port, path combination. * Get an instance like this: * $solr =& apachesolr_get_solr(); */ 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]; } /** * It is important to hold on to the Solr response object for the duration of the * page request so that we can use it for things like building facet blocks. */ function &apachesolr_static_response_cache($response = NULL) { static $_response; if (!empty($response)) { $_response = drupal_clone($response); } return $_response; } /* * The query object is built from the keys. If you want to build queries * programmatically you can pass in different keys. If you don't pass in * any keys, search_get_keys() is used instead. */ 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'); } /** * array('index_type' => 'integer', * 'multiple' => TRUE, * 'name' => 'fieldname', * ), */ 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'; // reserve d for date break; case 'boolean': $type_prefix = 'b'; break; case 'date': $type_prefix = 'd'; break; default: $type_prefix = 's'; } $sm = $field['multiple'] ? 'm' : 's'; return $type_prefix . $sm . $field['name']; } /** * This invokes the hook_apachesolr_cck_field_mappings to find out how to handle * CCK fields. */ function apachesolr_cck_fields() { static $_fields; // If CCK isn't enabled, do nothing. 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 {content_node_field_instance} i INNER JOIN {content_node_field} f ON i.field_name = f.field_name;"); while ($row = db_fetch_object($result)) { // Only deal with fields that have options widgets (facets don't make sense otherwise), or fields that have specific mappings. 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 FALSE; } } /** * Implementation of hook_theme(). */ function apachesolr_theme() { return array( 'apachesolr_facet_item' => array( 'arguments' => array('name' => NULL, 'count' => NULL, 'path' => NULL, 'active' => FALSE, 'unclick_link' => NULL), ), 'apachesolr_unclick_link' => array( 'arguments' => array('url' => NULL), ), 'apachesolr_facet_list' => array( 'arguments' => array('items' => NULL), ), 'apachesolr_sort_list' => array( 'arguments' => array('items' => NULL), ), 'apachesolr_sort_link' => array( 'arguments' => array('text' => NULL, 'path' => NULL, 'query' => NULL, 'direction' => NULL), ), 'apachesolr_breadcrumb_type' => array( 'arguments' => array('type' => NULL), ), 'content_apachesolr_breadcrumb_uid' => array( 'arguments' => array('uid' => NULL), ), 'apachesolr_breadcrumb_tid' => array( 'arguments' => array('tid' => NULL), ), ); } function theme_apachesolr_facet_item($name, $count, $path, $active = FALSE, $unclick_link = NULL) { $attributes = array(); if ($active) { $attributes['class'] = 'active'; } if ($unclick_link) { return $unclick_link . " $name"; } else { return l($name . " ($count)", $path, array('attributes' => $attributes)); } } function theme_apachesolr_unclick_link($path) { return l("(-)", $path); } function theme_apachesolr_sort_link($text, $path, $query, $direction = NULL) { $icon = ''; if ($direction) { $icon = theme('tablesort_indicator', $direction); } return $icon . ' ' . l($text, $path, array('query' => $query)); } function theme_apachesolr_facet_list($items) { return theme('item_list', $items); } function theme_apachesolr_sort_list($items) { return theme('item_list', $items); } /** * Return the human readable text for a content type. */ function theme_apachesolr_breadcrumb_type($type) { return node_get_types('name', $type); } /** * Return the username from $uid */ function theme_apachesolr_breadcrumb_uid($uid) { $user = user_load(array('uid' => $uid)); return $user->name; } /** * Return the term name from $tid. */ function theme_apachesolr_breadcrumb_tid($tid) { $term = taxonomy_get_term($tid); return $term->name; }