? SolrPhpClient Index: Solr_Base_Query.php =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/Solr_Base_Query.php,v retrieving revision 1.1.4.40.2.10 diff -u -p -r1.1.4.40.2.10 Solr_Base_Query.php --- Solr_Base_Query.php 26 Dec 2009 13:08:51 -0000 1.1.4.40.2.10 +++ Solr_Base_Query.php 20 Feb 2010 10:50:10 -0000 @@ -409,7 +409,6 @@ class Solr_Base_Query implements Drupal_ // Gets information about the fields already in solr index. $index_fields = $this->solr->getFields(); - foreach ((array) $index_fields as $name => $data) { // Look for a field alias. $alias = isset($this->field_map[$name]) ? $this->field_map[$name] : $name; Index: apachesolr.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.admin.inc,v retrieving revision 1.1.2.28.2.23 diff -u -p -r1.1.2.28.2.23 apachesolr.admin.inc --- apachesolr.admin.inc 27 Dec 2009 17:29:34 -0000 1.1.2.28.2.23 +++ apachesolr.admin.inc 20 Feb 2010 10:50:10 -0000 @@ -249,6 +249,9 @@ function apachesolr_enabled_facets_form_ foreach ($form_state['values']['apachesolr_enabled_facets'] as $module => $facets) { $enabled[$module] = array_filter($facets); } + // This cache being stale can prevent new facet filters from working. + $solr = apachesolr_get_solr(); + $solr->clearCache(); variable_set('apachesolr_enabled_facets', $enabled); drupal_set_message($form_state['values']['submit_message'], 'warning'); } @@ -265,9 +268,12 @@ function apachesolr_enabled_facets_form( $module_facets = array(); $module_list = array(); foreach (module_implements('apachesolr_facets') as $module) { - $module_facets[$module] = module_invoke($module, 'apachesolr_facets'); - uasort($module_facets[$module], 'apachesolr_sort_facets'); - $module_list[$module] = $module; + $return = module_invoke($module, 'apachesolr_facets'); + if (!empty($return)) { + $module_facets[$module] = $return; + uasort($module_facets[$module], 'apachesolr_sort_facets'); + $module_list[$module] = $module; + } } $enabled_facets = apachesolr_get_enabled_facets(); @@ -284,6 +290,7 @@ function apachesolr_enabled_facets_form( } } foreach($module_facets as $module => $facets) { + $form['apachesolr_enabled_facets'][$module] = array( '#type' => 'fieldset', '#title' => check_plain($module_list[$module]['name']), @@ -381,8 +388,8 @@ function apachesolr_clear_index_submit($ $form_state['redirect'] = 'admin/settings/apachesolr/index/batch/confirm'; } else { - $form_state['redirect'] = 'admin/settings/apachesolr/index/clear/confirm'; -} + $form_state['redirect'] = 'admin/settings/apachesolr/index/clear/confirm'; + } } /** Index: apachesolr.index.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.index.inc,v retrieving revision 1.1.2.6.2.18 diff -u -p -r1.1.2.6.2.18 apachesolr.index.inc --- apachesolr.index.inc 2 Jan 2010 23:44:04 -0000 1.1.2.6.2.18 +++ apachesolr.index.inc 20 Feb 2010 10:50:10 -0000 @@ -110,43 +110,55 @@ function apachesolr_node_to_document($no // Get CCK fields list $cck_fields = apachesolr_cck_fields(); + // NOTE: One CCK field might result in multiple Solr fields. foreach ($cck_fields as $key => $cck_info) { if (isset($node->$key)) { // Got a CCK field. See if it is to be indexed. $function = $cck_info['indexing_callback']; + $fields = NULL; if ($cck_info['indexing_callback'] && function_exists($function)) { - $field = $function($node, $key); + // NOTE: This function should always return an array. + $fields = $function($node, $key, $cck_info); } - else { - $field = $node->$key; - } - $index_key = apachesolr_index_key($cck_info); - if (is_array($field)) { - foreach ($field as $value) { + // NOTE: By commenting this I'm making it so there is no default $fields. + // Only when there is an indexing_callback in the cck_info will + // this CCK field get indexed. + //else { + // $fields = $node->$key; + //} + // NOTE: This is now being done in the mandatory indexing callback. + // $index_key = apachesolr_index_key($cck_info); + if (is_array($fields)) { + // NOTE: If this were done as $key => $value, and the $index_key were + // done within the loop, could we support indexing one CCK field to + // multiple Solr fields? + foreach ($fields as $field) { // Don't index NULLs or empty strings // We can use 'value' rather than 'safe' since we strip tags and later check_plain(). // Furthermore, what is being indexed is the KEY for the CCK value. It will need // a trip through content_format() later to display the value. - switch ($cck_info['field_type']) { - case 'nodereference': - $index_value = (isset($value['nid']) && strlen($value['nid'])) ? $value['nid'] : FALSE; - break; - - case 'userreference': - $index_value = (isset($value['uid']) && strlen($value['uid'])) ? $value['uid'] : FALSE; - break; - - default: - $index_value = (isset($value['value']) && strlen($value['value'])) ? $value['value'] : FALSE; - break; + //switch ($cck_info['field_type']) { + // // NOTE: It seems like it would be cleaner to give these all + // // indexing callbacks and then let the callback generate the + // // $index_value. + // case 'nodereference': + // $index_value = (isset($value['nid']) && strlen($value['nid'])) ? $value['nid'] : FALSE; + // break; + // + // case 'userreference': + // $index_value = (isset($value['uid']) && strlen($value['uid'])) ? $value['uid'] : FALSE; + // break; + // + // default: + // $index_value = (isset($value['value']) && strlen($value['value'])) ? $value['value'] : FALSE; + // break; + //} + + if ($cck_info['multiple']) { + $document->setMultiValue($field['key'], apachesolr_clean_text($field['value'])); } - if ($index_value) { - if ($cck_info['multiple']) { - $document->setMultiValue($index_key, apachesolr_clean_text($index_value)); - } - else { - $document->$index_key = apachesolr_clean_text($index_value); - } + else { + $document->{$field['key']} = apachesolr_clean_text($field['value']); } } } @@ -169,6 +181,38 @@ function apachesolr_node_to_document($no return $document; } +function apachesolr_cck_nodereference_indexing_callback($node, $field_name, $cck_info) { + $fields = array(); + if (isset($node->{$field_name})) { + $index_key = apachesolr_index_key($cck_info); + foreach ($node->$field_name as $field) { + if ($index_value = (isset($field['nid']) && strlen($field['nid'])) ? $field['nid'] : FALSE) { + $fields[] = array( + 'key' => $index_key, + 'value' => $index_value, + ); + } + } + } + return $fields; +} + +function apachesolr_cck_userreference_indexing_callback($node, $field_name, $cck_info) { + $fields = array(); + if (isset($node->$field_name)) { + $index_key = apachesolr_index_key($cck_info); + foreach ($node->{$field_name} as $field) { + if ($index_value = (isset($field['uid']) && strlen($field['uid'])) ? $field['uid'] : FALSE) { + $fields[] = array( + 'key' => $index_key, + 'value' => $index_value, + ); + } + } + } + return $fields; +} + /** * Extract taxonomy from $node and add to dynamic fields. */ @@ -251,7 +295,7 @@ function apachesolr_add_tags_to_document /** * Additional index utility functions */ - + /** * hook_cron() helper to try to make {apachesolr_search_node} consistent with {node}. */ @@ -303,7 +347,7 @@ function apachesolr_cron_check_node_tabl } } } - + function apachesolr_nodeapi_mass_update($nodes) { if (empty($nodes)) { return TRUE; @@ -330,7 +374,7 @@ function apachesolr_nodeapi_mass_update( return FALSE; } } - + function apachesolr_nodeapi_mass_delete($nodes) { if (empty($nodes)) { return TRUE; @@ -354,4 +398,3 @@ function apachesolr_nodeapi_mass_delete( return FALSE; } } - Index: apachesolr.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.module,v retrieving revision 1.1.2.12.2.155.2.57 diff -u -p -r1.1.2.12.2.155.2.57 apachesolr.module --- apachesolr.module 2 Jan 2010 14:42:58 -0000 1.1.2.12.2.155.2.57 +++ apachesolr.module 20 Feb 2010 10:50:10 -0000 @@ -280,6 +280,7 @@ function apachesolr_rebuild_index_table( db_query("UPDATE {apachesolr_search_node} SET changed = %d WHERE changed > %d", $time, $time); apachesolr_clear_last_index(); } + cache_clear_all('*', 'cache_apachesolr', TRUE); } function apachesolr_exclude_types($namespace) { @@ -744,10 +745,12 @@ function apachesolr_get_facet_definition $operator_settings = variable_get('apachesolr_operator', array()); foreach (module_implements('apachesolr_facets') as $module) { $facets = module_invoke($module, 'apachesolr_facets'); - foreach ($facets as $delta => $info) { - $definitions[$module][$delta] = $info; - if(isset($definitions[$module][$delta])) { - $definitions[$module][$delta]['operator'] = isset($operator_settings[$module][$delta]) ? $operator_settings[$module][$delta] : 'AND'; + if (!empty($facets)) { + foreach ($facets as $delta => $info) { + $definitions[$module][$delta] = $info; + if(isset($definitions[$module][$delta])) { + $definitions[$module][$delta]['operator'] = isset($operator_settings[$module][$delta]) ? $operator_settings[$module][$delta] : 'AND'; + } } } } @@ -756,6 +759,24 @@ function apachesolr_get_facet_definition } /** + * Returns a member of the facet definitions array if it contains + * $field as the 'field_name' element. + * + * @param string $field + * The field_name being sought. + */ +function apachesolr_get_facet_definition_by_field($field) { + $definitions = apachesolr_get_facet_definitions(); + foreach ($definitions as $module => $facets) { + foreach ($facets as $key => $values) { + if ($facet = array_search($field, $values)) { + return $definitions[$module][$key]; + } + } + } +} + +/** * Implementation of hook_form_[form_id]_alter(). * * Hide the core 'title' field in favor of our 'name' field. @@ -1145,7 +1166,7 @@ function apachesolr_facetcount_form($mod '#description' => t('The maximum number of filter links to show in this block.'), '#default_value' => isset($limits[$module][$delta]) ? $limits[$module][$delta] : variable_get('apachesolr_facet_query_limit_default', 20), ); - + // TODO: Generalize how we know what type a facet block is by putting field // type into the facet definition. 'created' and 'changed' are date blocks. if ($delta != 'created' && $delta != 'changed') { @@ -1406,7 +1427,7 @@ function apachesolr_static_response_cach function apachesolr_drupal_query($keys = '', $filters = '', $solrsort = '', $base_path = '', $solr = NULL) { list($module, $class) = variable_get('apachesolr_query_class', array('apachesolr', 'Solr_Base_Query')); include_once drupal_get_path('module', $module) .'/'. $class .'.php'; - + if (empty($solr)) { $solr = apachesolr_get_solr(); } @@ -1546,14 +1567,17 @@ function apachesolr_cck_fields() { $mappings['nodereference'] = array( 'nodereference_buttons' => array( 'display_callback' => 'apachesolr_cck_nodereference_field_callback', + 'indexing_callback' => 'apachesolr_cck_nodereference_indexing_callback', 'index_type' => 'integer', ), 'nodereference_select' => array( 'display_callback' => 'apachesolr_cck_nodereference_field_callback', + 'indexing_callback' => 'apachesolr_cck_nodereference_indexing_callback', 'index_type' => 'integer', ), 'nodereference_autocomplete' => array( 'display_callback' => 'apachesolr_cck_nodereference_field_callback', + 'indexing_callback' => 'apachesolr_cck_nodereference_indexing_callback', 'index_type' => 'integer', ), ); @@ -1564,10 +1588,12 @@ function apachesolr_cck_fields() { ), 'userreference_select' => array( 'display_callback' => 'apachesolr_cck_userreference_field_callback', + 'indexing_callback' => 'apachesolr_cck_userreference_indexing_callback', 'index_type' => 'integer', ), 'userreference_autocomplete' => array( 'display_callback' => 'apachesolr_cck_userreference_field_callback', + 'indexing_callback' => 'apachesolr_cck_userreference_indexing_callback', 'index_type' => 'integer', ), ); @@ -1579,20 +1605,21 @@ function apachesolr_cck_fields() { // Only deal with fields that have option widgets (facets don't make sense otherwise), or fields that have specific mappings. if ((isset($mappings[$row->field_type][$row->widget_type]) || isset($mappings['per-field'][$row->field_name]))) { if (isset($mappings['per-field'][$row->field_name])) { - $fields[$row->field_name] = $mappings['per-field'][$row->field_name]; - $row->index_type = $mappings['per-field'][$row->field_name]['index_type']; - $row->indexing_callback = $mappings['per-field'][$row->field_name]['indexing_callback']; - $row->display_callback = $mappings['per-field'][$row->field_name]['display_callback']; + $field = $mappings['per-field'][$row->field_name]; + $fields[$row->field_name] = $field; } else { - $row->index_type = $mappings[$row->field_type][$row->widget_type]['index_type']; - $row->indexing_callback = $mappings[$row->field_type][$row->widget_type]['indexing_callback']; - $row->display_callback = $mappings[$row->field_type][$row->widget_type]['display_callback']; + $field = $mappings[$row->field_type][$row->widget_type]; } - $row->multiple = (bool) $row->multiple; // It's important that we put the 'cck_' here because several points in the later processing // depend on it to route program flow to cck specific handlers. - $row->name = 'cck_' . $row->field_name; + $row->name = 'cck_' . $row->field_name; + $row->index_type = $field['index_type']; + $row->indexing_callback = $field['indexing_callback']; + $row->display_callback = $field['display_callback']; + $row->facet_block_callback = $field['facet_block_callback']; + $row->multiple = (bool) $row->multiple; + $fields[$row->field_name] = array_merge((array) $fields[$row->field_name], (array) $row); $fields[$row->field_name]['content_types'][] = $row->content_type; unset($fields[$row->field_name]['content_type']); @@ -1605,6 +1632,15 @@ function apachesolr_cck_fields() { } /** + * Implementation of hook_content_fieldapi + */ +function apachesolr_content_fieldapi($op) { + if ($op == 'update instance') { + cache_clear_all('*', 'cache_apachesolr', TRUE); + } +} + +/** * Use the content.module's content_format() to format the * field based on its value ($facet). * @@ -1739,7 +1775,7 @@ function apachesolr_mlt_suggestions($set } } $query = apachesolr_drupal_query('id:' . $id); - + $type_filters = array(); if (is_array($settings['mlt_type_filters'])) { foreach ($settings['mlt_type_filters'] as $type_filter) { Index: apachesolr_search.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr_search.module,v retrieving revision 1.1.2.6.2.111.2.45 diff -u -p -r1.1.2.6.2.111.2.45 apachesolr_search.module --- apachesolr_search.module 4 Jan 2010 14:27:56 -0000 1.1.2.6.2.111.2.45 +++ apachesolr_search.module 20 Feb 2010 10:50:11 -0000 @@ -1,5 +1,5 @@ facet_counts->facet_fields->$facet_field) > 0) { + if (count((array)$response->facet_counts->facet_fields->$facet_field) > 0 || + count((array)$response->facet_counts->facet_dates->$facet_field) > 0) { // This bit is modeled on block.modul, block_list(). $result = db_query(db_rewrite_sql("SELECT DISTINCT b.* FROM {blocks} b LEFT JOIN {blocks_roles} r ON b.module = r.module AND b.delta = r.delta @@ -385,6 +386,7 @@ function apachesolr_search_execute($keys return apachesolr_search_process_response($response, $query, $params); } +// NOTE: Why does this take the $query object? function apachesolr_search_basic_params($query) { $params = array( 'fl' => 'id,nid,title,comment_count,type,created,changed,score,path,url,uid,name', @@ -436,7 +438,17 @@ function apachesolr_search_add_facet_par if (apachesolr_block_visibility($query, $module, $delta)) { // TODO: generalize handling of date and range facets. // TODO: put field type in the facet definitions. - if ($module == 'apachesolr_search' && ($facet_field == 'created' || $facet_field == 'changed')) { + // NOTE: This is a dependency on the apachesolr_date module. Using function_exists to avoid problems. + if (strpos($facet_field, 'cck_field_date') && function_exists('apachesolr_date_search_date_range')) { + list($start, $end, $gap) = apachesolr_date_search_date_range($query, $facet_field); + if ($gap) { + $params['facet.date'][] = $facet_field; + $params['f.'. $facet_field .'.facet.date.start'] = $start; + $params['f.'. $facet_field .'.facet.date.end'] = $end; + $params['f.'. $facet_field .'.facet.date.gap'] = $gap; + } + } + else if ($module == 'apachesolr_search' && ($facet_field == 'created' || $facet_field == 'changed')) { list($start, $end, $gap) = apachesolr_search_date_range($query, $facet_field); if ($gap) { $params['facet.date'][] = $facet_field; @@ -698,10 +710,11 @@ function apachesolr_search_apachesolr_fa foreach ($fields as $name => $field) { // $delta can only be 32 chars, and the CCK field name may be this // long also, so we cannot add anything to it. - $facets[$field['field_name']] = array( - 'info' => t('CCK @field_type field: Filter by @field', array('@field_type' => $field['field_type'], '@field' => $field['label'])), - 'facet_field' => apachesolr_index_key($field), - 'content_types' => $field['content_types'], + $facets[$field['field_name']] = array_merge($field, array( + 'info' => t('CCK @field_type field: Filter by @field', array('@field_type' => $field['field_type'], '@field' => $field['label'])), + 'facet_field' => apachesolr_index_key($field), + 'content_types' => $field['content_types'], + ) ); } } @@ -712,7 +725,6 @@ function apachesolr_search_apachesolr_fa * Implementation of hook_block(). */ function apachesolr_search_block($op = 'list', $delta = 0, $edit = array()) { - switch ($op) { case 'list': $enabled_facets = apachesolr_get_enabled_facets('apachesolr_search'); @@ -770,12 +782,16 @@ function apachesolr_search_block($op = ' return apachesolr_date_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by post date')); default: + if ($fields = apachesolr_cck_fields()) { + foreach ($fields as $name => $field) { + if ($field['field_name'] == $delta) { $index_key = apachesolr_index_key($field); $callback = isset($field['display_callback']) ? $field['display_callback'] : FALSE; - return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $index_key, t('Filter by @field', array('@field' => $field['label'])), $callback); + $block_function = (isset($field['facet_block_callback']) && function_exists($field['facet_block_callback'])) ? $field['facet_block_callback'] : 'apachesolr_facet_block'; + return $block_function($response, $query, 'apachesolr_search', $delta, $index_key, t('Filter by @field', array('@field' => $field['label'])), $callback); } } } @@ -889,10 +905,6 @@ function apachesolr_search_taxonomy_face $items = apachesolr_search_nested_facet_items($query, $facets, $response->response->numFound); // Process all terms into an item list if ($items && ($response->response->numFound > 1 || $contains_active)) { - // Get information needed by the taxonomy blocks about limits. - $initial_limits = variable_get('apachesolr_facet_query_initial_limits', array()); - $limit_default = variable_get('apachesolr_facet_query_initial_limit_default', 10); - $limit = isset($initial_limits['apachesolr_search'][$delta]) ? $initial_limits['apachesolr_search'][$delta] : $limit_default; return array( 'subject' => t('Filter by @name', array('@name' => $vocab->name)), @@ -946,10 +958,18 @@ function apachesolr_search_language_name * the facets will be returned in the order they were received. */ function apachesolr_search_nested_facet_items($query, $facets, $num_found, $sort = TRUE) { - $items = array(); foreach($facets as $field) { - $facet_text = theme('apachesolr_breadcrumb_' . $field['#name'], $field, $field['#exclude']); + $facet_definition = apachesolr_get_facet_definition_by_field($field['#name']); + if (isset($facet_definition['display_callback'])) { + $function = $facet_definition['display_callback']; + if (function_exists($function)) { + $facet_text = $function($field['#value'], $field); + } + } + if (!$facet_text) { + $facet_text = theme('apachesolr_breadcrumb_' . $field['#name'], $field, $field['#exclude']); + } if (!$facet_text) { $facet_text = $field['#value']; } @@ -1297,15 +1317,30 @@ function apachesolr_search_apachesolr_th } } +/** + * Theme function for CCK fields in breadcrumbs. + * TODO: The logic for getting here is too convoluted, and + * there are too many bizarre naming conventions in play (_cck_, _end). + * The checks for _cck_ and _end MUST get refactored. + */ function theme_apachesolr_breadcrumb_cck($field) { $matches = preg_split('/_cck_/', $field['#name']); if (isset($matches[1])) { + $match = $matches[1]; + + // TODO: If the apachesolr_date module is present we might + // have fields with the suffix '_end'. These are the end-dates + // and need the suffix removed. + if (module_exists('apachesolr_date')) { + $match = preg_replace('/_end{1}$/', '', $match); + } + $mappings = apachesolr_cck_fields(); - if (isset($mappings[$matches[1]]['display_callback'])) { - $function = $mappings[$matches[1]]['display_callback']; + if (isset($mappings[$match]['display_callback'])) { + $function = $mappings[$match]['display_callback']; if (function_exists($function)) { $facet = $field['#value']; - $options = array('delta' => $matches[1]); + $options = array_merge($mappings[$match], array('delta' => $matches[1])); return $function($facet, $options); } } @@ -1416,7 +1451,6 @@ function apacehsolr_get_parent_terms($ti function apachesolr_search_currentsearch_block($response, $query) { $fields = $query->get_filters(); $links = array(); - // If current search has keys, offer current search without them if ($keys = $query->get_query_basic()) { $links[] = theme('apachesolr_unclick_link', $keys, $query->get_path(''), array('query' => $query->get_url_queryvalues())); Index: contrib/apachesolr_commentsearch/apachesolr_commentsearch.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/contrib/apachesolr_commentsearch/Attic/apachesolr_commentsearch.module,v retrieving revision 1.1.2.8 diff -u -p -r1.1.2.8 apachesolr_commentsearch.module --- contrib/apachesolr_commentsearch/apachesolr_commentsearch.module 4 Jan 2010 13:38:53 -0000 1.1.2.8 +++ contrib/apachesolr_commentsearch/apachesolr_commentsearch.module 20 Feb 2010 10:50:11 -0000 @@ -94,7 +94,14 @@ function apachesolr_commentsearch_apache } function apachesolr_commentsearch_apachesolr_theme_breadcrumb_alter(&$breadcrumb_name) { - $breadcrumb_name = 'apachesolr_commentsearch_breadcrumb_type'; + // While the goal here is to hijack nearly every breadcrumb generation, we + // can't do it if it's a ckk facet. That would step on the toes of + // date facets, etc. So reverse the logic from apachesolr_search_apachesolr_theme_breadcrumb_alter + // and only alter non-cck breadcrumbs. + $matches = preg_split('/_cck_/', $fieldname); + if (!empty($matches[1])) { + $breadcrumb_name = 'apachesolr_commentsearch_breadcrumb_type'; + } } function apachesolr_commentsearch_comment(&$a1, $op) { Index: contrib/apachesolr_date/apachesolr_date.info =================================================================== RCS file: contrib/apachesolr_date/apachesolr_date.info diff -N contrib/apachesolr_date/apachesolr_date.info --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ contrib/apachesolr_date/apachesolr_date.info 20 Feb 2010 10:50:11 -0000 @@ -0,0 +1,9 @@ +; $Id: apachesolr.info,v 1.1.2.1.2.8 2009/06/04 13:33:36 robertDouglass Exp $ +name = Apache Solr Date +description = Index and facet on CCK date fields. +dependencies[] = apachesolr +dependencies[] = apachesolr_search +dependencies[] = date +package = Apache Solr +core = "6.x" +php = 5.1.4 Index: contrib/apachesolr_date/apachesolr_date.module =================================================================== RCS file: contrib/apachesolr_date/apachesolr_date.module diff -N contrib/apachesolr_date/apachesolr_date.module --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ contrib/apachesolr_date/apachesolr_date.module 20 Feb 2010 10:50:11 -0000 @@ -0,0 +1,427 @@ + $field) { + if (in_array($field['field_type'], array('date', 'datetime', 'datestamp'))) { + $cck_info = content_fields($field['field_name']); + // Only fields with a value2 have end dates to facet upon. + if (isset($cck_info['columns']['value2'])) { + // $delta can only be 32 chars, and the CCK field name may be this + // long also, so we cannot add anything to it. + $facets[$field['field_name'] . '_end'] = array_merge($field, array( + 'info' => t('CCK @field_type field: Filter by @field end', array('@field_type' => $field['field_type'], '@field' => $field['label'])), + // 'facet_field' will later be block deltas. + 'facet_field' => apachesolr_index_key($field) . '_end', + 'content_types' => $field['content_types'], + ) + ); + } + } + } + } + return $facets; +} + +/** + * Implementation of hook_block. + */ +function apachesolr_date_block($op = 'list', $delta = 0, $edit = array()) { + switch ($op) { + case 'list': + $enabled_facets = apachesolr_get_enabled_facets('apachesolr_date'); + $facets = apachesolr_date_apachesolr_facets(); + // Add the blocks + $blocks = array(); + foreach ($enabled_facets as $delta => $facet_field) { + if (isset($facets[$delta])) { + $blocks[$delta] = $facets[$delta] + array('cache' => BLOCK_CACHE_PER_PAGE,); + // TODO: This is ugly. The only consolation is that the admin can + // override block titles anyway. + $blocks[$delta]['label'] = $blocks[$delta]['label'] . ' (end)'; + } + } + 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_current_query(); + + $facets = apachesolr_get_enabled_facets('apachesolr_date'); + if (empty($facets[$delta])) { + return; + } + + if (!apachesolr_block_visibility($query, 'apachesolr_date', $delta)) { + return; + } + // $delta comes to us with _end on the end, which we snip off right here. + $delta = substr($delta, 0, strlen($delta) - 4); + if ($fields = apachesolr_cck_fields()) { + if ($field = $fields[$delta]) { + $index_key = apachesolr_index_key($field) . '_end'; + $callback = isset($field['display_callback']) ? $field['display_callback'] : FALSE; + $block_function = (isset($field['facet_block_callback']) && function_exists($field['facet_block_callback'])) ? $field['facet_block_callback'] : 'apachesolr_facet_block'; + return $block_function($response, $query, 'apachesolr_search', $delta, $index_key, t('Filter by @field end', array('@field' => $field['label'])), $callback); + } + } + } + break; + + case 'configure': + return apachesolr_facetcount_form('apachesolr_date', $delta); + + case 'save': + apachesolr_facetcount_save($edit); + break; + } +} + +/** + * Implementation of hook_apachesolr_prepare_query. + * Adds sorts for enabled date facets to the sorting block. + */ +function apachesolr_date_apachesolr_prepare_query(&$query) { + // Because we get the enabled facets just from apachesolr_search we're + // limiting sorting to start dates. + $enabled_facets = apachesolr_get_enabled_facets('apachesolr_search'); + $facet_definitions = apachesolr_date_apachesolr_facets(); + $facet_definitions += apachesolr_search_apachesolr_facets(); + foreach ($enabled_facets as $key => $value) { + if (strpos($value, 'tds_cck_field_date') !== FALSE) { + $cck_field = content_fields($facet_definitions[$key]['field_name']); + $query->set_available_sort($value, array( + 'title' => $cck_field['widget']['label'], // how the sort is to appear in the sorts block + 'default' => 'desc', + )); + } + } +} + +/** + * Implementation of hook_apachesolr_cck_fields_alter. + * This function adds the CCK date fields' definitions to let + * them be recognized as facets. + */ +function apachesolr_date_apachesolr_cck_fields_alter(&$mappings) { + $defaults = array( + 'indexing_callback' => 'apachesolr_date_date_field_indexing_callback', + // Trie-Range date types. + 'index_type' => 'tdate', + 'facet_block_callback' => 'apachesolr_date_date_facet_block', + 'display_callback' => 'apachesolr_date_display_callback', + ); + // NOTE: The structure of this array essentially blocks us from having + // multiple mappings per CCK field. For that we'd need a structure like + // $mappings['date']['date_select'][] = $defaults; + $mappings['date']['date_select'] = $defaults; + $mappings['date']['date_text'] = $defaults; + $mappings['datetime']['date_select'] = $defaults; + $mappings['datetime']['date_text'] = $defaults; + $mappings['datestamp']['date_select'] = $defaults; + $mappings['datestamp']['date_text'] = $defaults; + $mappings['datestamp']['date_select']['indexing_callback'] = 'apachesolr_date_datestamp_field_indexing_callback'; + $mappings['datestamp']['date_text']['indexing_callback'] = 'apachesolr_date_datestamp_field_indexing_callback'; +} + +/** + * This function is used during indexing to normalize the DATE and DATETIME + * fields into the appropriate format for Apache Solr. + */ +function apachesolr_date_date_field_indexing_callback($node, $field_name, $cck_info) { + $fields = array(); + if (isset($node->$field_name)) { + $index_key = apachesolr_index_key($cck_info); + foreach ($node->$field_name as $field) { + // Construct a Solr-ready date string in UTC time zone based on the field's date string and time zone. + $tz = new DateTimeZone(isset($field['timezone']) ? $field['timezone'] : 'UTC'); + + // $fields may end up having two values; one for the start date + // and one for the end date. + if (isset($field['value'])) { + if ($date = date_create($field['value'], $tz)) { + $index_value = apachesolr_date_iso($date->format('U')); + $fields[] = array( + 'key' => $index_key, + 'value' => $index_value, + ); + } + } + if (isset($field['value2'])) { + if ($date = date_create($field['value2'], $tz)) { + $index_value = apachesolr_date_iso($date->format('U')); + $fields[] = array( + // The value2 element is the end date. Therefore it gets indexed + // into its own Solr field. + 'key' => $index_key . '_end', + 'value' => $index_value, + ); + } + } + } + } + return $fields; +} + +/** + * This function is used during indexing to normalize the DATESTAMP fields + * into the appropriate format for Apache Solr. + */ +function apachesolr_date_datestamp_field_indexing_callback($node, $field_name, $cck_info) { + $fields = array(); + if (isset($node->$field_name)) { + $index_key = apachesolr_index_key($cck_info); + + // $fields may end up having two values; one for the start date + // and one for the end date. + foreach ($node->$field_name as $field) { + if (isset($field['value']) && $field['value'] != 0) { + $index_value = apachesolr_date_iso($field['value']); + $fields[] = array( + 'key' => $index_key, + 'value' => $index_value, + ); + } + if (isset($field['value2']) && $field['value'] != 0) { + $index_value = apachesolr_date_iso($field['value2']); + $fields[] = array( + // The value2 element is the end date. Therefore it gets indexed + // into its own Solr field. + 'key' => $index_key . '_end', + 'value' => $index_value, + ); + } + } + } + return $fields; +} + +/** + * When faceting and filtering we need to infer ranges of dates. + * This function looks at a query and a facet field and returns a + * date range for use in querying. + * + * @param Drupal_Solr_Query_Interface $query + * Current query object. + * @param $facet_field + * The field for which a range must be generated. + * + * @return array $gap + * The array contains a start, end, and gap element. + * + * Example return value: + * array( + * 0 => '2007-01-05T13:05:00Z/YEAR', + * 1 => '2013-11-17T15:04:00Z+1YEAR/YEAR', + * 2 => '+1YEAR', + * ); + * + */ +function apachesolr_date_search_date_range($query, $facet_field) { + foreach ($query->get_filters($facet_field) as $filter) { + // If we had an ISO date library we could use ISO dates + // directly. Instead, we convert to Unix timestamps for comparison. + // Only use dates if we are able to parse into timestamps. + $start = strtotime($filter['#start']); + $end = strtotime($filter['#end']); + if ($start && $end && ($start < $end)) { + $start_iso = $filter['#start']; + $end_iso = $filter['#end']; + // Determine the drilldown gap for this range. + $gap = apachesolr_date_gap_drilldown(apachesolr_date_find_query_gap($start_iso, $end_iso)); + } + } + + // If there is no $delta field in the query object, get initial + // facet.date.* params from the DB and determine the best search + // gap to use. + if (!isset($start_iso)) { + // NOTE: Finding the field namd and loading the field info is a hacky + // bit of string manipulation. We look once with what comes in ($field_name), + // and if that doesn't find any CCK field definition for us, we hack off + // the last four characters (presumed to be '_end' for an ending date), + // and try again. If that doesn't find anything we go home. + + $field_name = substr($facet_field, 8); + $field = content_fields($field_name); + $db_info = content_database_info($field); + $column = $db_info['columns']['value']['column']; + // This check is in place for the cases where the field name has + // _end appended to the end, and signifies that it is and end date. + if (!$field) { + $field_name = substr($field_name, 0, strlen($field_name) - 4); + $field = content_fields($field_name); + $db_info = content_database_info($field); + $column = $db_info['columns']['value2']['column']; + } + + // By this point we should have the following: + // $field_name, a cck field name + // $field, a cck field definition + // $db_info, information from content.module about retrieving db data + // $column, the column name in the db table for this field + if (!$field) { + return; + } + + $tz = new DateTimeZone(isset($field['timezone_db']) ? $field['timezone_db'] : 'UTC'); + $table = $db_info['table']; + $start_value = db_result(db_query("SELECT MIN(cck.$column) FROM {{$table}} cck INNER JOIN {node} n WHERE n.status = 1")); + if (is_numeric($start_value)) { + $start_iso = apachesolr_date_iso($start_value); + } + else if ($date = date_create($start_value, $tz)) { + $start_iso = apachesolr_date_iso($date->format('U')); + } + + $end_value = db_result(db_query("SELECT MAX(cck.$column) FROM {{$table}} cck INNER JOIN {node} n WHERE n.status = 1")); + if (is_numeric($end_value)) { + $end_iso = apachesolr_date_iso($end_value); + } + else if ($date = date_create($end_value, $tz)) { + $end_iso = apachesolr_date_iso($date->format('U')); + } + + if (isset($start_iso) && isset($end_iso)) { + $gap = apachesolr_date_determine_gap($start_iso, $end_iso); + } + else { + // TODO: Find the gap. + $end_iso = $start_iso; + $gap = "YEAR"; + } + } + + // Return a query range from the beginning of a gap period to the beginning + // of the next gap period. We ALWAYS generate query ranges of this form + // and the apachesolr_date_*() helper functions require it. + return array("$start_iso/$gap", "$end_iso+1$gap/$gap", "+1$gap"); +} + +/** + * Helper function for displaying a date facet blocks. + */ +function apachesolr_date_date_facet_block($response, $query, $module, $delta, $facet_field, $filter_by, $facet_callback = FALSE) { + // The items in the facet block (facet links and unclick links). + $items = array(); + // The array that is ultimately sent into apachesolr_l(), @see apachesolr_l + $options = array(); + // The display text for any given facet or unclick link. + $facet_text = ''; + + // Clone the query as we either add or remove a filter before generating + // the link. + $new_query = clone $query; + // Set the default gap. + $gap = 'YEAR'; + foreach (array_reverse($new_query->get_filters($facet_field)) as $filter) { + $new_query->remove_filter($facet_field, $filter['#value']); + $gap = apachesolr_date_find_query_gap($filter['#start'], $filter['#end']); + $facet_text = apachesolr_date_format_iso_by_gap($gap, $filter['#start']); + $options['gap'] = $gap; + $options['query'] = $new_query->get_url_queryvalues(); + array_unshift($items, theme('apachesolr_unclick_link', $facet_text, $new_query->get_path(), $options)); + } + + // Add links for additional date filters. + if (!empty($response->facet_counts->facet_dates->$facet_field)) { + $field = clone $response->facet_counts->facet_dates->$facet_field; + // A field will have this type of structure, where the main information + // is in the form DATE => COUNT: + // stdClass Object + // ( + // [2007-01-01T00:00:00Z] => 5 + // [2008-01-01T00:00:00Z] => 4 + // [2009-01-01T00:00:00Z] => 2 + // [2010-01-01T00:00:00Z] => 6 + // [2011-01-01T00:00:00Z] => 7 + // [2012-01-01T00:00:00Z] => 4 + // [2013-01-01T00:00:00Z] => 4 + // [gap] => +1YEAR + // [end] => 2014-01-01T00:00:00Z + // ) + + // Isolate $end and $gap and clean the field to only have the actual + // date values. + $end = $field->end; + unset($field->end); + + if (isset($field->gap)) { + $gap = $field->gap; + unset($field->gap); + } + + // Treat each date facet as a range start, and use the next date + // facet as range end. Use 'end' for the final end. + $range_end = array(); + foreach ($field as $facet => $count) { + if (isset($prev_facet)) { + $range_end[$prev_facet] = $facet; + } + $prev_facet = $facet; + } + $range_end[$prev_facet] = $end; + + foreach ($field as $facet => $count) { + // Solr sends this back if it's empty. + if ($facet == '_empty_' || $count == 0) { + continue; + } + if ($facet_callback && function_exists($facet_callback)) { + $facet_text = $facet_callback($facet, array('gap' => $gap)); + } + else { + $facet_text = apachesolr_date_format_iso_by_gap($gap, $facet); + } + $new_query = clone $query; + $new_query->add_filter($facet_field, '['. $facet .' TO '. $range_end[$facet] .']'); + $options['query'] = $new_query->get_url_queryvalues(); + $items[] = theme('apachesolr_facet_link', $facet_text, $new_query->get_path(), $options, $count, FALSE, $response->response->numFound); + } + } + if (count($items) > 0) { + // Get information needed by the rest of the blocks about limits. + $initial_limits = variable_get('apachesolr_facet_query_initial_limits', array()); + $limit = isset($initial_limits[$module][$delta]) ? $initial_limits[$module][$delta] : variable_get('apachesolr_facet_query_initial_limit_default', 10); + $output = theme('apachesolr_facet_list', $items, $limit, $delta); + return array('subject' => $filter_by, 'content' => $output); + } + return NULL; +} + +/** + * Handles facet text and breadcrumb displays for dates or date ranges. + * @param $facet + * A date string, eg: 2008-01-01T00:00:00Z or a date range, eg: [2007-01-01T00:00:00Z TO 2008-01-01T00:00:00Z] + * @param $options + * An array with a key 'gap' of the form +1YEAR, or +1MONTH etc. + * + * TODO: Why is $options an array? + */ +function apachesolr_date_display_callback($facet, $options) { + if (preg_match('@[\[\{](\S+) TO (\S+)[\]\}]@', $facet, $match)) { + return apachesolr_date_format_range($match[1], $match[2]); + } + $gap = preg_replace('/^\+[0-9]+/', '', $options['gap']); + return apachesolr_date_format_iso_by_gap($gap, $facet); +}