Index: google_cse.theme.inc =================================================================== --- google_cse.theme.inc (revision 1192) +++ google_cse.theme.inc (revision 1293) @@ -1,5 +1,5 @@ intval($results['from']), + '!to' => intval($results['to']), + '!total' => intval($results['total']) + ) + ); + + $perpage = variable_get('google_cse_results_num', 10); + $query = array('query' => $_GET['query']); + + if ($results['from'] > 1) { + $newstart = intval($results['from']) - $perpage - 1; + if ($newstart < 0) { + $newstart = 0; + } + $query['start'] = $newstart; + + $variables['pager_prev'] = l(t('Previous page'), + variable_get('google_cse_results_path', 'search/google'), + array('query' => drupal_query_string_encode($query))); + } + + if ($results['to'] < $results['total']) { + $query['start'] = intval($results['to']); + + $variables['pager_next'] = l(t('Next page'), + variable_get('google_cse_results_path', 'search/google'), + array('query' => drupal_query_string_encode($query))); + } + + preg_match('/ more:([^&]*)/', $_GET['query'], $selected_label); + $variables['original_query'] = preg_replace('/ more:[^&]*/', '', $_GET['query']); + $variables['has_refinements'] = count($results['labels']) == TRUE; + + $selected_label = $selected_label[1]; + + $variables['refinements_label'] = t('Refine results for'); + + $variables['refinements'] = array(); + foreach ($results['labels'] as $label => $title) { + $selected = ($label == $selected_label) ? ' selected' : ''; + $classes = "google-cse-refinements-label" . $selected; + $attributes = drupal_attributes(array('class' => $classes)); + $new_query['query'] = $original_query . " more:$label"; + $link = l(t($title), + variable_get('google_cse_results_path', 'search/google'), + array('query' => drupal_query_string_encode($new_query))); + $variables['refinements'][] = array('link' => $link, 'attributes' => $attributes); + } + + $variables['results'] = array(); + foreach ($results['items'] as $item) { + $link = l($item['title'], $item['url'], array('html' => TRUE)); + $filtered_excerpt = filter_xss_admin(preg_replace('/
/', '', $item['excerpt'])); + $url = l($item['url'], $item['url']); + $variables['results'][] = array('title' => $link, 'excerpt' => $filtered_excerpt, 'url' => $url); + } +} + +/** * Display an Add-to-Google button. */ function template_preprocess_google_cse_results_gadget(&$variables) { Index: google_cse.admin.inc =================================================================== --- google_cse.admin.inc (revision 1192) +++ google_cse.admin.inc (revision 1293) @@ -23,9 +23,10 @@ '#default_value' => variable_get('google_cse_results_display', 'here'), '#options' => array( 'here' => t('On this site (requires JavaScript)'), + 'xml' => t('On this site (without JavaScript or frames, requires current Google Business account)'), 'google' => t('On Google'), ), - '#description' => t('Search results can be displayed on this site, using JavaScript, or on Google, which does not require JavaScript.'), + '#description' => t('Search results can be displayed on this site, using JavaScript, or on Google, which does not require JavaScript. If you have a Business account on Google Custom Search, you can also display results on this site without using JavaScript.'), ); $form['google_cse_results_title'] = array( '#title' => t('Search results page title'), Index: google_cse.module =================================================================== --- google_cse.module (revision 1192) +++ google_cse.module (revision 1293) @@ -1,5 +1,5 @@ array('search Google CSE'), 'title callback' => 'google_cse_results_tab', 'description' => 'Google Custom Search Engine', @@ -41,6 +42,11 @@ 'file' => 'google_cse.theme.inc', 'template' => 'google_cse_results', ), + 'google_cse_xml_results' => array( + 'arguments' => array('form' => TRUE, 'self' => FALSE), + 'file' => 'google_cse.theme.inc', + 'template' => 'google_cse_xml_results', + ), 'google_cse_results_gadget' => array( 'arguments' => array(), 'file' => 'google_cse.theme.inc', @@ -91,37 +97,48 @@ function google_cse_searchbox_form_builder(&$form_state, $form_id, $self = FALSE) { $form = array(); // The default form. - if (variable_get('google_cse_results_display', 'here') == 'here') { - $form['#action'] = url($self ? $_GET['q'] : 'search/google'); + if (variable_get('google_cse_results_display', 'here') != 'google') { + $form['#action'] = url($self ? $_GET['q'] : variable_get('google_cse_results_path', 'search/google')); $cof = variable_get('google_cse_cof_here', 'FORID:11'); } else { $form['#action'] = 'http://'. variable_get('google_cse_domain', 'www.google.com') .'/cse'; $cof = variable_get('google_cse_cof_google', 'FORID:0'); } + $form['#method'] = 'get'; + + $form['query'] = array( + '#type' => 'textfield', + '#default_value' => isset($_GET['query']) ? preg_replace('/ more:[^&]*/', '', $_GET['query']) : '', + ); + $form['sa'] = array( + '#type' => 'submit', + '#value' => t('Search'), + ); + + // items needed for non-XML-display versions of search form, + // whether it is in the block or on the results page + + if (variable_get('google_cse_results_display', 'here') != 'xml') { $form['cx'] = array( '#type' => 'hidden', '#value' => variable_get('google_cse_cx', ''), ); + $form['cof'] = array( '#type' => 'hidden', '#value' => $cof, ); - $form['query'] = array( - '#type' => 'textfield', - '#default_value' => isset($_GET['query']) ? $_GET['query'] : '', - ); - $form['sa'] = array( - '#type' => 'submit', - '#value' => t('Search'), - ); + foreach (google_cse_advanced_settings() as $parameter => $setting) { $form[$parameter] = array( '#type' => 'hidden', '#value' => $setting, ); } + } + // And the small differences between both. switch ($form_id) { case 'google_cse_searchbox_form': @@ -130,12 +147,13 @@ break; case 'google_cse_results_searchbox_form': $form['query']['#size'] = intval(variable_get('google_cse_results_searchbox_width', 40)); - $form['query']['#title'] = t('Enter your keywords'); + $form['query']['#title'] = variable_get('google_cse_searchbox_header', t('Enter your keywords')); if (variable_get('google_cse_results_gadget', 1)) { $form['sa']['#suffix'] = theme('google_cse_results_gadget'); } - break; } + + // TODO: May need to remove google_cse_sitesearch_form($form); return $form; } @@ -145,8 +163,13 @@ */ function google_cse_results() { google_cse_results_set_title(); - return theme('google_cse_results'); + if (variable_get('google_cse_results_display', 'here') == 'here') { + print theme('page', theme('google_cse_results')); + } else { + $results = _google_cse_get_search_results(); + print theme('page', theme('google_cse_xml_results', $results)); } +} /** * Return the Google CSE tab title, either a setting or a translation. @@ -235,3 +258,214 @@ function google_cse_sitesearch_default() { return isset($_GET['sitesearch']) ? $_GET['sitesearch'] : (variable_get('google_cse_sitesearch_default', 0) ? array_shift(preg_split('/[\s]+/', variable_get('google_cse_sitesearch', ''))) : ''); } + +/* Queries Google to get XML results, and parses them into an array + * of search items -- see theme_google_cse_result_items() function below + * for documentation of this array. + */ +function _google_cse_get_search_results() { + + // formulate the query for Google + + $url = 'http://www.google.com/search'; + + $query = array( + 'q' => $_GET['refinement'] == '' ? $_GET['query'] : $_GET['query'] . ' more:' . $_GET['refinement'], + 'start' => $_GET['start'], + 'num' => variable_get('google_cse_results_num', 10), + 'client' => 'google-csbe', + 'output' => 'xml_no_dtd', + 'cx' => variable_get('google_cse_cx', ''), + ) + google_cse_advanced_settings(); + + // get the XML results + + $res = drupal_http_request($url . '?' . drupal_query_string_encode($query)); + + if ($res->code != '200') { + return array(); + } + + // parse results and return them + return _google_cse_parse_xml_results($res->data); + +} + + +/* + * Internal function: parses the XML results returned by Google into + * a results array (see function theme_google_cse_result_items for doc) + */ +function _google_cse_parse_xml_results($data) { + + global $google_cse_elem; + global $google_cse_item_index; + global $google_cse_results; + global $google_cse_in_facet_item; + + $google_cse_in_facet_item = FALSE; + + $xml_p = drupal_xml_parser_create($data); + xml_set_element_handler($xml_p, '_google_cse_xml_elem_start', + '_google_cse_xml_elem_end'); + xml_set_character_data_handler($xml_p, '_google_cse_xml_character_data'); + + $google_cse_elem = ''; + $google_cse_item_index = 0; + $google_cse_results = array('items' => array()); + + if (!xml_parse($xml_p, $data, 1)) { + return array(); + } + + return $google_cse_results; +} + +/* + * Internal function for handling XML elements when parsing Google results. + */ +function _google_cse_xml_elem_start($parser, $name, $attribs = array()) { + global $google_cse_elem; + global $google_cse_item_index; + global $google_cse_results; + global $google_cse_partial_data; + global $google_cse_in_facet_item; + global $google_cse_curr_facet_label; + + // we only care about some of the XML elements in the Google results + // See http://www.google.com/coop/docs/cse/resultsxml.html for doc + + switch ($name) { + case 'RES': + // attributes give the from/to indices of this results set + $google_cse_results['from'] = $attribs['SN']; + $google_cse_results['to'] = $attribs['EN']; + break; + + case 'M': + // gives approx total number of results of search + $google_cse_elem = 'total'; + break; + + case 'R': + // starts a new search result item + $google_cse_item_index = count($google_cse_results['items']); + $google_cse_results['items'][$google_cse_item_index] = array(); + break; + + case 'FACETITEM': + // starts a new facet item + $google_cse_in_facet_item = TRUE; + break; + + case 'LABEL': + $google_cse_elem = $google_cse_in_facet_item ? 'facetlabel' : 'label'; + break; + + case 'ANCHOR_TEXT': + $google_cse_elem = 'anchor_text'; + break; + + // The rest of these are components of search result items + + case 'U': + $google_cse_elem = 'url'; + break; + + case 'T': + $google_cse_elem = 'title'; + break; + + case 'CRAWLDATE': + $google_cse_elem = 'crawldate'; + break; + + case 'S': + $google_cse_elem = 'excerpt'; + break; + + case 'LANG': + $google_cse_elem = 'lang'; + break; + + case 'HN': + $google_cse_elem = 'crowd_host'; + break; + + // anything else: make sure we are not getting data now! + default: + $google_cse_elem = ''; + break; + + } + + // this flag makes sure the character data handler gets reset + $google_cse_partial_data = 0; +} + +/* + * Internal function for handling XML elements when parsing Google results + */ +function _google_cse_xml_elem_end($parser, $name) { + global $google_cse_elem; + global $google_cse_item_index; + global $google_cse_partial_data; + global $google_cse_in_facet_item; + + // close out the element and make sure character data handler gets reset + $google_cse_elem = ''; + $google_cse_partial_data = 0; + switch ($name) { + case 'FACETITEM': + $google_cse_in_facet_item = FALSE; + break; + } +} + + +/* + * Internal function for handling XML elements when parsing Google results + */ +function _google_cse_xml_character_data($parser, $data) { + global $google_cse_elem; + global $google_cse_item_index; + global $google_cse_results; + global $google_cse_partial_data; + global $google_cse_curr_facet_label; + + if ($google_cse_elem == 'total') { + // handle the "total" element + if (!$google_cse_partial_data) { + $google_cse_results['total'] = ''; + } + $google_cse_results['total'] .= $data; + + } else if ($google_cse_elem == 'facetlabel') { + if (!$google_cse_partial_data) { + $google_cse_curr_facet_label = ''; + } + $google_cse_curr_facet_label .= html_entity_decode($data); + + } else if ($google_cse_elem == 'anchor_text') { + if (!$google_cse_partial_data) { + $google_cse_results['labels'][$google_cse_curr_facet_label] = ''; + } + $google_cse_results['labels'][$google_cse_curr_facet_label] .= html_entity_decode($data); + + } else if (array_search($google_cse_elem, + array('url', 'title', 'crawldate', + 'excerpt', 'lang', 'crowd_host')) !== FALSE) { + + // handle the item sub-elements + if (!$google_cse_partial_data) { + $google_cse_results['items'][$google_cse_item_index][$google_cse_elem] = ''; + } + + $google_cse_results['items'][$google_cse_item_index][$google_cse_elem] .= html_entity_decode($data); + + } + + // sometimes this gets called multiple times within the same element, + // due to line breaks or whatever, so make sure we append data + $google_cse_partial_data = 1; +} \ No newline at end of file Index: google_cse_xml_results.tpl.php =================================================================== --- google_cse_xml_results.tpl.php (revision 0) +++ google_cse_xml_results.tpl.php (revision 1293) @@ -0,0 +1,56 @@ + +
+

+ + +
+ + + + + +

+ +

+ + + +
+

:

+ + >  + +
+ + +

+ + | + + +

+ + +
+

+

+

+
+ + +

+ + | + + +

+ + + +
+ + +
+ Index: google_cse.css =================================================================== --- google_cse.css (revision 1192) +++ google_cse.css (revision 1293) @@ -4,3 +4,15 @@ #google-cse-searchbox-form .form-radios div { display: block; } #google-cse-results-searchbox-form .form-radios div { display: block; } #google-cse-results-gadget a { margin-left: 2em; } +#google-cse-results-gadget img { margin-left: 2em; } +.google-cse-result-item { margin-bottom: 10px; } +.google-cse-result-item p { margin-bottom: 0; margin-top: 0; } +.google-cse-item-title { font-size: medium; } +.google-cse-item-title a { color: #0000CC; } +.google-cse-item-url { font-size: small; } +.google-cse-item-url a { color: #008000; font-size: small; } +.google-cse-refinements { font-size: medium; margin: 20px 0 30px 0;} +.google-cse-refinements p { margin-bottom: 5px; } +.google-cse-refinements-label { margin-right: 30px; } +.google-cse-no-results { margin: 20px 0 30px 0;} +.google-cse-nav-link-wrapper { float: right;} \ No newline at end of file