Index: solr/project_solr.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project/solr/project_solr.module,v
retrieving revision 1.64
diff -u -r1.64 project_solr.module
--- solr/project_solr.module	10 Jul 2010 05:12:43 -0000	1.64
+++ solr/project_solr.module	22 Jul 2010 01:31:54 -0000
@@ -136,8 +136,10 @@
         }
       }
     }
+    // Set Latest Activity to be the last file update time for the newest release on the project.
     $document->ds_project_latest_activity = apachesolr_date_iso($max_filetime);
     if (!empty($max_official_filetime)) {
+      // Set Latest Release to be the last file update time for an official release on the project.
       $document->ds_project_latest_release = apachesolr_date_iso($max_official_filetime);
     }
     
@@ -156,6 +158,29 @@
   }
 }
 
+/**
+ * Implementation of hook_apachesolr_modify_query().
+ */
+function project_solr_apachesolr_modify_query(&$query, &$params) {
+  $filter_set = array();
+  // Pull all the meta-type specific facets.
+  foreach ($query->get_filters() as $filter) {
+    // Grab any meta-type specific facet (im_vid_X facets).
+    if (preg_match('/^im_vid_(.*)$/', $filter['#name'], $matches)) {
+      $filter_set[$filter['#name']][] = $filter['#value'];
+      $query->remove_filter($filter['#name'], $filter['#value']);
+    }
+  }
+  // Add the meta-type specific facets back as an OR list.
+  foreach ($filter_set as $name => $filters) {
+    $tid_query = apachesolr_drupal_query();
+    foreach ($filters as $value) {  
+      $tid_query->add_filter($name, $value);
+    }
+    $query->add_subquery($tid_query, 'OR');
+  }
+}
+
 //----------------------------------------
 // Page callbacks
 //----------------------------------------
@@ -194,7 +219,7 @@
 
     $sort = isset($_GET['solrsort']) ? check_plain($_GET['solrsort']) : '';
 
-    // Validate sort parameter
+    // Validate sort parameter.
     if ((!isset($sort) || !preg_match('/^([a-z0-9_]+ (asc|desc)(,)?)+$/i', $sort)) && empty($text_query)) {
       $sort = variable_get('project_solr_default_sort', 'sort_title asc');
     }
@@ -206,7 +231,7 @@
       throw new Exception(t('Could not construct a Solr query.'));
     }
 
-    $query->add_field_aliases(array('im_project_release_api_tids' => 'drupal_core'));
+    $query->add_field_aliases(array('im_project_release_api_tids' => variable_get('project_solr_project_release_api_tids_alias', 'drupal_core')));
 
     $params = array(
       'fl' => 'id,nid,title,body,format,comment_count,type,created,changed,score,url,uid,name,sis_project_release_usage,ds_project_latest_release,ds_project_latest_activity',
@@ -228,7 +253,7 @@
     $solr = apachesolr_get_solr();
 
     // Cache the built query. Since all the built queries go through
-    // this process, all the hook_invocations will happen later
+    // this process, all the hook_invocations will happen later.
     apachesolr_current_query($query);
 
     // This hook allows modules to modify the query and params objects.
@@ -262,7 +287,7 @@
     apachesolr_static_response_cache($response);
     apachesolr_has_searched(TRUE);
 
-    // Set breadcrumb
+    // Set breadcrumb.
     $breadcrumb = menu_get_active_breadcrumb();
     drupal_set_breadcrumb($breadcrumb);
 
@@ -281,6 +306,7 @@
 
     $output .= '</div>'; // id="project-overview"
     $output .= theme('pager', NULL, $params['rows'], 0);
+    $output = drupal_get_form('project_solr_browse_projects_form', $parent_term) . $output;
   }
   catch (Exception $e) {
     watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR);
