Index: google_cse.theme.inc =================================================================== --- google_cse.theme.inc (.../vendor/drupal-contrib/modules/google_cse/current) (revision 1372) +++ google_cse.theme.inc (.../appistry.com/trunk/sites/all/modules/google_cse) (revision 1372) @@ -1,5 +1,5 @@ variable_get('google_cse_cx', ''), 'cof' => variable_get('google_cse_cof_google', 'FORID:0'), 'sitesearch' => google_cse_sitesearch_default(), - ) + google_cse_advanced_settings(); + ) + google_cse_advanced_settings(); $variables['results_searchbox_form'] = $variables['form'] ? drupal_get_form('google_cse_results_searchbox_form', $variables['self']) : ''; $variables['noscript'] = t('!google, or enable JavaScript to view them here.', array('!google' => l(t('View the results at Google'), 'http://'. variable_get('google_cse_domain', 'www.google.com') .'/cse', array('query' => $query)))); @@ -26,12 +27,109 @@ 'googleCSE' => array( 'resultsWidth' => intval(variable_get('google_cse_results_width', 600)), 'domain' => variable_get('google_cse_domain', 'www.google.com'), - ), - ); + ), + ); drupal_add_js($settings, 'setting'); } /** + * The search results page can be themed/customized. This is the + * function for theming the Business search results page. + * + * The input is a "results" array. + * + * Component 'items' is an array of search results, + * where each item is an array with the + * following components (not all are displayed in this default function): + * 'url' - URL of search result + * 'title' - Title of search result + * 'crawldate' - Date page was last indexed by Google + * 'excerpt' - Excerpt of page with search terms highlighted + * 'lang' - Google's guess as to the language of the search result + * 'crowd_host' - Indicates that lots more results were found on this + * host, and you could do another query on this host to find more + * results (value is a URL-encoded host name to be used in a query) + * + * Components 'from', 'to', and 'total' + * indicate "Viewing results [from] to [to] of approximately [total]'. + * + * + */ +function template_preprocess_google_cse_xml_results(&$variables) { + $results = $variables['form']; + + $variables['prefix'] = filter_xss_admin(variable_get('google_cse_results_prefix', '')); + $variables['suffix'] = filter_xss_admin(variable_get('google_cse_results_suffix', '')); + $variables['results_searchbox_form'] = $variables['form'] ? drupal_get_form('google_cse_results_searchbox_form', $variables['self']) : ''; + + $variables['header'] = check_plain(variable_get('google_cse_results_title', t('Search Results'))); + + $variables['has_results'] = !(empty($results) || empty($results['items'])); + $variables['no_results'] = t('Your search returned no results. Please try a different search.'); + + $variables['pager_status'] = t('Viewing results !from to !to of approximately !total', + array( + '!from' => 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))); + } + + $variables['original_query'] = preg_replace('/ more:[^&]*/', '', $_GET['query']); + $variables['has_refinements'] = count($results['labels']) == TRUE; + + if ($variables['has_refinements']) { + $variables['refinements_label'] = t('Refine results for'); + $variables['refinements'] = array(); + + preg_match('/ more:([^&]*)/', $_GET['query'], $selected_label); + $selected_label = $selected_label[1]; + + 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'] = $variables['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 (.../vendor/drupal-contrib/modules/google_cse/current) (revision 1372) +++ google_cse.admin.inc (.../appistry.com/trunk/sites/all/modules/google_cse) (revision 1372) @@ -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 (.../vendor/drupal-contrib/modules/google_cse/current) (revision 1372) +++ google_cse.module (.../appistry.com/trunk/sites/all/modules/google_cse) (revision 1372) @@ -1,5 +1,5 @@ array('search Google CSE'), 'title callback' => 'google_cse_results_tab', 'description' => 'Google Custom Search Engine', @@ -40,13 +41,18 @@ 'arguments' => array('form' => TRUE, 'self' => FALSE), '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', 'template' => 'google_cse_results_gadget', - ), - ); + ), + ); } /** @@ -57,7 +63,7 @@ return array( 0 => array('info' => t('Google CSE'), 'cache' => BLOCK_NO_CACHE), 1 => array('info' => t('Google CSE results'), 'cache' => BLOCK_NO_CACHE), - ); + ); } elseif ($op == 'view' && user_access('search Google CSE')) { switch ($delta) { @@ -74,11 +80,11 @@ */ function google_cse_forms() { $forms = array(); - $forms['google_cse_searchbox_form'] = array( + $forms['google_cse_searchbox_form'] = array( 'callback' => 'google_cse_searchbox_form_builder', 'callback arguments' => array('google_cse_searchbox_form'), ); - $forms['google_cse_results_searchbox_form'] = array( + $forms['google_cse_results_searchbox_form'] = array( 'callback' => 'google_cse_searchbox_form_builder', 'callback arguments' => array('google_cse_results_searchbox_form'), ); @@ -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['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'] : '', + '#default_value' => isset($_GET['query']) ? preg_replace('/ more:[^&]*/', '', $_GET['query']) : '', ); $form['sa'] = array( '#type' => 'submit', '#value' => t('Search'), ); - foreach (google_cse_advanced_settings() as $parameter => $setting) { - $form[$parameter] = array( + + // 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' => $setting, + '#value' => variable_get('google_cse_cx', ''), ); + + $form['cof'] = array( + '#type' => 'hidden', + '#value' => $cof, + ); + + 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,7 +163,12 @@ */ 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)); + } } /** @@ -220,7 +243,7 @@ '#type' => $type, '#options' => array( '' => ($var = variable_get('google_cse_sitesearch_option', '')) ? (($type == 'radios') ? check_plain($var) : $var) : t('Search the web'), - ) + $options, + ) + $options, '#default_value' => google_cse_sitesearch_default(), ); if ($type == 'select' && isset($form['sa'])) { @@ -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 (.../vendor/drupal-contrib/modules/google_cse/current) (revision 0) +++ google_cse_xml_results.tpl.php (.../appistry.com/trunk/sites/all/modules/google_cse) (revision 1372) @@ -0,0 +1,56 @@ + +
+

+ + +
+ + + + + +

+ +

+ + + +
+

:

+ + >  + +
+ + +

+ + | + + +

+ + +
+

+

+

+
+ + +

+ + | + + +

+ + + +
+ + +
+ Index: google_cse.css =================================================================== --- google_cse.css (.../vendor/drupal-contrib/modules/google_cse/current) (revision 1372) +++ google_cse.css (.../appistry.com/trunk/sites/all/modules/google_cse) (revision 1372) @@ -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