@@ -404,10 +430,9 @@
       return;
     }
     else if (module_exists('project_release') && $delta == 'project_solr_compability') {
-      $vid = _project_release_get_api_vid();
       $facet = 'im_project_release_api_tids';
       $terms = array();
-      $active_terms = array_reverse(project_release_compatibility_list(FALSE), TRUE);
+      $active_terms = project_release_compatibility_list();
 
       $active_term_counts = array();
       if (isset($response->facet_counts->facet_fields->$facet)) {
@@ -448,7 +473,7 @@
     }
     else if ($delta == 'project_solr_text') {
       return array(
-        'subject' => t('Search @project_type', array('@project_type' => drupal_strtolower($query->get_query_basic()))),
+        'subject' => t('Search @project_type', array('@project_type' => $query->get_query_basic())),
         'content' => drupal_get_form('project_sort_freetext', $query->get_path()),
       );
     }
@@ -456,6 +481,174 @@
 }
 
 /**
+ * Build project browsing navigation form.
+ */
+function project_solr_browse_projects_form(&$form_state, $parent_term) {
+  drupal_add_js(drupal_get_path('module', 'project_solr') .'/project_solr.js');
+
+  $response = apachesolr_static_response_cache();
+  $query = apachesolr_current_query();
+
+  // Get the terms at the current depth.
+  $current_tid = '';
+  foreach ($query->get_filters() as $field) {
+    if ($field['#name'] == 'tid') {
+      $current_tid = $field['#value'];
+      break;
+    }
+  }
+
+  // Create a drop-down for sub-terms of primary project terms.
+  $tree = taxonomy_get_tree(_project_get_vid(), $parent_term->tid, -1, 1);
+  foreach ($tree as $term) {
+    $terms[$term->tid] = $term->name;
+  }
+  if (!empty($terms)) {
+    $vocab = taxonomy_vocabulary_load(_project_get_vid());
+    asort($terms);
+    $terms = array('' => '') + $terms;
+    $form['tid'] = array(
+      '#type' => 'select',
+      '#options' => $terms,
+      '#title' => $vocab->name,
+      '#default_value' => $current_tid,
+    );
+  }
+
+  if (module_exists('project_release')) {
+    $terms = array('' => '');
+    $active_terms = project_release_compatibility_list();
+    foreach ($active_terms as $tid => $term_name) {
+      $active = $query->has_filter('im_project_release_api_tids', $tid);
+      if ($active) {
+        $current_tid = $tid;
+      }
+      $terms[$tid] = $term_name;
+    }
+    if (!empty($terms)) {
+      $form['api_version'] = array(
+        '#title' => t('Filter by compatibility'),
+        '#type' => 'select',
+        '#options' => $terms,
+        '#default_value' => $current_tid,
+      );
+    }
+  }
+
+  // Retrieve all the vocabularies related to project so that the facets can be built.
+  $vocabs = project_get_related_tids_map(TRUE);
+  foreach ($vocabs as $vid => $vocab) {
+    if ($parent_term->tid == variable_get('project_type_associated_tid_' . $vid, NULL)) {
+      $selected = array();
+      // Extract selected values from our filters.
+      foreach ($query->get_filters() as $filter) {
+        if ($filter['#name'] == 'im_vid_'. $vid) {
+          $selected[] = $filter['#value'];
+        }
+      }
+
+      // Build checkbox items for all terms in related vocabularies.
+      $tree = taxonomy_get_tree($vid, 0, -1, 1);
+      $terms = array();
+      foreach ($tree as $term) {
+        $terms[$term->tid] = $term->name;
+      }
+      asort($terms);
+
+      $form['im_vid_' . $vid] = array(
+        '#title' => t($vocab->name),
+        '#type' => 'checkboxes',
+        '#options' => $terms,
+        '#default_value' => $selected,
+      );
+    }
+  }
+
+  $form['text'] = array(
+    '#title' => t('Search @project_type', array('@project_type' => $parent_term->name)),
+    '#type' => 'textfield',
+    '#default_value' => isset($_GET['text']) ? $_GET['text'] : '',
+    '#size' => 20,
+  );
+
+  $sorts = array(
+    'sort_title asc' => t('Title'),
+    'created desc' => t('Creation date'),
+  );
+  if (module_exists('project_release')) {
+    $sorts['ds_project_latest_release desc'] = t('Last release');
+    $sorts['ds_project_latest_activity desc'] = t('Recent activity');
+  }
+  if (module_exists('project_usage')) {
+    $sorts['sis_project_release_usage desc'] = t('Usage statistics');
+  }
+  $sorts[''] = ('Relevancy');
+
+  // Determine default value based on current value of solrsort.
+  $solrsort = isset($_GET['solrsort']) ? $_GET['solrsort'] : '';
+  $solrsort = explode(' ', $solrsort);
+  $solrsort[0] = preg_replace('/_[0-9]+$/', '', $solrsort[0]);
+  $solrsort = implode(' ', $solrsort);
+  if (empty($solrsort) && empty($form['text']['#default_value'])) {
+    $solrsort = 'sort_title asc';
+  }
+
+  $form['solrsort'] = array(
+    '#title' => t('Sort by'),
+    '#type' => 'select',
+    '#default_value' => $solrsort,
+    '#options' => $sorts,
+  );
+
+  $form['path'] = array(
+    '#type' => 'value',
+    '#value' => 'project/' . drupal_strtolower($parent_term->name),
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+  );
+  return $form;
+}
+
+function project_solr_browse_projects_form_submit($form, &$form_state) {
+  $query = new ProjectSolrQuery(apachesolr_get_solr(), $form_state['values']['text'], '', '', $form_state['values']['path']);
+
+  if (!empty($form_state['values']['tid'])) {
+    $query->add_filter('tid', $form_state['values']['tid']);
+  }
+
+  if (!empty($form_state['values']['api_version'])) {
+    $query->add_filter(variable_get('project_solr_project_release_api_tids_alias', 'drupal_core'), $form_state['values']['api_version']);
+  }
+
+  // Loop over all project-related vocabularies and create filters
+  // for any values that have been posted.
+  $vocabs = project_get_related_tids_map(TRUE);
+  foreach ($vocabs as $vid => $vocab) {
+    $values = array_filter($form_state['values']['im_vid_' . $vid]);
+    if (!empty($values)) {
+      foreach($values as $value) {
+        $query->add_filter('im_vid_'. $vid, $value);
+      }
+    }
+  }
+
+  // Rewrite solrsort parameter with API version tid, if necessary.
+  $solrsort = explode(' ', $form_state['values']['solrsort']);
+  if (module_exists('project_release') && !empty($form_state['values']['api_version'])) {
+    if ($solrsort[0] == 'ds_project_latest_release' || $solrsort[0] == 'ds_project_latest_activity') {
+      $solrsort[0] .= '_'. $form_state['values']['api_version'];
+    }
+  }
+  $query_values = $query->get_url_queryvalues();
+  if (isset($form_state['values']['text'])) {
+    $query_values['text'] = $query->get_query_basic();
+  }
+  $query->set_solrsort($solrsort[0], $solrsort[1]);
+  $form_state['redirect'] = array($query->get_path(), $query_values);
+}
+/**
  * Append the API tid to selected fields that might be in the string.
  */
 function project_solr_append_api_term($values, $tid) {
@@ -510,7 +703,7 @@
 //----------------------------------------
 
 /**
- * Perform the business logic to
+ * Perform the business logic to render search results for project-related searches.
  */
 function project_solr_render_search_result($result) {
   $project = node_load($result->nid);
@@ -543,5 +736,4 @@
   }
   $options['attributes']['class'] = implode(' ', $options['attributes']['class']);
   return apachesolr_l($facet_text,  $path, $options);
-}
-
+}
\ No newline at end of file
Index: project.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project/project.module,v
retrieving revision 1.359
diff -u -r1.359 project.module
--- project.module	8 Jul 2010 23:16:17 -0000	1.359
+++ project.module	22 Jul 2010 01:31:54 -0000
@@ -330,28 +330,53 @@
     variable_set('project_enable_alias', FALSE);
   }
 
+  $vocabs = project_get_related_tids_map();
   $project_vid = _project_get_vid();
-  $vocabs = taxonomy_get_vocabularies('project_project');
   $tree = taxonomy_get_tree($project_vid, 0, -1, 1);
   $top_level = array(t('- None -'));
   foreach ($tree as $term) {
     $top_level[$term->tid] = $term->name;
   }
   foreach ($vocabs as $vid => $vocab) {
+    $form['project_type_associated_tid_' . $vid] = array(
+      '#title' => t('Associated Project type taxonomy term for @vocab', array('@vocab' => $vocab->name)),
+      '#type' => 'select',
+      '#options' => $top_level,
+      '#default_value' => variable_get('project_type_associated_tid_' . $vid, NULL),
+      '#description' => t('If this vocabulary is specific to a project type, set the term here to allow Project to automatically show and hide the form as necessary.'),
+    );
+  }
+  if (module_exists('project_solr') && module_exists('project_release')) {
+    $form['project_solr_project_release_api_tids_alias'] = array(
+      '#title' => t('Alias for the Project Release API tid for use with Solr'),
+      '#type' => 'textfield',
+      '#default_value' => variable_get('project_solr_project_release_api_tids_alias', 'drupal_core'),
+    );
+  }
+  return system_settings_form($form);
+}
+
+/**
+ * Helper function for getting vocabularies related to top-level project terms.
+ *
+ * @param $active 
+ *   Boolean. TRUE to return only vocabularies actively associated with a term.
+ *   Defaults to FALSE.
+ * @return
+ *   An array of vocabulary objects, keyed on vocabulary ID.
+ */
+function project_get_related_tids_map($active = FALSE) {
+  $related_vocabs = array();
+  $project_vid = _project_get_vid();
+  $vocabs = taxonomy_get_vocabularies('project_project');
+  foreach ($vocabs as $vid => $vocab) {
     // Skip freetagging vocabularies, and the hard-coded project type
     // vocabulary, since things will go crazy if you set this for itself.
-    if (!$vocab->tags && $vid != $project_vid) {
-      $form['project_type_associated_tid_' . $vid] = array(
-        '#title' => t('Associated Project type taxonomy term for @vocab', array('@vocab' => $vocab->name)),
-        '#type' => 'select',
-        '#options' => $top_level,
-        '#default_value' => variable_get('project_type_associated_tid_' . $vid, NULL),
-        '#description' => t('If this vocabulary is specific to a project type, set the term here to allow Project to automatically show and hide the form as necessary.'),
-      );
+    if (!$vocab->tags && $vid != $project_vid && (!$active || variable_get('project_type_associated_tid_' . $vid, NULL))) {
+      $related_vocabs[$vid] = $vocab;
     }
   }
-
-  return system_settings_form($form);
+  return $related_vocabs;
 }
 
 /**
Index: solr/project_solr.js
===================================================================
RCS file: solr/project_solr.js
diff -N solr/project_solr.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ solr/project_solr.js	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,30 @@
+// $Id$
+
+Drupal.behaviors.projectSolrBrowseForm = function() {
+  // Disable relevancy option if no text is entered.
+  if (!$('#edit-text').val()) {
+    $('#edit-solrsort').children('[value=]').attr({'disabled':'disabled'});
+  }
+  // If length is zero as user starts typing text, select relevancy option.
+  $('#edit-text').keydown(function() {
+    if (($(this).val()).length == 0) {
+      $('#edit-solrsort').val("");
+    }
+  });
+  // If length is zero as user finishes typing text, deselect relevancy option.
+  $('#edit-text').keyup(function() {
+    if (($(this).val()).length == 0 && $('#edit-solrsort').val() == "") {
+      $('#edit-solrsort').val("sort_title asc");
+    }
+  }); 
+  $('#edit-text').blur(function() {
+    // Enable relevancy option if text is entered.
+    if ($(this).val()) {
+      $('#edit-solrsort').children('[value=]').removeAttr('disabled');
+    }
+    // Disable relevancy option if no text is entered.
+    else {
+      $('#edit-solrsort').children('[value=]').attr({'disabled':'disabled'});
+    }
+  });  
+};
